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:
| Surface | Limit | Example paths |
|---|---|---|
| Pages | 600 requests/min | /japan/<prefecture>/..., building pages, hubs |
| API | 120 requests/min | /japan/api/v1/... |
| MCP | 120 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 thanDNK-JP-<2-digit prefecture>-<8 digits>), apagethat is not a positive integer, or a slug that does not decode. Thedetailnames which part of the request failed (path parameters, query parameters) without echoing your input. Every endpoint that takes a parameter declares this response inopenapi.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, HEADheader for any other method against an API path. The API is read-only;HEADworks everywhereGETdoes. - 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>.mdredirects to the.mdtwin 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
- Search and pagination for the parameters that shape a request before you worry about its limit.
- Authentication for what the rate limit key is built from.
Updated 1 day ago
