/v1 API returns is below, with what causes it and what you do
about it.
Error shape
Every error is JSON with anerror string:
/ads adds a stable code. /commerce-metrics and the cost-import routes add a stable
reason. Branch on code or reason rather than the error text, which we reword.
Status codes
Rate limit
Write endpoints spend a second, smaller budget on top of this one: 20 write requests per
key per minute, including dry runs. A write therefore needs room in both budgets.
The limit counts requests, not rows. One
/events request for 1000 rows costs the same
as one /workspace request.
Every /v1 response carries your current budget, so you do not have to be rejected to
find it.
The first two headers are present on success and on error responses.
Per-key limits
The workspace owner sets a named key’s limit in Settings → API. See API keys. The plan caps it.GET /v1/usage and GET /v1/context return the limit in effect as rate_limit.
When the limit store is unreachable
Reads and writes behave differently on purpose.
A read outage is a wasted query, so a store outage must not take every read down with
it. Reads fall back to a standby limit of 30 requests per 60 seconds. That limit is
per server, not per key, so your effective budget during an outage is smaller than 100 and
is not exactly predictable. Treat
X-RateLimit-Degraded: 1 as a signal to slow down.
A write that lands twice costs a duplicate conversion rule or a duplicate postback, so
writes stop rather than guess. Send an Idempotency-Key on every write, and a retry after
a store outage stays safe.
Key requests and token exchange
These routes create credentials, so their limits fail closed. When the limit store is unreachable they return503.
Parameter limits
We clamp a value above the maximum instead of returning
400, so an oversized limit
still gives you a page of data.
Pagination
/events and /users page with limit and offset, and both return has_more. Walk a
page at a time, adding limit to offset, and stop when has_more is false. Do not
stop on a short page instead, because a full range can still end on one. Keep every other
parameter identical across the walk, and keep the date range fixed, or the pages will
overlap. On /users, offset plus limit must stay at 5000 or less, and a larger
window returns 400 rather than a truncated page. Narrow the date range or pass search
when you need to go deeper.
On /events, total_count counts the whole range, not the page. With property_filters
and no include_total, it is exact on the last page and a lower bound before it, flagged by
total_count_is_lower_bound: true. Send include_total=true for the exact count on every
page. That count reads the range a second time, so the request takes longer.
Ingest limits
Event ingest runs on a different host,https://ingest.datalyr.com, and has its own
limits.
The two ingest transports carry different limits. /track takes one server event per
request. The SDK batch transport takes an array.
The batch limit counts events, not requests. A batch of 50 events costs 50. The
/track
limit counts requests, which is the same thing there, because each request carries one
event. See Ingest API for the full /track contract.
Verify a limit
Send 101/v1/workspace requests inside one minute with a key at the default limit.
Request 101 returns 429 and the header Retry-After: 60.
Next
- API reference: the endpoint list and date rules.
- Authentication: key types and the
401body. - Web SDK: how the browser SDK retries a
429.