# Errors

Source: https://help.ruuster.com/api/errors

> Every error code with its HTTP status and when it happens, with example error responses.

| Status | Code | When |
|---|---|---|
| 400 | `invalid_parameter` | A parameter is missing, unknown or out of range; `message` names it |
| 400 | `invalid_range` | `to` before `from`, or the range is too long for the granularity |
| 400 | `invalid_cursor` | The cursor is malformed or from another brokerage |
| 400 | `invalid_mls_number` | The MLS number can't be read |
| 400 | `invalid_address` | The address can't be read: send the street with a city or zip |
| 400 | `mls_not_in_scope` | `mlsId` isn't one of your brokerage's MLSs, or a public record is for a home outside the states your MLSs cover |
| 400 | `url_not_allowed` | Webhook URL isn't public https or contains credentials |
| 400 | `crm_not_connected` | A CRM call without the `x-crm-vendor` and `x-crm-key` headers. See [Connect your CRM](https://help.ruuster.com/api/crm) |
| 400 | `crm_not_supported` | Your CRM can't answer this question (for example, Sierra keeps no calls); `message` says why |
| 401 | `missing_credentials`, `invalid_credentials`, `email_required` | See Authentication |
| 403 | `brokerage_not_enabled`, `account_not_eligible`, `access_revoked` | See Authentication |
| 403 | `role_not_permitted` | The key belongs to an agent; only owners and admins can use the API |
| 403 | `browser_not_supported` | The request came from a browser |
| 403 | `crm_key_rejected` | Your CRM refused the CRM key (rotated, revoked or mistyped) |
| 403 | `crm_write_not_allowed` | A CRM change without `x-crm-write: allow`, confirmed by someone other than the person who previewed it, or with a key for a different CRM account |
| 409 | `crm_change_not_open` | The change expired (15 minutes after its preview), finished, or was cancelled |
| 409 | `crm_change_too_soon` | A change was confirmed within 8 seconds of its preview. Show the preview and ask before confirming |
| 404 | `not_found` | Unknown id, not your brokerage's, or a path that isn't part of the API |
| 405 | `method_not_allowed` | Wrong method; see the `Allow` header |
| 409 | `subscription_limit_reached`, `subscription_blocked` | Webhook limits and blocks |
| 413 | `payload_too_large` | Request body over 64 KB |
| 429 | `rate_limited`, `daily_quota_exceeded` | See [limits](https://help.ruuster.com/api/limits); wait `Retry-After` seconds |
| 429 | `crm_rate_limited` | Your CRM is rate-limiting the CRM key; wait `Retry-After` seconds |
| 500 | `internal_error` | Ruuster's fault; quote `requestId` |
| 503 | `source_unavailable` | Ruuster couldn't retrieve the records in time. Retry with backoff, starting at 5 seconds |
| 503 | `directory_unavailable` | Ruuster couldn't check your key in time; retry |
| 503 | `crm_unavailable` | Your CRM didn't answer in time or returned errors; retry after `Retry-After` seconds |
| 503 | `api_paused` | Ruuster paused the API for maintenance; see `/status` |

## Example error responses

Every error uses the same shape and carries `X-Request-Id`.

```json
{ "ok": false, "error": { "code": "missing_credentials", "message": "Send your API key in an x-api-key header, with your Ruuster login email in x-email." }, "requestId": "req_01J9ZC1T7W3B9N5Q2X8D4K6M0P" }
```

| Status | Example `error` | Trigger |
|---|---|---|
| 400 | `{ "code": "invalid_range", "message": "Daily granularity allows at most 93 days; you asked for 120." }` | `?from=2026-06-03&to=2026-10-01&granularity=day` |
| 400 | `{ "code": "invalid_parameter", "message": "limit must be between 1 and 200 for lead rows." }` | `/leads?limit=1000` |
| 400 | `{ "code": "invalid_parameter", "message": "Send exactly one of email, phone, crmExternalId." }` | `/leads/lookup` with none |
| 400 | `{ "code": "url_not_allowed", "message": "Webhook URLs must be public https:// addresses." }` | `POST /webhooks` with `http://10.0.0.5/hook` |
| 401 | `{ "code": "invalid_credentials", "message": "The API key and email do not match." }` | wrong key or unknown email |
| 403 | `{ "code": "brokerage_not_enabled", "message": "API access is not enabled for your brokerage. Ask your Ruuster CSM." }` | brokerage not approved |
| 403 | `{ "code": "access_revoked", "message": "This API key has been blocked. Contact support@ruuster.com." }` | key blocked by Ruuster |
| 403 | `{ "code": "role_not_permitted", "message": "Only brokerage owners and admins can use the Ruuster API." }` | a key belonging to an agent |
| 403 | `{ "code": "browser_not_supported", "message": "Call this API from a server, not a browser." }` | request carries `Origin` |
| 404 | `{ "code": "not_found", "message": "No such resource." }` | unknown id, or not yours, or a path not in the API |
| 405 | `{ "code": "method_not_allowed", "message": "Use GET." }` + `Allow: GET` | `POST /summary` |
| 409 | `{ "code": "subscription_limit_reached", "message": "Your brokerage already has 10 webhook subscriptions." }` | 11th subscription |
| 429 | `{ "code": "rate_limited", "message": "120 requests per minute exceeded." }` + `Retry-After: 23` | too many calls |
| 429 | `{ "code": "daily_quota_exceeded", "message": "Daily lead-row quota of 50,000 reached; resets at 00:00 America/Chicago." }` + `Retry-After: 33120` | roster scraping |
| 500 | `{ "code": "internal_error", "message": "Something went wrong on our side." }` | bug; quote `requestId` |
| 503 | `{ "code": "source_unavailable", "message": "Ruuster couldn't retrieve the records in time. Retry in a few seconds." }` + `Retry-After: 5` | a lookup call took too long |
| 503 | `{ "code": "api_paused", "message": "The Ruuster API is temporarily paused for maintenance." }` | maintenance pause |

Rate-limited response, in full:

```
HTTP/2 429
content-type: application/json; charset=utf-8
retry-after: 23
x-request-id: req_01J9ZD6P0H2F8R4V1N7C3Y5K9B
x-ratelimit-limit: 120
x-ratelimit-remaining: 0
x-ratelimit-reset: 1790863283

{ "ok": false, "error": { "code": "rate_limited", "message": "120 requests per minute exceeded." }, "requestId": "req_01J9ZD6P0H2F8R4V1N7C3Y5K9B" }
```
