The shape
What an error looks like
Branch on success. When it is false, error.type is the value to switch on — it is stable across releases, unlike error.message, which is written for humans and may be reworded.
{
"success": false,
"error": {
"type": "INSUFFICIENT_CREDITS",
"message": "Not enough credits remaining for this request.",
"status": 402,
"doc_url": "https://www.monocrawl.com/docs/errors#insufficient-credits",
"details": { "required": 5, "remaining": 2 }
},
"credits_used": 0,
"credits_remaining": 2,
"request_id": "req_bd41f9027ac3e6518f"
}credits_used is always 0
Failed calls have a net charge of 0. A paid live call reserves credits before the upstream fetch and refunds that reservation if it fails; the ledger may show both the debit and its matching refund.
doc_url is generated
It is built from the error type: lowercased, underscores to hyphens, appended to this page. Every link below is a live anchor you can hand to a user.
request_id identifies the call
Standard envelopes carry it, including individual batch results. Batch wrappers and protocol errors may not. Quote the affected request id when contacting support.
Reference
Every error type
| Type | Status | Meaning |
|---|---|---|
UNAUTHORIZED | 401 | No key was sent, or the key does not match an active key on any account. |
KEY_REVOKED | — | Reserved. Part of the published error-type list, but not currently emitted. |
KEY_LIMIT_EXCEEDED | 402 | This key has an optional credit_limit and the call would push it past that cap. |
RATE_LIMITED | 429 | The key exceeded 600 requests in the last minute or 25 in flight, or invalid-key attempts were throttled. |
FAIR_USE_EXCEEDED | 429 (503 while the quota system itself is unavailable) | You reached the rolling 30-day fair-use ceiling on a metered endpoint. |
INSUFFICIENT_CREDITS | 402 | Your account balance is below the cost of this call. |
RESOURCE_NOT_FOUND | 404 | The endpoint exists, but the thing you asked it for does not — no such profile, post, repository or job. |
INVALID_PARAMETERS | 400, 409, 413 or 422 | The request was understood but the parameters were not usable. |
UPSTREAM_ERROR | 502, 503, or the upstream status | The source platform answered with something we could not turn into data. |
UPSTREAM_TIMEOUT | 504 | The source platform did not answer inside the adapter’s timeout on any attempt. |
ENDPOINT_NOT_AVAILABLE | 501 | The path is known, but no eligible real-data route can serve this request. |
ENDPOINT_NOT_FOUND | 404 or 410 | No endpoint answers that path, or the endpoint has been retired. |
INTERNAL_ERROR | 500 | Something went wrong on our side while processing the request. |
In detail
What to do about each one
KEY_REVOKED—Reserved. Part of the published error-type list, but not currently emitted.
A revoked key today answers 401 UNAUTHORIZED with details.reason = invalid_api_key. The type is listed here because it appears in the error-type roster served by /v1/utility/llms, and a defensive client should map it alongside UNAUTHORIZED.
What to do · Handle it exactly as you handle UNAUTHORIZED: stop retrying and surface a re-key prompt.
KEY_LIMIT_EXCEEDED402This key has an optional credit_limit and the call would push it past that cap.
details carries credit_limit and credits_used. The cap is reserved atomically before the wallet is touched, so it can also trigger mid-flight when several concurrent calls on one key race towards the same limit. Nothing is charged.
What to do · Raise the limit on the key, move the workload to another key, or let the budget do its job and stop the caller.
RATE_LIMITED429The key exceeded 600 requests in the last minute or 25 in flight, or invalid-key attempts were throttled.
The response carries a retry-after header and details.retry_after_seconds. A sliding-window rejection reports the seconds until the oldest request ages out; a concurrency rejection reports 1 second. Limits are per key, and batch items are admitted individually.
What to do · Wait for retry_after_seconds and retry. If you hit this constantly, spread the load or cap your own client-side concurrency below 25.
FAIR_USE_EXCEEDED429 (503 while the quota system itself is unavailable)You reached the rolling 30-day fair-use ceiling on a metered endpoint.
Eligible endpoints using our metered commercial upstream share a per-account ceiling on upstream attempts per rolling 30 days; retries count. A refusal reports the limit and usage in details.limit and details.used. Quota-checked live responses report x-fairuse-limit and x-fairuse-remaining when available; cached responses may omit them. Missing headers do not mean unlimited quota. If the quota check is briefly degraded, a small shared temporary limit applies; if it is fully unavailable, these endpoints answer 503 until it recovers. Degraded refusals carry details.degraded, x-fairuse-degraded and retry-after. Nothing is charged for a refusal.
What to do · If you genuinely need this volume, contact us — sustained high-volume profile traffic is exactly what enterprise pricing is for, and the ceiling rises as our upstream rates improve.
INSUFFICIENT_CREDITS402Your account balance is below the cost of this call.
details carries required and remaining, and the envelope repeats the balance in credits_remaining. It can also surface after a concurrent-spend race even when the pre-flight check passed — in which case the request is refused honestly and charged 0.
What to do · Top up in the console. Zero-cost endpoints such as credits/balance and utility/* keep working at any balance, so you can always read your own state.
RESOURCE_NOT_FOUND404The endpoint exists, but the thing you asked it for does not — no such profile, post, repository or job.
This is a definitive answer, not a fault: the pipeline neither retries it nor falls back to another provider, because doing so would invent data for a genuine miss.
What to do · Check the identifier. Handle it as an ordinary empty result rather than an outage.
INVALID_PARAMETERS400, 409, 413 or 422The request was understood but the parameters were not usable.
Endpoints answer 422 for a missing or blank required parameter, an unparseable URL or an out-of-range value; details usually names the field (for example { "missing": "handle" }). A parameter name the endpoint does not declare answers 400 before any cache lookup or charge, with details.unknown_parameters, details.accepted_parameters and, for a near miss, details.suggestions (for example { "handel": "handle" }); an empty undeclared value is treated as omitted. POST /v1/batch answers 400 for a malformed body or a list longer than 20 items, and 413 if its body exceeds the size limit. Idempotency conflicts use 409 while the original is in progress and 422 if the key is reused for different parameters.
What to do · Fix the request. GET /v1/utility/endpoint?id=platform/endpoint returns that endpoint’s exact parameter schema, free.
UPSTREAM_ERROR502, 503, or the upstream statusThe source platform answered with something we could not turn into data.
Upstream 5xx responses are reported as 502. Some non-5xx upstream faults keep their own status — an exhausted upstream rate limit, for instance, reaches you as 429 with this type. Eligible retries and fallbacks may already have been attempted; deadlines, quota, configuration or spending limits can stop the chain earlier. A 503 with this type means something different: after repeated failures we take that upstream out of rotation for a short cooldown, so the request was refused immediately without calling it. That response carries a retry-after header, and like every error it costs 0 credits.
What to do · Retry with backoff, honouring retry-after when it is present. If it persists for one platform, check /v1/status for that platform’s error rate before assuming the fault is yours.
UPSTREAM_TIMEOUT504The source platform did not answer inside the adapter’s timeout on any attempt.
Every attempt — the first plus its retries — has to finish inside the adapter’s own request timeout. A timeout is treated as transient, but retries are bounded by the request deadline and available upstream budget.
What to do · Use bounded backoff. A returned UPSTREAM_TIMEOUT error has a net cost of 0, but a timeout in your client does not prove the server stopped. A later successful retry may be billed; use Idempotency-Key from the first direct request to protect ambiguous retries when the store is available.
ENDPOINT_NOT_AVAILABLE501The path is known, but no eligible real-data route can serve this request.
Distinct from ENDPOINT_NOT_FOUND on purpose: the path is real and planned, we simply cannot serve it with genuine data today. The production API will not answer it with sample data — realistic-looking JSON you could mistake for a result is worse than a clear refusal. Unwired-route refusals include a sandbox path in details; other eligibility refusals may not.
What to do · Call the same path under /sandbox to develop against a clearly-marked sample response, or GET /v1/utility/endpoints for registered operations and their metadata. The refusal costs 0 credits.
ENDPOINT_NOT_FOUND404 or 410No endpoint answers that path, or the endpoint has been retired.
details carries the platform and endpoint as parsed from the URL, which is usually enough to spot a typo or a missing path segment. A retired endpoint answers 410 with this same type: details.reason is endpoint_removed, details.successor names the operation to call instead, and the versioning page explains the notice you had before it went.
What to do · Check the path against GET /v1/utility/endpoints — the registered catalogue, free to call.
INTERNAL_ERROR500Something went wrong on our side while processing the request.
The failure is logged against the request_id in the envelope, with no internal detail leaked into the response body. Nothing is charged.
What to do · Retry once. If it repeats, send the request_id to support — it is the fastest way to the exact log line.
Sandbox
Sample output requires an explicit sandbox URL
Production /v1 returns real upstream/cache data or a typed error. It never substitutes representative data when a provider fails. If you deliberately use the same path under /sandbox, the URL and envelope both disclose that the output is synthetic, and it costs nothing.
{
"success": true,
"platform": "youtube",
"endpoint": "/sandbox/youtube/channel",
"data": {
…
"_warnings": [
"SYNTHETIC: representative response shape, not observed data."
]
},
"credits_used": 0,
"credits_remaining": 99,
"request_id": "req_2e8a5c704b1df936a0",
"cached": false,
"synthetic": true
}synthetic: true on the envelope
The sandbox marks representative output on the envelope as well as in the URL.
credits_used is 0
Sandbox-shaped numbers are never charged as observed data.
A warning inside data
data._warnings states that the result is synthetic, so saved output keeps the disclosure.
/v1 errors honestly
A provider outage returns an error with zero charge; integrations can retry or fail over intentionally.
Sandbox output is for schema exploration and test fixtures. Do not persist it as real data or compute real-world metrics from it. Production clients do not need a synthetic-response branch unless they intentionally call /sandbox.
Retries
What we retry before you see a failure
Transient upstream faults can be retried inside the request. Attempts and fallbacks are bounded by the request deadline, provider eligibility, quota and spending limits, so not every request can use the full retry allowance.
| Upstream outcome | Our behaviour |
|---|---|
| Network error or timeout | Up to 2 retries per provider by default (3 attempts), subject to deadline and budget. |
| Upstream 5xx | Retried on the same schedule. |
| Upstream 429 | Retried — treated as transient pressure, not a verdict. |
| Upstream 404 or 422 | A genuine missing resource or invalid input is not retried. A provider-specific unsupported option can instead fall through to another capable source. |
| Success | Returned immediately. |
Backoff is exponential from a 150 ms base with random jitter, so simultaneous retries spread out rather than arriving together. The attempt count is recorded on the usage event for the call.
Retry these
For transient 429, 500, 502, 503 and 504 failures, use bounded exponential backoff and honour retry-after. Check the error type: a fair-use ceiling needs a quota reset or changed limits, not a tight retry loop. Successful retries are billed normally.
Do not retry these
400, 401, 402, 404 and 422 — the request has to change first. Retrying an unchanged 402 will not conjure credits, and retrying a 404 will not conjure the resource.
Rate-limit numbers and the full credit model live in endpoint pricing.