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 type | Limit | Key |
|---|---|---|
| Authenticated | 60 requests / minute | User ID |
| Unauthenticated | 15 requests / minute | IP 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:
MCP rate limit response
When the limit is exceeded, the server responds with HTTP 429 and a JSON-RPC error body:
The response includes these headers:
| Header | Description |
|---|---|
Retry-After | Seconds until the rate limit resets |
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Remaining requests (always 0 when limited) |
Best practices
- Poll scans at 30–60 second intervals. After triggering
scan_visibility, callget_scan_statusevery 30–60 seconds rather than in a tight loop. - Cache account data. The
get_accountresponse 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.