Errors and rate limits
The error response shape, status codes, and how often you can call the API.
Error format
Every error, on the REST API and the MCP server alike, is a JSON body with a short, machine-readable error code and a human-readable message:
{
"error": "not_found",
"message": "Not found, or not visible to this caller."
}Check the error field in code, not the message text, which is meant for a person reading it and may change.
A validation failure (400) adds a fields object naming what was wrong with each input:
{
"error": "invalid_input",
"message": "Request failed validation.",
"fields": {
"weightGrams": "Expected number, received string"
}
}Status codes
| Status | error | When |
|---|---|---|
| 400 | invalid_input | A parameter or body field failed validation, or the request body wasn't valid JSON. |
| 401 | unauthorized | The request has no credential, or the credential is invalid or expired. |
| 403 | forbidden | The credential is valid but can't be used here, for example it carries neither falconry:read nor falconry:write. |
| 403 | insufficient_scope | The credential is read-only and the request is a write operation. |
| 404 | not_found | The endpoint doesn't exist, or the resource does but isn't visible to this caller. |
| 405 | method_not_allowed | The endpoint exists but not for this HTTP method. |
| 409 | quota_exceeded | The write conflicts with your account's current plan or quota. |
| 413 | payload_too_large | The request body is bigger than 64 KB. |
| 429 | rate_limited | Too many requests. See rate limits below. |
| 500 | internal_error | Something went wrong on FalconryScale's side. |
| 503 | auth_unavailable | Your credential couldn't be checked right now, not that it's invalid. Carries a Retry-After header, in seconds. Wait that long and try again; your credential itself needs no action. |
A write can also surface other invalid_* codes from backend validation (for example invalid_date_range or invalid_weight), always as a 400 with the same { error, message } shape.
Authentication challenges
A 401 or a scope-related 403 carries a WWW-Authenticate header pointing at this resource's OAuth metadata, so a compliant client can recover automatically:
WWW-Authenticate: Bearer error="invalid_token", resource_metadata="https://app.falconryscale.com/.well-known/oauth-protected-resource/mcp"error="invalid_token" appears when a credential was sent and rejected; it's omitted when no credential was sent at all. error="insufficient_scope" appears instead when the credential is valid but lacks the scope the request needs.
A 503 auth_unavailable carries no WWW-Authenticate header at all, it isn't a verdict on your credential, just a sign the check itself couldn't run. Retry after the Retry-After header's delay instead of treating it like a 401.
Rate limits
Three limits apply, in order, on every request to the REST API and the MCP server alike:
- Per IP, before your credential is even checked: 120 requests per minute, burst 60. This guards against a flood of bad credentials from one source and applies to every request, authenticated or not.
- Per credential: 60 requests per minute, burst 30. This is your real budget, each API key or OAuth connection gets its own bucket.
- Per account, across every credential you have: 60 requests per minute, burst 30. Creating more API keys doesn't multiply your total budget, they all draw from this same ceiling once you're signed in.
On the MCP server, each tools/call is charged against your per-account limit too, on top of the HTTP-request charge above, since a tool call is what actually costs a database round trip.
Going over any of these returns 429 with a Retry-After header, in seconds:
HTTP/1.1 429 Too Many Requests
Retry-After: 12
{ "error": "rate_limited", "message": "Too many requests." }Wait at least that long before retrying. If you're regularly hitting this, batch calls where you can, get_today_summary and get_latest_weights each cover what would otherwise take several separate requests.
Request size
A request body on the REST API or the MCP server is capped at 64 KB. A larger body is rejected with 413 payload_too_large before it's read, or as soon as it's read if your client doesn't send a Content-Length header.
Individual fields have their own, smaller limits, an id is at most 64 characters, notes are capped at 2000 characters, and a location description at 500, all well inside the overall body cap. A field over its limit comes back as the usual 400 invalid_input with a fields entry naming it.
get_weight_history defaults to, and caps at, 500 rows per call. list_health_records and list_medications each default to, and cap at, 200 rows per call. Pass limit to ask for fewer.
MCP batch requests
The MCP server doesn't accept JSON-RPC batch requests (a JSON array of calls in one HTTP request). A batch body is rejected outright with a JSON-RPC error, before any of it runs:
{ "jsonrpc": "2.0", "id": null, "error": { "code": -32600, "message": "Invalid Request: JSON-RPC batch requests are not supported." } }Send one call per request instead.