Skip to content

Compare and export

GET/v1/analytics/compare

Compare codes

GET /v1/analytics/compare takes codeIds (up to 4, comma-separated) and the usual range, tz and groupBy. The answer has one series per code over the same buckets: its points, its totals, the previous period of the same length, the change against it and the code's share of the compared scans.

Codes the key can't see come back in omitted instead of failing the request. A static code is listed with tracked: false and zero points, because its scans aren't counted.

requestGET /v1/analytics/compare
curl "https://api.dotwick.com/v1/analytics/compare?codeIds=qr_8841ce2mxk%2Cqr_2k8m4q7x1c9v&range=7d&tz=Europe%2FBerlin" \  -H "Authorization: Bearer sk_live_…"
response200 OKshortened to three days; there is one point per day of the range
{  "groupBy": "day",  "tz": "Europe/Berlin",  "from": "2026-09-09T00:00:00+02:00",  "to": "2026-09-12T00:00:00+02:00",  "previous": {    "from": "2026-09-06T00:00:00+02:00",    "to": "2026-09-09T00:00:00+02:00"  },  "buckets": [    "2026-09-09T00:00:00+02:00",    "2026-09-10T00:00:00+02:00",    "2026-09-11T00:00:00+02:00"  ],  "series": [    {      "codeId": "qr_8841ce2mxk",      "name": "Van fleet – unit 14",      "tracked": true,      "points": [        {          "ts": "2026-09-09T00:00:00+02:00",          "scans": 40,          "uniques": 31        },        {          "ts": "2026-09-10T00:00:00+02:00",          "scans": 52,          "uniques": 44        },        {          "ts": "2026-09-11T00:00:00+02:00",          "scans": 58,          "uniques": 47        }      ],      "totals": {        "scans": 150,        "uniques": 122,        "bots": 3      },      "previous": {        "scans": 100,        "uniques": 80      },      "deltas": {        "scans": 0.5,        "uniques": 0.525      },      "share": 0.75    },    {      "codeId": "qr_2k8m4q7x1c9v",      "name": "Van fleet – unit 15",      "tracked": true,      "points": [        {          "ts": "2026-09-09T00:00:00+02:00",          "scans": 12,          "uniques": 10        },        {          "ts": "2026-09-10T00:00:00+02:00",          "scans": 20,          "uniques": 17        },        {          "ts": "2026-09-11T00:00:00+02:00",          "scans": 18,          "uniques": 15        }      ],      "totals": {        "scans": 50,        "uniques": 42,        "bots": 0      },      "previous": {        "scans": 60,        "uniques": 51      },      "deltas": {        "scans": -0.167,        "uniques": -0.176      },      "share": 0.25    }  ],  "omitted": []}

Download scans as CSV

GET /v1/analytics/export answers with the CSV itself (text/csv, as an attachment) for the filters of the other analytics endpoints: codeId or codeIds, folderId, tagId, gtin, country and a range. kind picks the file: daily_totals, scan_rows, breakdowns.

daily_totals is one row per period (add splitByCode=true for one row per code), scan_rows is one row per scan, and breakdowns lists every row of the dimensions you name. All three need a plan with CSV export; scan_rows also needs per-scan analytics. Rows older than the plan's history aren't in the file.

Above 10,000 rows the answer is 413 payload_too_large with details.reason use_export_job. Start an export job instead.

Large ranges: an export job

POST /v1/analytics/exports takes the same filters as a JSON body (kind is required) and prepares the file in the background, up to 1,000,000 rows. More than that is 413 with details.reason too_many_rows: narrow the range. Send an Idempotency-Key.

The answer is 202 with an exportId. Poll GET /v1/exports/{id} until it is ready and read the download link from it. An email announces the file too; send email: false to skip it.

requestPOST /v1/analytics/exports
curl -X POST https://api.dotwick.com/v1/analytics/exports \  -H "Authorization: Bearer sk_live_…" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: van-014-scans-q3" \  -d '{    "kind": "scan_rows",    "range": "90d",    "tz": "Europe/Berlin",    "codeIds": [      "qr_8841ce2mxk"    ],    "email": false  }'
response202 Accepted
{  "exportId": "exp_2m7x4k9q1c8v3b6n",  "status": "queued"}