Rate limits and errors

JMAD rate-limits by surface, not by account, since there are no accounts. Each surface gets its own fixed 60-second window and its own request budget:

SurfaceLimitExample paths
Pages600 requests/min/japan/<prefecture>/..., building pages, hubs
API120 requests/min/japan/api/v1/...
MCP120 requests/min/japan/mcp

The window resets on the clock, not per client: if you send your 120th API request at second 58 of a window, your budget resets at second 60, not 58 seconds later. Only the API surface reports your budget as you spend it: every API response under the limit carries X-RateLimit-Limit and X-RateLimit-Remaining. Pages and MCP responses carry neither header, on a success or a 429. Rate-limit rejections on those surfaces include Retry-After.

Going over the limit

A request over the limit gets 429 Too Many Requests with a Retry-After header giving the seconds until the next window opens, but the body differs by surface.

The API returns an ApiProblem JSON body:

{
  "status": 429,
  "title": "Too Many Requests",
  "detail": "The api surface allows 120 requests per minute; retry after 12s."
}

The MCP surface returns a JSON-RPC 2.0 error envelope instead, using the MCP error format:

{
  "jsonrpc": "2.0",
  "id": null,
  "error": {
    "code": -32000,
    "message": "the mcp surface allows 120 requests per minute; retry after 12s"
  }
}

The page surface returns the same signal in markdown, since a page fetch is more likely to come from a crawler reading text than a client parsing a schema:

# 429 Too Many Requests

This page surface allows 600 requests per minute. Retry after 12 seconds.

Either way, the connection is never simply dropped. Read Retry-After and wait; do not retry immediately in a loop.

Other error shapes

Every error the REST API returns is an ApiProblem JSON body with the same three fields, whatever the status:

{
  "status": 404,
  "title": "Not Found",
  "detail": "No building recorded for DNK-JP-13-99999999."
}
  • 400 for a request that does not match the endpoint's schema: a malformed id (anything other than DNK-JP-<2-digit prefecture>-<8 digits>), a page that is not a positive integer, or a slug that does not decode. The detail names which part of the request failed (path parameters, query parameters) without echoing your input. Every endpoint that takes a parameter declares this response in openapi.json. This shape is the REST API's; the page surface answers the same conditions differently, below.
  • 404 for a well-formed identifier with no record behind it, for an area slug with no published buildings (/japan/api/v1/areas/osaka), and for any path under /japan/api/v1/ that is not an endpoint. On the page surface the same conditions render the site's 404 page, and a markdown client gets a markdown recovery body instead: a note that the path names no record, the ID scheme for reference, a link to search, and a link to /llms.txt.
  • 405 with an Allow: GET, HEAD header for any other method against an API path. The API is read-only; HEAD works everywhere GET does.
  • 404 on the page surface for a segment that does not decode as well: a hub URL, a building URL or /japan/id/<id> whose segment is not a valid slug or a valid ID names no resource, so it is a 404 and not a 400 — the site's 404 page in HTML, the markdown recovery body when you ask for markdown. It is never a lookup that quietly returns an empty page. The page surface answers 400 only for a path carrying a percent-escape the server cannot decode, which is a malformed request rather than a missing resource.
  • 301 for a merged or retired ID: /japan/id/<id> redirects to the surviving ID's pretty path, and /japan/id/<id>.md redirects to the .md twin of that path. See DNK asset IDs for how merges work.
  • 308 for a path with a trailing slash: /japan/tokyo/chiyoda/ redirects permanently to /japan/tokyo/chiyoda, query string intact. The canonical form of every path in the /japan/ tree carries no trailing slash, and the redirect is permanent, so a client or a crawler can replace the URL it holds rather than following the hop again.

A 500 from the API is a bug on our side, never a signal about your request; a response the server could not encode is reported as a 500, not as a 400.

How to back off

Treat 429 as a signal to slow down, not an error to retry past. Read Retry-After and wait at least that long before your next call to that surface. If you are paging through search results (see search and pagination) and hit a limit mid-page, resume from the same page number after backing off; nothing about pagination state is lost.

MCP resource limits

Each server process admits up to 32 concurrent MCP requests. Excess requests receive HTTP 503 with Retry-After: 1; wait at least one second before retrying. This bound also covers clients that send their request bodies slowly.

Request-rate budgets are separate from memory limits. MCP POST bodies have a 65,536-byte limit and a 10-second body-read deadline. HTTP 413 means reduce the request size; HTTP 408 means the complete body did not arrive in time. Both include a JSON-RPC error envelope.

MCP sessions expire 15 minutes after initialization. A 404 for an expired or deleted session means initialize a new session without the old ID. Each server process supports up to 256 sessions, including initializations in progress. A new initialization at capacity receives HTTP 503 with Retry-After: 30. Wait before retrying. Existing sessions continue to work.

DELETE /japan/mcp with the session ID and negotiated protocol-version header terminates a session and returns HTTP 204 with no body. See MCP server for the complete session contract.

Next


Did this page help you?