Skip to main content

Analytics

Read a store's dashboard figures for a period with GET /v1/analytics: orders, revenue, profit, visitors and conversion, their change, and the chart series.

Written by Support

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.

Scope

Description

analytics:read

Read 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

Param

Type

Default

Notes

range

string

this_month

today, yesterday, 7d, 30d, this_month, last_month, this_year or custom. Any other value returns 400 bad_request.

from

YYYY-MM-DD

none

First day. Required with range=custom, ignored otherwise.

to

YYYY-MM-DD

none

Last day. Required with range=custom, ignored otherwise. Never before from, at most 365 days after it.

Periods

range

Period

Compared with

group_by

today

Today

Yesterday

hour

yesterday

Yesterday

The day before

hour

7d

Today and the 6 days before

The 7 days before those

day

30d

Today and the 29 days before

The 30 days before those

day

this_month

The 1st to the last day of the current month

The whole previous month

day

last_month

The whole previous month

The month before it

day

this_year

1 January to 31 December of the current year

The whole previous year

month

custom

from to to, both days included

The same number of days just before from

hour, 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

Field

Meaning

from, to

Start and end of the period, YYYY-MM-DD HH:MM:SS, Algiers time.

prev_from, prev_to

Start and end of the comparison period.

label

The range you asked for.

group_by

Bucket 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.

KPI

What it counts

total_orders

Orders created in the period, whatever their status.

delivered_orders

Orders delivered in the period, counted on the day they were delivered.

cancelled_orders

Orders created in the period that are now cancelled.

total_revenue

Owner only. Subtotal minus discount of the orders delivered in the period. Shipping and payment fees are not included.

total_profit

Owner 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_value

Owner only. Average order total of the orders created in the period, cancelled and returned orders left out.

total_visitors

Distinct visitors in the period, store pages and landing pages together.

page_views

Page views in the period, store pages and landing pages together.

conversion_rate

total_orders ÷ total_visitors × 100, with one decimal. 0 when there were no visitors.

new_customers

Customer 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

Chart

Content

revenue_over_time

One 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_hour

Always 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_status

Orders created in the period by current status: pending, confirmed, processing, shipped, delivered, cancelled and returned.

top_products

Up 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_time

One 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.

devices

Page 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_sources

Up to 6 sources by page views, busiest first, each with source, count and percent. percent is the share among the listed sources.

top_wilayas

Up 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

HTTP

Code

Cause

400

bad_request

range 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 ...").

401

unauthorized

Bad or missing key.

402

quota_exceeded

The store's monthly request quota is used up. See Rate limits.

403

forbidden

"Missing scope: analytics:read", or a personal key whose store is not on an active Enterprise plan ("API access requires an active Enterprise plan").

429

rate_limited

The 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.

Did this answer your question?