Error codes
Every error body carries a code. Codes are stable within a version, so branch on them rather than on the message text. New codes may be added within v1, so treat an unrecognised code as a generic failure of its HTTP status class rather than crashing.
Authentication
invalid_token
401. The Authorization header is missing, malformed, or the key does not exist.
Check the header is exactly Authorization: Bearer asky_sk_.... The most common cause is a stray newline, quote, or trailing space picked up when the key was copied into an environment variable. Verify with GET /v1/me.
token_expired
401. The key passed its expiration date.
Roll the key to get a working replacement with the same name, scopes, and brand access. If you received expiry reminder emails and no one acted, check they are reaching a monitored address.
token_revoked
401. The key was revoked, or it was replaced by a roll and its grace window has closed.
If a roll is in progress, deploy the new value. If someone revoked it deliberately, ask before minting a replacement: it may have been revoked because it leaked.
Authorization
insufficient_scope
403. The key is valid but does not carry the scope this endpoint requires.
The message names the scope needed. GET /v1/me lists what the key has. Scopes cannot be added to an existing key, so create a new one with the access you need. See API keys.
plan_required
403. API access is not enabled for this workspace.
Contact us to enable it. This is not something you can change in Settings.
Requests
invalid_request
400. The request is malformed. The message names the problem.
Common causes: an unknown query parameter, usually a typo, since unknown parameters are rejected rather than ignored; a badly formatted date, which must be YYYY-MM-DD; a limit above 100; a date range wider than the endpoint allows; or a malformed id.
invalid_cursor
400. The cursor value is not valid for this request.
Cursors are opaque and short-lived. This usually means the cursor was stored and reused later, was modified, was issued for a different query, or came from a different key. Restart the walk from the first page.
not_found
404. The resource does not exist, or your key cannot reach it.
Both cases return the same response on purpose. Check the id came from a listing endpoint under the same key, and check GET /v1/me for the key’s workspace and brand access. A key restricted to specific brands returns this for every other brand.
method_not_allowed
405. v1 is read-only. Use GET.
Limits
rate_limited
429. Too many requests for this key in the current window.
Wait for Retry-After, then retry with exponential backoff and jitter. If you hit this routinely, raise limit to fetch more per request, filter server-side, or reduce polling frequency.
Server
internal_error
500. Something failed on our side. The request was not your fault.
Retry with backoff. If it persists, contact us with the request_id, which lets us find the exact failure in our logs.
service_unavailable
503. Temporarily unavailable, usually brief and usually during a deployment.
Retry after Retry-After. A scheduled sync should treat this as transient rather than alerting a person on the first occurrence.
Quick reference
| Code | Status | Retry? |
|---|---|---|
invalid_token | 401 | No |
token_expired | 401 | No |
token_revoked | 401 | No |
insufficient_scope | 403 | No |
plan_required | 403 | No |
invalid_request | 400 | No |
invalid_cursor | 400 | No, restart the walk |
not_found | 404 | No |
method_not_allowed | 405 | No |
rate_limited | 429 | Yes, after Retry-After |
internal_error | 500 | Yes, with backoff |
service_unavailable | 503 | Yes, after Retry-After |