Skip to main content

Errors

Every way the Level API can say no — thirteen problem types, what each one means, and which of them are worth retrying.

Errors are returned as RFC 9457 problem documents with the content type application/problem+json:

{
  "type": "https://docs.marketing-bar.com/api/problems/insufficient-scope",
  "title": "Insufficient scope",
  "status": 403,
  "detail": "This endpoint requires reports:data.",
  "instance": "/v1/reports/8a1b0c2d…/data",
  "traceId": "0HN7…",
  "retryAfterSeconds": null
}

Branch on type, never on title or detail. The catalogue below is closed — there are thirteen types and there will not be a fourteenth without a version bump — while the human wording may be improved at any time.

traceId identifies the request in Level's logs. Quote it when you contact support.

The catalogue

TypeHTTPRetry?
invalid-token401After getting a new token
insufficient-scope403No
plan-feature-required403No
subscription-locked403No
not-found404No
invalid-parameter400No
rate-limit-exceeded429Yes, after Retry-After
rate-limiter-unavailable503Yes
billing-unavailable503Yes
identity-unavailable503Yes
data-source-unavailable503Later
request-timeout503With a smaller request
internal-error500Yes, once

The caller's side

invalid-token — 401 {#invalid-token}

The token is missing, malformed, expired, belongs to a revoked client, is a signed-in person's token rather than an API client's, or was issued for a different audience.

Level does not say which: telling a caller why a credential was refused is help offered to whoever is guessing.

What to do. Exchange the client secret for a fresh token and retry once. If the new token is refused too, the client or its secret has been revoked — check the API clients screen.

insufficient-scope — 403 {#insufficient-scope}

A valid token whose scopes don't cover this endpoint — asking for report data with only reports:read, for instance.

What to do. Two possibilities: you asked for too few scopes in the token request, or the client was never granted the scope. GET /v1/me shows what the token actually holds. Granting a new scope means editing the client — no code change fixes it.

plan-feature-required — 403 {#plan-feature-required}

The organization owner's plan doesn't include the public API (Business and above). detail names the plan.

What to do. Nothing technical. See Plans & limits.

subscription-locked — 403 {#subscription-locked}

The owner is over their plan limits and everything they own is read-locked. On /v1 this blocks reads too, unlike in the app — but /v1/me, /v1/health, /v1/docs and /v1/openapi.json keep answering, so an integration can always read that it is locked.

What to do. The owner has to delete down to fit the plan or upgrade. See what happens if you go over the limits.

not-found — 404 {#not-found}

No such resource — or one that exists but is outside this client's allow-list, or belongs to another organization. All three answer the same way on purpose: a 403 would confirm that somebody else's row exists.

What to do. Check the id, then check the client's allow-list in the API clients screen.

invalid-parameter — 400 {#invalid-parameter}

A parameter is missing, malformed or out of range: pageSize over 200, a date range wider than 400 days, an unknown metric name, a groupBy this report can't answer. detail says which — and for groupBy, which ones it can.

What to do. Fix the request. Retrying it unchanged will fail identically.

rate-limit-exceeded — 429 {#rate-limit-exceeded}

The hourly budget of the subscription is spent, or this client has reached its 50 % fair share while others still have room. Retry-After carries the seconds.

What to do. Wait out Retry-After, then look at Quotas & rate limitsETag caching and wider, rarer pulls are usually the whole fix.

Level's side

rate-limiter-unavailable — 503 {#rate-limiter-unavailable}

Level can't meter requests right now, and an unmetered request is not allowed through. Fail-closed, one mode only.

What to do. Retry after Retry-After. /v1/me still answers and reports quota.state: "unavailable".

billing-unavailable — 503 {#billing-unavailable}

The owner's plan couldn't be established — not even from a snapshot, which is served for up to 60 minutes when the billing store is briefly unreachable.

What to do. Retry. /v1/me reports plan.state: "unknown" while this lasts.

identity-unavailable — 503 {#identity-unavailable}

Only on the client-registration path, and therefore only ever seen in the app, not by an integration: the service that holds credentials didn't answer.

What to do. Try creating or rotating the client again in a minute.

data-source-unavailable — 503 {#data-source-unavailable}

An ad platform connection expired or lost a permission, so there is nothing to serve — and it isn't your request's fault.

What to do. Someone has to reconnect the platform in Level. See Managing connections. Retrying earlier than that won't help.

request-timeout — 503 {#request-timeout}

The request hit the thirty-second deadline and was cancelled.

What to do. Ask for less: a shorter date range, fewer ad accounts via adAccountIds, a smaller pageSize. Repeating the same call will time out again.

internal-error — 500 {#internal-error}

Anything else. It carries no detail, ever.

What to do. Retry once. If it persists, contact support with the traceId from the response.