Revenue and units per product
Top products by revenue, units or orders, grouped by product id, SKU, variant, name, category or brand. Reads one row per line item, expanded from the ecommerce spec's items array at ingest — which is why this is a separate endpoint rather than an explore query: items is an array, and the typed events table the explore engine reads holds only scalars.
revenue is sum(unit price x quantity) across line items. It excludes shipping and tax, so it will NOT equal GET /commerce/summary revenue. Both are correct for their own question; revenue_basis says which you got.
Set event to add_to_cart or view_item to get the same rollup at an earlier funnel stage — which products get looked at versus bought.
Labels (item_name, item_brand, item_category) are reported as the most recent value seen rather than grouped on, so a product renamed mid-window stays one row instead of splitting its revenue in two.
Rows come back ordered by order_by descending and truncated at limit; truncated: true means the catalogue's tail was dropped, never its top sellers. Currencies are never summed together.
curl -X GET "https://mythic-analytics.gulp.workers.dev/client/v1/data/commerce/items?event=purchase&group_by=item_id&order_by=revenue&date_from=example_string&date_to=example_string¤cy=example_string&limit=100" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN"
import requests
import json
url = "https://mythic-analytics.gulp.workers.dev/client/v1/data/commerce/items?event=purchase&group_by=item_id&order_by=revenue&date_from=example_string&date_to=example_string¤cy=example_string&limit=100"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
response = requests.get(url, headers=headers)
print(response.json())
const response = await fetch("https://mythic-analytics.gulp.workers.dev/client/v1/data/commerce/items?event=purchase&group_by=item_id&order_by=revenue&date_from=example_string&date_to=example_string¤cy=example_string&limit=100", {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_TOKEN"
}
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
)
func main() {
req, err := http.NewRequest("GET", "https://mythic-analytics.gulp.workers.dev/client/v1/data/commerce/items?event=purchase&group_by=item_id&order_by=revenue&date_from=example_string&date_to=example_string¤cy=example_string&limit=100", nil)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
}
require 'net/http'
require 'json'
uri = URI('https://mythic-analytics.gulp.workers.dev/client/v1/data/commerce/items?event=purchase&group_by=item_id&order_by=revenue&date_from=example_string&date_to=example_string¤cy=example_string&limit=100')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
{
"success": true,
"data": {
"event": "purchase",
"group_by": "item_id",
"order_by": "revenue",
"revenue_basis": "line_items",
"truncated": false,
"items": [
{
"item_key": "7734115",
"currency": "USD",
"item_name": "The Collection Snowboard",
"item_brand": "Hydrogen",
"item_category": "Snowboards",
"sku": "SNOW-01",
"revenue": 2700,
"units": 90,
"orders": 61,
"buyers": 58,
"average_unit_price": 30,
"discount_total": 145
}
],
"note_on_revenue": "example_string"
}
}
{
"error": "Bad Request",
"message": "The request contains invalid parameters or malformed data",
"code": 400,
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
{
"error": "Unauthorized",
"message": "Authentication required. Please provide a valid API token",
"code": 401
}
{
"error": "Service Unavailable",
"message": "The service is temporarily unavailable. Please try again later",
"code": 503
}
/commerce/items
Target server for requests. Edit to use your own host.
Agency key (ak_) or location secret key (sk_) as a bearer token. Format: Bearer ak_... or Bearer sk_.... Scoped keys (mcp_) are accepted too and need the read scope of the family the route belongs to (people, events, sessions, replays, exceptions, heatmaps or bigquery_export). See Using an mcp_ key over HTTP.
ak_) or location secret key (sk_) as a bearer token. Format: Bearer ak_... or Bearer sk_.... Scoped keys (mcp_) are accepted too and need the read scope of the family the route belongs to (people, events, sessions, replays, exceptions, heatmaps or bigquery_export). See Using an mcp_ key over HTTP.
Commerce event name. Default purchase.
Product dimension to group on.
ISO date (YYYY-MM-DD) or timestamp. Default 30 days ago.
ISO date or timestamp, inclusive.
ISO 4217 code.
Max rows, 1-1000. Default 100.
Request Preview
Response
Response will appear here after sending the request
Authentication
Bearer token. Agency key (ak_) or location secret key (sk_) as a bearer token. Format: Bearer ak_... or Bearer sk_.... Scoped keys (mcp_) are accepted too and need the read scope of the family the route belongs to (people, events, sessions, replays, exceptions, heatmaps or bigquery_export). See Using an mcp_ key over HTTP.
Query Parameters
Commerce event name. Default purchase.
Product dimension to group on.
item_idskuitem_variant_iditem_nameitem_categoryitem_brandrevenueunitsordersISO date (YYYY-MM-DD) or timestamp. Default 30 days ago.
ISO date or timestamp, inclusive.
ISO 4217 code.
Max rows, 1-1000. Default 100.