Rate limits & errors

Rate limits & errors Pro

Rate limits

The API is a Pro feature. Requests are rate limited per workspace (all of a workspace's tokens share one quota - minting more doesn't raise it), and MCP tool calls count against the same quota:

WindowLimit
Reads (GET)600 / min
Writes (POST · PATCH · DELETE)60 / min
Burst (read + write)30 / sec
Daily total10,000 / day

Writes are held about ten times tighter than reads. A write is the expensive side: updating a map replaces its whole node and edge set, so one call rewrites many rows. It is also the destructive side. Nothing legitimate needs write volume - importing a repo is a single create, an editing session is a handful - so the ceiling is set well above real use and well below what a looping client can burn through. This mirrors how GitHub caps content-generating requests far below its general budget.

MCP tools draw from the same two buckets: the read tools count as reads, and create_map / update_map / delete_map count as writes. The same operation costs the same whether it arrives over REST or MCP.

The per-minute and burst limits cap how fast you can call; the daily total caps overall volume and resets daily at 00:00 UTC - the same instant for every workspace. Hitting the daily cap returns 429 with "Daily request quota exceeded." and a Retry-After counting down to midnight UTC. Running into these ceilings at scale? Talk to support and we'll raise them.

Every response carries the current limit state in its headers, so you can back off before you're throttled:

HeaderExampleMeaning
RateLimit-Limit600Requests allowed in the current window
RateLimit-Remaining588Requests left before you're throttled
RateLimit-Reset41Seconds until the window resets
Retry-After41Seconds to wait - only sent on a 429

When you exceed the limit the API responds with 429 Too Many Requests and a JSON body describing when to retry:

{
  "error": "rate_limited",
  "message": "Rate limit exceeded. Retry after 41s.",
  "retryAfter": 41
}

Honor the Retry-After header with exponential backoff and jitter rather than retrying immediately - tight retry loops just burn your remaining budget. Batch writes where you can.

You can watch today's usage (REST API vs MCP, split out) and the daily history in Dashboard → Settings → Workspace → API.

Embeds are not rate limited

Embedded maps are a public, cached, credential-free surface - they carry no token, so they never touch these quotas. A widely-embedded map costs you nothing against your API limits.

Abuse protection & auto-disable

A 429 is not a penalty - it just means "slow down." A well-behaved client that honors Retry-After is never punished. But a client that keeps hammering through its own 429s at a high sustained rate (ignoring Retry-After) is treated as abuse, and the token is automatically disabled.

  • Detection is rate-based: it only trips on a sustained flood of requests sent while already rate-limited (on the order of tens per second). Normal usage, and even an occasional retry after a limit, never trigger it.
  • A disabled token stops authenticating immediately (401 invalid_token), exactly like a revoked one.
  • There is no re-enable. The token shows as Disabled in Settings → Workspace → API with the reason; the fix is to delete it and create a new one. (Rotating is also the right move if a token has leaked - the disable usually means someone is misusing it.)

Honor the Retry-After header with exponential backoff and jitter. Tight retry loops don't just burn your budget - sustained ones will get the token disabled.

Log retention

Each request (REST and MCP) is recorded to a per-workspace activity log, surfaced as Recent requests on the API page: what ran (the endpoint, or the MCP tool name), which map it touched, who authorized it, the status, and how long it took. Logs are retained for 30 days, then automatically deleted, and can be filtered and exported as CSV. The log is for observability only; it's never used for billing.

Two things it deliberately doesn't capture: rejected sign-ins (a 401 never reaches the log) and requests turned away during a burst (those are refused before any database work, which is what keeps a flood cheap). Logging is also best-effort, so a database blip can drop a row. A quiet log is not proof that nothing happened - for abuse specifically, rely on the auto-disable behaviour above rather than on log volume.

Errors

Every error shares the same JSON envelope - a machine-readable error code, a human message, and sometimes extra fields (fields, limit, retryAfter). Always branch on the HTTP status and the error code, never on message text.

Statuserror codeWhen
400invalid_requestMalformed body, missing required field, or unknown export format.
401unauthorizedNo bearer token supplied.
401invalid_tokenToken unknown, expired, or revoked.
403node_limitMap exceeds your plan's node cap.
403forbiddenFeature gated behind a higher plan, or the credential is missing a required scope.
404not_foundThe map doesn't exist in your workspace.
429rate_limitedToo many requests - back off per Retry-After.
500server_errorSomething failed on our side; safe to retry.
{
  "error": "invalid_token",
  "message": "The API token is invalid, expired, or revoked."
}

A 500 carries an X-Request-Id header - include it when you contact support so we can trace the failure. These are transient; retry with backoff.

to paste into any AI.

Ask ChatGPT about this page.

Ask Claude about this page.

Need help? Sign in to chat with support

Have ideas?

General inquiry? Email support.