API

Errors and limits

HTTP status codes on the Content API, JSON-RPC error codes on MCP, the deprecation headers, and the rate limits that apply to both.


The two surfaces report failure differently, because they speak different protocols. The Content API answers with HTTP status codes; MCP is JSON-RPC, so a failed call is usually an HTTP 200 carrying an error object.

Content API#

Errors come back as { "error": "<message>" } with a matching status.

StatusBodyWhat went wrong
400Page and limit are requiredA category feed was requested without page or limit.
400Lang is requiredA category feed was requested without lang.
401Invalid API KeyNo credential was sent, or it does not match any key. Check the Authorization: Bearer header.
403Permission deniedThe key is real but its permission level does not include reading. Rotate it to a read (2) or read + write (3) key.
404Article not foundNo published article matches that handle or article_id. A draft or a future-dated article looks exactly like a missing one from here, on purpose.
500Generic messageSomething failed on our side. Retry with backoff.

An empty array is not an error

Sending no recognized parameter returns [] with a 200. A category feed with no matching articles returns { "articles": [], "has_more": false }, also with a 200. Only a single-article lookup produces a 404.

Deprecation headers#

Passing the API key as ?private_key= or ?api_key= still works, and every such response carries two headers so a client can notice before the form is removed.

http
Deprecation: true
Warning: 299 - "Passing the API key as a query parameter is deprecated; use the Authorization: Bearer header"

The fix is one line: move the key into Authorization: Bearer <key>. See Authentication.

MCP errors#

Two layers can fail, and they are reported differently on purpose.

  • The envelope fails -- bad JSON, an unknown method, a missing tool name, an unusable credential. You get a JSON-RPC error object with one of the codes below.
  • The tool fails -- the article does not exist, the handle is taken, publishing was refused for a missing cover. You get a normal result with isError: true and a human-readable message in the text content, because the model is meant to read it and correct itself.
CodeMeaningTypical cause
-32700Parse errorThe request body was not valid JSON.
-32600Invalid requestThe body was not a JSON-RPC object or batch.
-32601Method not foundA method other than initialize, tools/list, tools/call or ping.
-32602Invalid paramsNo tool name, an unknown tool, a project_id this connection cannot reach, or a non-scalar argument rejected by the injection guard.
-32603Internal errorAn unexpected failure. The detail is suppressed; the call is still logged.
-32001UnauthorizedMissing, expired, revoked or unrecognized credential; or the rate limit.
A permission refusal, as the caller sees it
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Error: Permission denied: \"delete_article\" needs the \"delete_project_articles\" permission on \"Acme Blog\", and your role there is CONTRIBUTOR. The connection cannot do more than your own account can. Call get_my_capabilities for the full picture of what is allowed here."
      }
    ],
    "isError": true
  }
}

A missing tool is a permission answer

Tools a connection may not use are absent from tools/list rather than present and failing. If an agent reports that a tool "does not exist", the cause is almost always the grant's access level or your role on the project, not a server problem. get_my_capabilities prints the resolved picture.

A 401 on the MCP endpoint#

An unauthenticated MCP request answers with a WWW-Authenticate header pointing at the protected-resource metadata document. That is the standard handshake: a compliant client reads it, finds the authorization server, registers itself and starts the consent flow, all without being configured.

http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://reblog.so/.well-known/oauth-protected-resource"

Rate limits and timeouts#

LimitValueApplies to
Requests per second5The MCP endpoint, counted per OAuth grant or per API key. Over the limit, the call is refused and recorded.
Request duration300 secondsOne MCP request. Long enough for image generation and article writing, which routinely run 90 to 120 seconds.
Feed page size30The Content API limit parameter, capped server-side.
Access token60 minutesOAuth access tokens. Clients refresh silently.
Refresh token60 daysRotated on every use; replaying a consumed one invalidates the chain.
Authorization code60 secondsSingle use, PKCE required.

The rate limit is a per-process sliding window, so it is approximate under heavy parallelism. Treat 5 requests per second as the design target, not a hard ceiling to ride.

Every outcome is logged

Rejected credentials, rate-limited calls and permission refusals all appear in Settings -> MCP activity in the dashboard, next to the calls that succeeded. If something is failing and the reason is not obvious from the client, that page has the server's side of the story.