Errors

Every failure, typed and documented

Errors arrive in the same envelope as data, carrying a stable type, an HTTP status and a doc_url that points straight at the section of this page describing it.

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.

402 · application/json
{
  "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"
}

Read the billing outcome

A paid live call reserves credits before collection. A confirmed failed collection is uncharged or refunded; the ledger may show a debit and matching refund. If credits_used is null or error.details.billing_status is pending_reconciliation, keep request_id and the original idempotency key. Do not treat an unresolved outcome as zero or start a replacement request.

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.

Correct a request

Read the field guidance

The error message itself explains supported corrections. For example, TikTok Shop search rejects limit: remove it and use the returned cursor for more results. That endpoint has no page-size control. Instagram, TikTok, X and Threads handles accept one leading @.

Request errors include error.details.guidance: the field, its accepted values when fixed, an example request and the next action. The same choices appear in the endpoint catalogue, Playground and MCP. Common case, space, hyphen and underscore variants are accepted. A missing target, invalid credential or account limit does not have an enum of valid values; the guidance explains what to check instead.

{
  "field": "date_posted",
  "accepted_values": [
    "last-week",
    "last-month",
    "last-year"
  ],
  "example": {
    "platform": "instagram",
    "endpoint": "search-reels",
    "params": {
      "query": "meme",
      "date_posted": "last-week"
    }
  },
  "action": "Use a supported date window, or omit date_posted for any date. Day and hour windows are not supported."
}

Examples show a valid request shape, not a guarantee that changing public content exists or has results. Instagram topic search only covers curated topics; use Reels search for keywords or hashtag search for hashtags. If the source cannot identify a bad field, we say so and provide the catalogue requirements rather than guessing.

Reference

Every error type

TypeStatusMeaning
UNAUTHORIZED401No 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_EXCEEDED402This key has an optional credit_limit and the call would push it past that cap.
RATE_LIMITED429The key or account reached its plan’s request or concurrency limit. Starter key limits begin at 600/minute and 50 in flight; higher paid tiers increase both. Free accounts share 600/minute and 50 in flight across keys. Invalid-key attempts can also be throttled.
INSUFFICIENT_CREDITS402Your account balance is below the cost of this call.
RESOURCE_NOT_FOUND404The endpoint exists, but the thing you asked it for does not — no such profile, post, repository or job.
INVALID_PARAMETERS400, 409, 413 or 422The request was understood but the parameters were not usable.
TARGET_ACCESS_DENIED403The requested website refused access to its content.
UPSTREAM_ERROR502, 503, or the upstream statusThe source platform answered with something we could not turn into data.
FREE_CAPACITY_UNAVAILABLE503Free data-access capacity is unavailable, or this account reached its daily Free allowance.
CAPACITY_UNAVAILABLE503Shared daily data-access capacity is exhausted.
UPSTREAM_TIMEOUT504The source platform did not answer inside the adapter’s timeout on any attempt.
ENDPOINT_NOT_AVAILABLE501The path is known, but no eligible real-data route can serve this request.
ENDPOINT_NOT_FOUND404 or 410No endpoint answers that path, or the endpoint has been retired.
INTERNAL_ERROR500Something went wrong on our side while processing the request.

In detail

What to do about each one

UNAUTHORIZED401

No key was sent, or the key does not match an active key on any account.

details.reason separates the cases: missing_api_key when no credential was sent, invalid_api_key when the key was not recognised (including keys you have revoked or deleted), and query_string_key_rejected when a key arrived as ?api_key= — keys travel in the x-api-key header or as a Bearer token, never in the URL.

What to do · Send the key in the x-api-key header. Check it is intact: 46 characters beginning mn_. Retrying an unchanged request will not help.

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_EXCEEDED402

This 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_LIMITED429

The key or account reached its plan’s request or concurrency limit. Starter key limits begin at 600/minute and 50 in flight; higher paid tiers increase both. Free accounts share 600/minute and 50 in flight across keys. Invalid-key attempts can also be 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. Check the scope and effective headroom headers. Batch items are admitted individually.

What to do · Wait for retry_after_seconds and retry. Pace requests below the rate and concurrency ceilings reported in the response headers. Check the rate-limit guide for your plan’s key and account limits.

INSUFFICIENT_CREDITS402

Your 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 · Wait for your allowance to reset, or upgrade your plan in Billing. Zero-cost endpoints such as credits/balance and utility/* keep working at any balance, so you can always read your own state.

RESOURCE_NOT_FOUND404

The 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 422

The 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 · 400, 413 and 422 INVALID_PARAMETERS responses include error.retryable = false: fix the request before trying again. Follow details.guidance for a correction and example. GET /v1/utility/endpoint?id=platform/endpoint returns that endpoint’s exact parameter schema, free. A 409 in-progress conflict still requires waiting for the original request. Absence of retryable is not permission to retry every error.

TARGET_ACCESS_DENIED403

The requested website refused access to its content.

A publisher HTTP 401 or 403 is a target access refusal. Your Monocrawl API key may still be valid. The message preserves the publisher status, and the failed extraction costs zero credits.

What to do · Use an accessible source URL. Repeating the same request does not resolve a publisher access restriction.

UPSTREAM_ERROR502, 503, or the upstream status

The source platform answered with something we could not turn into data.

Upstream 5xx responses are reported as 502. Some upstream faults keep their own status, such as 429 for an upstream rate limit. Eligible retries and fallbacks may already have been attempted. A 503 may mean a circuit cooldown, exhausted source quota or no currently eligible source; details.reason distinguishes these cases. Only circuit cooldowns promise recovery after a short retry-after delay. Confirmed uncharged or refunded failures report zero; an unresolved accounting outcome can report credits_used:null and billing_status:pending_reconciliation.

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.

FREE_CAPACITY_UNAVAILABLE503

Free data-access capacity is unavailable, or this account reached its daily Free allowance.

For a daily allowance, details.reason is daily_budget, details.scope is account and details.resets_at gives midnight UTC. The retry-after header gives the remaining wait. Active subscriptions, including complimentary subscriptions, are exempt from this Free daily budget. Other Free capacity protections may use shorter retry delays.

What to do · Honor retry-after. A daily allowance will not recover after a short retry loop. No customer credits are charged.

CAPACITY_UNAVAILABLE503

Shared daily data-access capacity is exhausted.

details.reason is daily_budget, details.scope is deployment and details.resets_at gives midnight UTC. This shared protection applies to all plans. No customer credits are charged.

What to do · Wait for the reset specified by retry-after; contact support if this persists.

UPSTREAM_TIMEOUT504

The 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_AVAILABLE501

The 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 410

No 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_ERROR500

Something went wrong on our side while processing the request.

The failure is logged against request_id without leaking internal details. Inspect credits_used and error.details.billing_status: an unresolved accounting outcome can require reconciliation.

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.

200 · explicit sandbox
{
  "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 confirmed failed collection is uncharged or refunded. An unresolved accounting outcome must be reconciled before starting another logical request.

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 outcomeOur behaviour
Network error or timeoutUp to 2 retries per provider by default (3 attempts), subject to deadline and budget.
Upstream 5xxRetried on the same schedule.
Upstream 429Retried — treated as transient pressure, not a verdict.
Upstream 404 or 422A genuine missing resource or invalid input is not retried. A provider-specific unsupported option can instead fall through to another capable source.
SuccessReturned 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. 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.

First call in under a minute

150 free credits and a ready-made key the moment you sign up. No card.