API
API reference
The public API covers event ingestion, revenue ingestion and reading the reports of one website. Every request is authenticated with an API key.
Base URL and authentication
The base URL is https://abackanalytics.com and every endpoint below starts with /api/v1. Send the API key as a bearer token:
Authorization: Bearer abk_xxxxxxxx.secretThe key identifies one website. A website id in the body is checked against the key, and a mismatch is refused. The key is created on the Developers page of the website, the secret is shown exactly once, and it can be revoked at any time.
OpenAPI specification
This reference is also published as an OpenAPI 3.1 document, served as JSON at /api/openapi. It describes every endpoint, with the request bodies, the response schemas, the error codes and the limits. The OpenAPI page lists them in a table.
curl https://abackanalytics.com/api/openapiResponse envelope
A successful call answers with an ok flag and a data object. A failed call answers with an ok flag of false and an error object with a stable code and a message that is safe to show to a user.
{ "ok": true, "data": { ... } }{
"ok": false,
"error": {
"code": "validation_failed",
"message": "Event name is required."
},
"requestId": "req_7c1f2a9b4d6e8f0a2b3c"
}Every response carries a request id header, and the request id is repeated in the body of a failure. Quote it when you report a problem.
Scopes
A key carries the scopes it was created with. A request that needs a scope the key does not have is refused with a forbidden error, so a key that can only send events cannot read your reports.
events:write send events from your server
revenue:write report confirmed payments
reports:read read the reports of the website
export:read download the events of the website as a CSVRate limits and payload limits
- Public ingestion is rate limited per website and per client address at the edge. A request over the limit is refused with the code
rate_limitedand aRetry-Afterheader. - The server-to-server endpoints accept a body of at most 32 KB. A larger body is refused with
payload_too_largebefore it is parsed. - Accepted events are metered against the monthly limit of the plan. The current usage is visible in the dashboard.
POST /api/v1/events
Sends one event from your server. Scope: events:write. Answers 202 when the event was accepted and 200 when it was a duplicate.
curl -X POST https://abackanalytics.com/api/v1/events \
-H "Authorization: Bearer abk_xxxxxxxx.secret" \
-H "content-type: application/json" \
-d '{
"websiteId": "abk_xxxxxxxxxxxxxxxx",
"event": "signup_completed",
"properties": { "plan": "pro" }
}'{
"ok": true,
"data": {
"eventId": "evt_9f2c41b7ac0d4e6f8a1b",
"status": "accepted"
}
}Body fields:
websiteId required the public tracking id of the website
event required the event name, 1 to 64 characters
properties optional at most 20 properties, see the events page
timestamp optional epoch milliseconds, clamped by the server
visitorId optional the identifier from the visitor browser
sessionId optional links the event to a known session
path optional the page path the event happened onPOST /api/v1/revenue
Records one confirmed payment. Scope: revenue:write. Answers 201 when the payment was recorded and 200 when the idempotency key was already used.
curl -X POST https://abackanalytics.com/api/v1/revenue \
-H "Authorization: Bearer abk_xxxxxxxx.secret" \
-H "content-type: application/json" \
-d '{
"websiteId": "abk_xxxxxxxxxxxxxxxx",
"idempotencyKey": "order_2026_10_00041",
"amount": 1999,
"currency": "USD",
"customerRef": "cust_8f21"
}'{
"ok": true,
"data": {
"revenueEventId": "rev_3c9d5a1e7b2f4a6c8d0e",
"status": "recorded",
"attributed": true
}
}Body fields:
websiteId required the public tracking id of the website
idempotencyKey required 8 to 128 characters, unique per payment
amount required integer minor units, for example 1999
currency required three letter ISO code
provider optional defaults to "manual"
type optional payment, refund, subscription or chargeback
customerRef optional an opaque reference, never an email address
productId optional the product or plan identifier
visitorId optional the identifier from the visitor browser
sessionId optional links the payment to a known session
attributionId optional an opaque attribution reference
timestamp optional epoch milliseconds
properties optional at most 20 propertiesGET /api/v1/reports/overview
Returns the totals, the daily series, the goal statistics and the revenue summary of the website for a date range. Scope: reports:read.
curl "https://abackanalytics.com/api/v1/reports/overview?range=last_30_days" \
-H "Authorization: Bearer abk_xxxxxxxx.secret"{
"ok": true,
"data": {
"website": { "id": "site_...", "name": "Marketing site", "timezone": "UTC" },
"range": { "start": "2026-09-23", "end": "2026-10-23", "days": 30, "timezone": "UTC" },
"totals": {
"visitors": 4210,
"pageviews": 11894,
"sessions": 5031,
"events": 812,
"newVisitors": 3104,
"returningVisitors": 1106,
"bounceRate": 0.41,
"averageSessionSeconds": 96,
"viewsPerVisitor": 2.82
},
"series": [
{ "date": "2026-09-23", "visitors": 121, "pageviews": 344, "sessions": 140 }
],
"goals": [
{
"id": "goal_...",
"name": "Signup",
"eventName": "signup_completed",
"kind": "event",
"conversions": 96,
"visitors": 91,
"conversionRate": 0.0216
}
],
"revenue": [
{
"currency": "USD",
"grossMinor": 191904,
"refundsMinor": 1999,
"netMinor": 189905,
"payments": 96,
"customers": 94
}
]
}
}Query parameters:
range optional one of: last_24_hours, today, yesterday, last_7_days, last_30_days, last_90_days, this_month, last_month, last_12_months, year_to_date, custom
defaults to last_30_days
start optional YYYY-MM-DD, used when range is custom
end optional YYYY-MM-DD, used when range is custom, exclusiveGET /api/v1/reports/breakdown
Breaks a dimension down for the same range. Scope: reports:read. The dimension is checked against an allowlist, and an unknown value is refused with an invalid request error.
curl "https://abackanalytics.com/api/v1/reports/breakdown?dimension=source&range=last_30_days&limit=10" \
-H "Authorization: Bearer abk_xxxxxxxx.secret"{
"ok": true,
"data": {
"website": { "id": "site_...", "name": "Marketing site" },
"range": { "start": "2026-09-23", "end": "2026-10-23", "days": 30 },
"dimension": "source",
"rows": [
{ "value": "google", "visitors": 1904, "pageviews": 5210, "sessions": 2210, "events": 402 }
]
}
}Query parameters:
dimension optional one of: source, medium, campaign, channel, referrer_host, path, landing_page, country, device, browser, os, screen, event_name, term, content
defaults to source
range optional one of: last_24_hours, today, yesterday, last_7_days, last_30_days, last_90_days, this_month, last_month, last_12_months, year_to_date, custom
limit optional 1 to 200, defaults to 25
start optional YYYY-MM-DD, used when range is custom
end optional YYYY-MM-DD, used when range is custom, exclusiveGET /api/v1/live
The activity of the last few minutes: the most recent events, the number of visitors in the window and the time of the last event. Scope: reports:read.
The window is at most 60 minutes and the number of events is at most 200, because the endpoint reads the event stream rather than an aggregate. It is rate limited.
curl "https://abackanalytics.com/api/v1/live?minutes=5&limit=50" \
-H "Authorization: Bearer abk_xxxxxxxx.secret"{
"ok": true,
"data": {
"website": { "id": "site_...", "name": "Marketing site" },
"window": { "minutes": 5, "since": 1790000000000, "until": 1790000300000 },
"visitors": 12,
"lastEventAt": 1790000299000,
"events": [
{
"eventId": "evt_...",
"eventName": "pageview",
"eventType": "pageview",
"path": "/pricing",
"title": "Plans and pricing",
"source": "twitter",
"channel": "Social",
"country": "US",
"device": "desktop",
"occurredAt": 1790000299000
}
]
}
}Query parameters:
minutes optional 1 to 60, defaults to 5
limit optional 1 to 200, defaults to 50GET /api/account/sites
Lists the websites of the account the signed in user belongs to. This endpoint is authenticated with the session cookie of the dashboard, not with an API key, because it is scoped to the whole account rather than to one website.
The account is resolved from the session on the server. There is no account parameter in the query string, so there is no way to ask for the websites of another account. The answer carries the public tracking id of each website and never a secret: no visitor salt, no API key and no integration credential.
curl https://abackanalytics.com/api/account/sites \
-H "Cookie: abk_session=..."{
"ok": true,
"data": {
"websites": [
{
"id": "site_...",
"name": "Marketing site",
"domain": "example.com",
"timezone": "UTC",
"status": "active",
"trackingId": "abk_xxxxxxxxxxxxxxxx",
"retentionDays": 30
}
]
}
}Fields of each website:
id the identifier of the website
name the name of the website
domain the canonical domain, empty when there is none yet
timezone the timezone the days are counted in
status active, paused or archived
trackingId the public tracking id, safe to put in a browser snippet
retentionDays the number of days the raw events are keptError codes
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The request itself is wrong, for example an unknown date range or an unknown dimension. |
| 400 | validation_failed | The body did not match the schema. The error details list the field and the reason for each issue. |
| 401 | unauthorized | The Authorization header is missing, or the key is not valid, expired or revoked. |
| 402 | plan_restricted | The plan of the account does not include the feature the endpoint needs. |
| 402 | quota_exceeded | The monthly limit of the plan has been reached. |
| 403 | forbidden | The key is valid but the scope is missing, or the website in the body does not belong to the key. |
| 404 | not_found | The resource does not exist. |
| 409 | conflict | The request conflicts with an existing record, for example an account that already exists. |
| 413 | payload_too_large | The body is larger than the endpoint accepts. |
| 429 | rate_limited | Too many requests. The response carries a Retry-After header. |
| 500 | internal_error | Something failed on the server. The message is generic on purpose. Quote the request id when you report it. |
The remote MCP server
AI assistants that speak the Model Context Protocol can read your analytics at https://abackanalytics.com/api/mcp. Add it as a remote MCP server; a client that supports MCP authorization signs in by itself. It opens a page where you sign in, pick the one website it may read and approve. The assistant gets the read tools only: the overview, a breakdown and the live visitors.
Sign in follows OAuth 2.1 as the MCP authorization spec describes. A request without a token gets a 401 whose WWW-Authenticate header names the protected resource metadata. A client identifies itself with the https address of its client metadata document, or registers itself. There are no client secrets, PKCE with S256 is required, and the tokens are only valid at the MCP server. An access token lasts an hour and a refresh token 30 days; each refresh replaces both.
You can see and revoke the assistants you connected on the Developers page of the website. Access also ends when you leave the account.
MCP server https://abackanalytics.com/api/mcp
Protected resource https://abackanalytics.com/.well-known/oauth-protected-resource/api/mcp
Authorization server https://abackanalytics.com/.well-known/oauth-authorization-server
Authorization https://abackanalytics.com/oauth/authorize
Token https://abackanalytics.com/api/oauth/token
Dynamic registration https://abackanalytics.com/api/oauth/register
Revocation https://abackanalytics.com/api/oauth/revokeA client that cannot sign in can send an API key with the reports:read scope instead.
curl -X POST https://abackanalytics.com/api/mcp -H "Authorization: Bearer abk_xxxxxxxx.secret" -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'The command line interface
A small command line tool reads the same endpoints. It takes the key from the environment, so the key never ends up in a shell history.
export ABACK_API_KEY=abk_xxxxxxxx.secret
aback healthThe commands are:
aback health the state of the installation
aback report overview the totals, the goals and the revenue
aback report breakdown --dimension source one dimension broken down
aback live the activity of the last few minutes
aback send signup_completed record one event
aback snippet the tracking snippetThe full list is in the cli/README.md file of the repository.
Not documented here
This page documents the endpoints you call with an API key, and the account scoped read endpoint above. The CSV export uses a session cookie and answers with a file instead of the JSON envelope, so it is described in the OpenAPI document rather than here. There is no endpoint to manage members, roles or subscriptions over HTTP. If you need one of those, ask: the events page has the event contract and the revenue page has the payment contract.