PendiumDocs

Rate Limiting

Request rate limits, error handling, and best practices for the Pendium MCP server and REST API.

Limits

The MCP endpoint enforces per-caller rate limits within a 60-second sliding window:

Caller typeLimitKey
Authenticated60 requests / minuteUser ID
Unauthenticated15 requests / minuteIP address

These limits apply at the transport level — every MCP request (including initialize, tool calls, and listing) counts toward the limit.

REST API and the site export

REST endpoints use the same limits: 60 requests a minute per account with a key, 15 a minute per IP address without one. Four endpoints that start expensive work share a tighter limit of 5 requests a minute per account: POST /api/visibility/scan, POST /api/visibility/scan-preview, POST /api/visibility/topics, and POST /api/visibility/personas. Past it they answer 429 with code: "SCAN_RATE_LIMITED". scan-preview rejects a request without a key before any limit; for the other three, a request without a key counts against the 15-a-minute IP limit instead. The site export needs a key, except for drafts read with a preview token alone. A request with neither gets a 401 before any limit is checked. So only the per-account limit applies to it: the key's account, or for a token-only read, the account of the person who created the token.

Limits are counted per server instance, so they're approximate: treat them as a ceiling to stay under, not an exact budget.

For the site export, every request counts, including one answered with 304 Not Modified. The ETag saves bandwidth and rebuild time, not quota, so a site that checks for new content on a schedule should check every few minutes, not every few seconds. A build that pages through posts makes one request per 100 posts.

A limited REST request gets a 429 with the headers below and a JSON body:

{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMITED",
  "retryAfterMs": 12000,
  "message": "Too many requests. Limit: 60 per 60s. Try again in 12s."
}

MCP rate limit response

When the limit is exceeded, the server responds with HTTP 429 and a JSON-RPC error body:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32603,
    "message": "Rate limit exceeded. Limit: 60 per 60s. Try again in 12s."
  },
  "id": null
}

The response includes these headers:

HeaderDescription
Retry-AfterSeconds until the rate limit resets
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRemaining requests (always 0 when limited)

Best practices

  • Poll scans at 30–60 second intervals. After triggering scan_visibility, call get_scan_status every 30–60 seconds rather than in a tight loop.
  • Cache account data. The get_account response changes infrequently — avoid calling it on every interaction.
  • Batch your reads. If you need multiple pieces of data (report, voice, factsheet), call all tools in sequence within a single conversation turn rather than spreading them across many turns with overhead between each.
  • Use authenticated requests. The 60 req/min authenticated limit is 4x the unauthenticated limit. MCP clients authenticate via OAuth; REST API clients use an API key.

On this page