Skip to content

Limits, billing and security / Limits and errors

Limits and errors

Every limit Relay enforces in one table, the three shapes an error takes, and the outcome of each call.

Limits#

LimitValuePast the limit
Projects per account10A new project is refused.
Links per project10,000POST /v1/links answers LIMIT_REACHED.
Links created per IP address60 per minute429 RATE_LIMITED.
Link label64 characters400 VALIDATION_ERROR.
Calls per minute, per link60 by default, up to 6,000 in settingsRefused at once, never counted.
initialize, tools/list and ping, per link300 per minute429 with Retry-After.
Requests with an unknown URL token, per IP address120 per minute429 with Retry-After.
MCP request body80 KiB413.
Calls in flight, per link8A tool error saying the app is busy. Refused at once, never counted.
Time to answer a call180 secondsA tool error for the host and cancel for the app. Counted.
Time to send hello10 secondsSocket closed with 4400.
Tools per hello64Socket closed with 4400.
Manifest: app, instructions and tools as JSON64 KiBSocket closed with 4400.
Tool name64 charactersSocket closed with 4400.
Tool description2,000 charactersSocket closed with 4400.
Instructions4,000 charactersSocket closed with 4400.
ping frames and link.* commands, per link30 per minuteA rate_limited error frame; the frame is not executed and the socket stays open.
Invalid frames on one socket10Each gets an error frame; past the limit, the socket closes with 4400.
Call arguments as JSON64 KiB-32602 before the app sees it, never counted.
Result: content and structuredContent as JSON128 KiBAn invalid_result error frame for the app, and a tool error for the host.
Any frame256 KiBConnection dropped with no close frame; the app sees 1006. Reconnect with backoff.
Silence on the socket90 secondsSocket closed. Reconnect with backoff.
Call metadata kept30 daysOlder calls leave the Calls tab.

Three shapes of error#

Where an error shows up depends on who made the request.

Link creation: HTTP errors#

POST /v1/links answers errors with a status and an error object with a stable code. Every code is listed under Links.

⫻

401 Unauthorized

HTTP/1.1 401 UnauthorizedContent-Type: application/jsonX-Request-Id: req_0199c4e25b7a7d318f2e6a1c9b3d4e58
{  "error": {    "code": "UNAUTHORIZED",    "message": "Missing or invalid publishable key",    "requestId": "req_0199c4e25b7a7d318f2e6a1c9b3d4e58"  }}

The app's socket: error frames and close codes#

A frame Relay cannot use gets an `error` frame. A problem with the link or the hello closes the socket with a code:

⫻

error

{  "type": "error",  "id": null,  "code": "invalid_message",  "message": "tools[2].inputSchema is not a valid JSON Schema"}
CodeMeaning
4400Invalid or late hello, an unsupported protocol, or more than 10 invalid frames on one socket.
4401Unknown or revoked link token. Do not reconnect.
4403Origin not allowed. Do not reconnect.
4001A newer socket took over the link.
4410The link was revoked. Do not reconnect.
4423The project is suspended. Reconnect with backoff.
4429Too many open sockets for the project.

Reconnecting explains the backoff for the rest.

The host: tool errors and JSON-RPC errors#

When a call cannot be answered by the app, the host gets a tool result with isError: true that the model can repeat to the person. A call that is wrong in itself, such as an unknown tool or invalid arguments, gets JSON-RPC error -32602. Both are described under MCP endpoint.

⫻

200 OK

HTTP/1.1 200 OKContent-Type: application/jsonX-Request-Id: req_0199c4e25b7a7d318f2e6a1c9b3d4e58
{  "jsonrpc": "2.0",  "id": 3,  "result": {    "content": [      {        "type": "text",        "text": "Notes is not running on your device. Open it and try again."      }    ],    "isError": true  }}

Call outcomes#

Each call in the dashboard's Calls tab carries one outcome. Only the counted outcomes use the allowance or credits.

OutcomeMeaningCounted
okThe app answered with a result.Yes
tool_errorThe app answered with error.Yes
timeoutNo answer within 180 seconds.Yes
cancelledThe host cancelled the call or went away after it reached the app.Yes
disconnectedThe app's socket closed during the call.No, taken off the count
offlineThe app was not connected.No
pausedThe link was paused.No
busy8 calls were already in flight on the link.No
rate_limitedOver the link's calls per minute.No
invalid_inputThe arguments did not match the tool's inputSchema.No
unknown_toolThe tool is not in the app's list.No
cap_reachedThe call would have passed the project's spend cap.No
quota_exceededThe allowance was used up and the balance could not pay.No
  • Raise a link's calls per minute in the project's settings when one person's use needs more.
  • A run of offline calls usually means the person closed the app, or a reconnect loop is failing; check the close codes your app receives.