Skip to main content

Analytics

GET /v1/analytics returns the figures the dashboard home page shows for a period: ten KPIs, each with its value for a comparison period and the change, and eight chart series. Use it for client reports or a BI export instead of rebuilding the numbers from GET /v1/orders. The API and the dashboard share the same definitions and the same cache, so for the same period they show the same numbers. What each figure means for the merchant is explained on the Analytics page of the merchant docs.

The API always counts all traffic: store pages and landing pages together. The dashboard's traffic filter (landing pages only, or the store only) has no equivalent here.

Before you start​

  • The key needs analytics:read. Keys created from the dashboard (Settings → API, /dashboard/api) carry it since v1.7. Scopes are frozen when a key is created, so an older key answers 403 forbidden with "Missing scope: analytics:read": create a new key from the dashboard. A key created through POST /v1/keys only gets the scopes held by the key that created it.
  • Personal keys need a store on an active Enterprise plan. An installed app's token is not bound to Enterprise, but it needs analytics:read among the scopes the merchant approved. See Introduction.
  • Revenue, profit and average order value, and the revenue values inside the charts, are sent only when the key belongs to the store owner's account. Otherwise those fields are missing from the answer: they are not set to 0 or null.
ScopeDescription
analytics:readRead store analytics and KPIs. Included in merchant keys created from v1.7 on; an older key needs a new key.

GET /v1/analytics​

The report for one period.

Auth: platform key with analytics:read.

Query parameters​

ParamTypeDefaultNotes
rangestringthis_monthtoday, yesterday, 7d, 30d, this_month, last_month, this_year or custom. Any other value returns 400 bad_request.
fromYYYY-MM-DDnoneFirst day. Required with range=custom, ignored otherwise.
toYYYY-MM-DDnoneLast day. Required with range=custom, ignored otherwise. Never before from, at most 365 days after it.

Periods​

rangePeriodCompared withgroup_by
todayTodayYesterdayhour
yesterdayYesterdayThe day beforehour
7dToday and the 6 days beforeThe 7 days before thoseday
30dToday and the 29 days beforeThe 30 days before thoseday
this_monthThe 1st to the last day of the current monthThe whole previous monthday
last_monthThe whole previous monthThe month before itday
this_year1 January to 31 December of the current yearThe whole previous yearmonth
customfrom to to, both days includedThe same number of days just before fromhour, day or month

For custom, group_by is hour when to is from or the next day, day when to is at most 90 days after from, and month beyond that. this_month and this_year run to the end of the month or the year, so they compare the days so far with a whole previous month or year.

The report is cached for 120 seconds per store and period, and the dashboard reads the same cache. A call can return figures up to two minutes old, and calls inside that window get the same numbers.

Request​

curl 'https://api.dzbuild.app/v1/analytics?range=7d' \
-H "Authorization: Bearer $DZ_KEY"

A custom period:

curl 'https://api.dzbuild.app/v1/analytics?range=custom&from=2026-09-01&to=2026-09-30' \
-H "Authorization: Bearer $DZ_KEY"

Response 200​

The answer for range=7d on 6 October 2026, to a key created by the store owner. The time series and the lists are cut to their first entries.

{
"data": {
"period": {
"from": "2026-09-30 00:00:00",
"to": "2026-10-06 23:59:59",
"prev_from": "2026-09-23 00:00:00",
"prev_to": "2026-09-29 23:59:59",
"label": "7d",
"group_by": "day"
},
"kpis": {
"total_orders": { "value": 48, "change": 20, "previous": 40 },
"delivered_orders": { "value": 31, "change": 7, "previous": 29 },
"cancelled_orders": { "value": 6, "change": -25, "previous": 8 },
"total_revenue": { "value": 186000, "change": 8, "previous": 172000 },
"total_profit": { "value": 61500, "change": 6, "previous": 58000 },
"avg_order_value": { "value": 4350, "change": -1, "previous": 4400 },
"total_visitors": { "value": 1520, "change": 17, "previous": 1300 },
"page_views": { "value": 4810, "change": 17, "previous": 4100 },
"conversion_rate": { "value": 3.2, "change": 3, "previous": 3.1 },
"new_customers": { "value": 41, "change": 17, "previous": 35 }
},
"charts": {
"revenue_over_time": [
{ "label": "09/30", "orders": 7, "revenue": 24500 },
{ "label": "10/01", "orders": 5, "revenue": 18000 }
],
"orders_by_hour": [
{ "hour": "00:00", "orders": 0, "impressions": 35 },
{ "hour": "01:00", "orders": 1, "impressions": 22 }
],
"orders_by_status": {
"pending": 5,
"confirmed": 4,
"processing": 2,
"shipped": 9,
"delivered": 20,
"cancelled": 6,
"returned": 2
},
"top_products": [
{
"id": 12,
"name": "Classic watch",
"image": "https://cdn.dzbuild.app/uploads/products/123/123_1700000000_example.webp",
"qty_sold": 14,
"revenue": 49000,
"views": 380
}
],
"visitors_over_time": [
{ "label": "09/30", "page_views": 690, "unique_visitors": 215, "orders": 7 },
{ "label": "10/01", "page_views": 702, "unique_visitors": 230, "orders": 5 }
],
"devices": {
"desktop": { "count": 510, "percent": 11 },
"mobile": { "count": 4180, "percent": 87 },
"tablet": { "count": 120, "percent": 2 }
},
"traffic_sources": [
{ "source": "facebook", "count": 2900, "percent": 60 },
{ "source": "direct", "count": 1210, "percent": 25 },
{ "source": "tiktok", "count": 700, "percent": 15 }
],
"top_wilayas": [
{ "wilaya": "الجزائر", "orders": 9, "revenue": 41000 },
{ "wilaya": "وهران", "orders": 6, "revenue": 27500 }
]
}
}
}

The period​

FieldMeaning
from, toStart and end of the period, YYYY-MM-DD HH:MM:SS, Algiers time.
prev_from, prev_toStart and end of the comparison period.
labelThe range you asked for.
group_byBucket size of revenue_over_time and visitors_over_time: hour, day or month.

KPIs​

Each KPI has three numbers: value for the period, previous for the comparison period, and change, the percent change rounded to a whole number. When previous is 0 or below, change is 100 if value is above 0, and 0 otherwise. Amounts are rounded to whole numbers.

KPIWhat it counts
total_ordersOrders created in the period, whatever their status.
delivered_ordersOrders delivered in the period, counted on the day they were delivered.
cancelled_ordersOrders created in the period that are now cancelled.
total_revenueOwner only. Subtotal minus discount of the orders delivered in the period. Shipping and payment fees are not included.
total_profitOwner only. total_revenue minus quantity × cost price of the items in those orders. A product with no cost price counts as zero cost.
avg_order_valueOwner only. Average order total of the orders created in the period, cancelled and returned orders left out.
total_visitorsDistinct visitors in the period, store pages and landing pages together.
page_viewsPage views in the period, store pages and landing pages together.
conversion_ratetotal_orders ÷ total_visitors × 100, with one decimal. 0 when there were no visitors.
new_customersCustomer records created in the period.

These KPIs come from different sets of orders, so they do not add up: total_revenue ÷ total_orders is not avg_order_value.

Charts​

ChartContent
revenue_over_timeOne entry per bucket: label, the orders created in the bucket and, for the owner only, revenue, the order totals of those orders that are now delivered. It is not total_revenue, which counts by delivery day and leaves shipping out.
orders_by_hourAlways 24 entries, 00:00 to 23:00: the orders created and the page views (impressions) at that hour of the day, over the whole period.
orders_by_statusOrders created in the period by current status: pending, confirmed, processing, shipped, delivered, cancelled and returned.
top_productsUp to 10 products by quantity sold in the orders created in the period, cancelled and returned orders left out. Each has id, name, image (main image URL or null), qty_sold, views (distinct visitors on the product in the period) and, for the owner only, revenue (quantity × the price on the order).
visitors_over_timeOne entry per bucket: label, page_views, unique_visitors and orders. A visitor who returns on another day counts in both buckets, so the unique_visitors values can add up to more than total_visitors.
devicesPage views by device: desktop, mobile and tablet keys, each with count and percent. A device with no views has no key, and the field is an empty array [] when the period had no visits.
traffic_sourcesUp to 6 sources by page views, busiest first, each with source, count and percent. percent is the share among the listed sources.
top_wilayasUp to 10 wilayas by number of orders created in the period, all statuses: wilaya (the Arabic name, غير محدد for orders with no wilaya), orders and, for the owner only, revenue, the order totals of those orders whatever their status.

Labels follow group_by: 00:00 to 23:00 for hour (24 entries; a two-day custom period adds both days into the same hours), MM/DD for day, and the English month and year for month, such as Sep 2026. Days after today and months after the current one are left out.

The platform records source as direct, facebook, instagram, tiktok, google, youtube, twitter, snapchat, telegram or other. A visit whose link carries utm_source is classed by that tag (fb counts as facebook, ig as instagram, x as twitter, any tag not in the list as other); a visit without it is classed by the site it came from.

Errors​

HTTPCodeCause
400bad_requestrange is not one of the eight values ("Invalid range. Allowed: ..."), or a custom period whose from or to is missing or not YYYY-MM-DD, or whose to is before from or more than 365 days after it ("Invalid date range ...").
401unauthorizedBad or missing key.
402quota_exceededThe store's monthly request quota is used up. See Rate limits.
403forbidden"Missing scope: analytics:read", or a personal key whose store is not on an active Enterprise plan ("API access requires an active Enterprise plan").
429rate_limitedThe store's per-minute cap, shared by all of its keys, or the per-install cap of an installed app's token. Wait for Retry-After. See Rate limits.

Known limits​

  • Month ends. When today's day number does not exist in the previous month, as on 31 October, range=last_month returns the current month and range=this_month is compared with the current month itself. last_month is also compared with itself when the day does not exist two months back, as on 30 April. On those days, ask for the dates you need with range=custom.
This page for AI toolsView as MarkdownOpen in ChatGPTOpen in Claude