Errors & rate limits
Error shape
Section titled “Error shape”Errors are JSON with a human-readable error and, where useful, a stable
machine code:
{ "error": "this key is missing the 'analytics' scope", "code": "insufficient_scope" }| Status | Meaning |
|---|---|
400 |
Validation failed — the error says which field and why |
401 |
Missing or unknown API key |
402 |
Plan limit reached (quota or feature gate) — the console’s Billing page resolves it |
403 |
Key lacks a required scope (insufficient_scope) |
404 |
Resource not found (or not yours — we don’t distinguish) |
429 |
Rate limit exceeded — see below |
5xx |
Our fault; safe to retry with backoff |
Rate limits
Section titled “Rate limits”Requests are counted per minute against your plan’s allowance and enforced
with real 429 semantics:
HTTP/1.1 429 Too Many Requestsretry-after: 23x-ratelimit-limit: 120{ "error": "rate limit exceeded (120 requests/min on your plan)", "code": "rate_limited" }Honour retry-after and you’ll never see two in a row. Windows are aligned
to the minute, so a burst that trips the limit clears quickly.
Platform limits — stated plainly
Section titled “Platform limits — stated plainly”- Webhook latency: seconds, not sub-second (queue-decoupled from the redirect hot path).
- Delivery: at-least-once; de-dupe on
webhook-id(details). - Raw analytics retention: 90 days; aggregates kept indefinitely.
- Bulk create: 500 rows per request; monthly caps by plan.
- Custom stubs: ≤64 characters. Tags: ≤25 per link.
- Region: the platform runs in AWS eu-west-2 (London) — one region, by design, with the trust story documented at xengo.io/trust.
