Getting started
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 |
| 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; 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.
{ "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" }