api
Errors
Every error is a problem details object with a stable code and the request id.
Updated · API v1
The format
Errors answer with Content-Type: application/problem+json (RFC 9457) and the HTTP status of their type. The type is the URL of the type's page below; code is the same type as a short string, for your code to switch on.
{
"type": "https://www.adspy.sh/docs/api/errors/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "Invalid query: query.limit: Too big: expected number to be <=100",
"code": "invalid_request",
"request_id": "a91c4e07-2b6d-4f38-8e15-c7d09b3f62a4",
"instance": "/api/v1/creatives",
"errors": [{ "path": "query.limit", "message": "Too big: expected number to be <=100" }]
}| Field | Meaning |
|---|---|
| type | The URL of the error type's documentation page. |
| title | The type's short title, the same for every occurrence. |
| status | The HTTP status. |
| detail | What went wrong with this request, in English. |
| code | The type as a stable string, such as invalid_request. |
| request_id | The request's id, also in the X-Request-Id header. Quote it to support. |
| instance | The path that was requested. |
| errors | On invalid_request only: each field problem, with its path (query.limit, body.brand_keys.0) and message. |
Handling errors
- Switch on code, not on detail: detail is for people and can change.
- New codes may be added; treat an unknown code by its HTTP status.
- 5xx answers never carry internal messages, and nothing was charged. A 401 never says whether a key exists.
- Retry only the types marked retryable, after Retry-After when it is sent, with the same Idempotency-Key for a POST.
Error types
| Status | Code | Meaning | Retry |
|---|---|---|---|
| 400 | invalid_request | A path parameter, query parameter or body field is missing, unknown or out of range, or the body is not valid JSON. | No |
| 400 | invalid_cursor | The cursor was not issued by Adspy, was altered, expired after 24 hours, belongs to another organization or another list, or was sent with different filters than the request that returned it. | No |
| 400 | idempotency_key_required | Every POST needs an Idempotency-Key header. | No |
| 401 | unauthorized | The Authorization header is missing, or the key is unknown, expired or revoked. | No |
| 402 | payment_required | The organization's contract does not cover this request, or a spending limit an organization admin set in Adspy stops it. | No |
| 402 | partner_key_cap_reached | Partner keys only: the credits this key's requests settled this calendar month (UTC), plus the most this request can settle, pass the key's monthly cap. | No |
| 402 | partner_tenant_cap_reached | Partner keys only: the request named a trial cap in X-Partner-Credit-Cap, and the credits this tenant settled since it was created, plus the most this request can settle, pass it. The answer carries cap and used. | No |
| 403 | enterprise_required | The REST API is part of Adspy Enterprise, and this organization has no active Enterprise contract. | No |
| 403 | insufficient_scope | The key lacks the permission this route needs (read, actions or export). | No |
| 403 | forbidden | The credential is recognized but may not make this request, for example a partner key that names another tenant than the one it is pinned to. | No |
| 403 | not_offered_to_partner | Partner keys only: exports and usage are not offered to partner keys. Exports settle in the background, after the answer that would bill them; a partner's usage is its own feed. | No |
| 404 | not_found | The route does not exist, or the brand, creative, post, collection, fetch or export is unknown to the key's workspace. | No |
| 405 | method_not_allowed | The route exists but not with this HTTP method. | No |
| 409 | idempotency_conflict | This Idempotency-Key was already used for a different request, or for a request that failed without a charge. | No |
| 409 | operation_in_progress | A request with this Idempotency-Key is still running. | Yes |
| 409 | conflict | The resource changed while the request ran (a fetch replaced, a report expired). | Yes |
| 413 | payload_too_large | The body, the query string or the result passes its size limit. | No |
| 415 | unsupported_media_type | A POST or PATCH body was not sent as JSON. | No |
| 422 | instagram_not_in_api | The request names Instagram: a platform, a post id, an account, a profile link or an export of Instagram data. Instagram data is not offered through the API. | No |
| 429 | rate_limited | The key sent more requests a minute than its contract allows, or the organization more research reads than its limit (120 reads a minute, or the contract's per-key rate when that is higher), shared by its keys and its MCP connections. | Yes |
| 429 | fetch_quota_exceeded | The organization started its contract's number of new brand or account collections this calendar month (UTC; 200 by default), counted across its API keys and its MCP connections. | Yes |
| 429 | partner_fair_use | Partner keys of a client with a fair use policy only: this tenant settled its rolling 48-hour research allowance (scope research), or started its rolling 30-day number of new brand or account collections (scope collections). The answer carries scope and retry_at, the exact time the request fits again. | Yes |
| 429 | fetch_limited | The organization has three fetches running, or this advertiser was requested in the last hour. | Yes |
| 500 | internal_error | Adspy failed to answer. Nothing was charged. | Yes |
| 503 | service_unavailable | A dependency (the creative library, collection or exports) is temporarily unavailable. Nothing was charged. | Yes |
| 504 | timeout | The library query exceeded its time limit. Nothing was charged. | Yes |