Core concepts
Errors & the failed-calls-free rule
A locked error-type catalog mapped 1:1 to HTTP status, a complete machine-stable sub-code list, and one guarantee: failed, empty, blocked and not-found calls are never billed.
Failed / bad-data calls are NEVER billed
On every error response,
error.creditsCharged is 0. In a fan-out, every failed platform contributes 0. The only credit-bearing outcomes are a successful synchronous result and a successfully completed async job. This is a load-bearing product guarantee — and why our reliability claim is true at the billing layer.The error object
Branch on type (the error class, mapped to HTTP status), then on code (a stable, finer-grained reason). Every response also carries a requestId, retryable flag, and a docUrl that links to the matching page below.
Error types
| HTTP | type | When it fires | Retryable |
|---|---|---|---|
| 400 | invalid_request | Malformed, missing or contradictory input you control. | No — fix the request |
| 401 | authentication_error | Missing / malformed Authorization, unknown or revoked key. | No — fix credentials |
| 402 | insufficient_credits | The plan / balance lacks the credits this call would cost. | No — until topped up |
| 403 | permission_denied | Authenticated but not allowed: a missing scope, an unverified email, or a disabled account. | No — change plan/scope |
| 404 | not_found | The addressed resource does not exist (job, listing, route). | No |
| 429 | rate_limited | The per-plan token-bucket rate limit was exceeded. | Yes — after Retry-After |
| 500 | internal_error | An unexpected ScoutingAPI-side fault. | Maybe — retry with backoff |
| 503 | upstream_unavailable | Every upstream actor for the request failed or was blocked. | Yes — with backoff |
| 504 | upstream_timeout | Synchronous upstream work exceeded the request deadline. | Yes — with backoff |
Sub-code catalog
The complete error.code set. New codes may be added over time (additive) — branch on them for precise handling but tolerate unknown ones. Each links to its own page (the value of error.docUrl).
| code | type | Trigger |
|---|---|---|
| missing_parameter | invalid_request | A required parameter is absent (e.g. checkOut given checkIn). |
| invalid_parameter | invalid_request | A parameter is present but malformed (bad type, format or enum value). |
| invalid_date_range | invalid_request | checkOut / endDate is not strictly after checkIn / startDate. |
| date_in_past | invalid_request | A supplied date (checkIn / startDate) is in the past (evaluated in UTC). |
| child_ages_mismatch | invalid_request | childAges[] length does not equal children. |
| stay_too_long | invalid_request | The booking window exceeds the per-endpoint maximum stay. |
| window_too_long | invalid_request | The query date window exceeds the per-endpoint cap (availability > 365 days). |
| invalid_listing_url | invalid_request | The supplied url is not a recognisable listing URL for the platform. |
| invalid_property_type | invalid_request | A propertyType[] value is not in the enum. |
| invalid_amenity | invalid_request | An amenities[] token is not in the canonical taxonomy. |
| invalid_price_range | invalid_request | priceMin / priceMax is invalid (negative, or priceMax < priceMin). |
| invalid_language | invalid_request | A language filter value is not a valid ISO-639-1 code. |
| invalid_sort | invalid_request | A sort value is not in the endpoint's allowed enum. |
| invalid_cursor | invalid_request | The pagination cursor is malformed or not base64-decodable. |
| limit_out_of_range | invalid_request | limit is < 1 or > 100. |
| mutually_exclusive_params | invalid_request | Conflicting or ambiguous identifiers were supplied (e.g. both listingId and url, or none). |
| platform_not_enabled | invalid_request | A requested platform is not enabled for the request (fast-follow, or not on the plan). |
| no_enabled_platform | invalid_request | After resolving platforms[], no platform remains to query. |
| missing_api_key | authentication_error | No Authorization header. |
| invalid_api_key | authentication_error | The Authorization header is present but the key is unknown or malformed. |
| revoked_api_key | authentication_error | The key was revoked or soft-deleted. |
| scope_insufficient | permission_denied | The key / token lacks a scope required for the operation. |
| email_unverified | permission_denied | The API key is valid, but the account email has not been verified yet — so LIVE credit (calls made with a scout_live_ key) is locked until you confirm your email (§7.5.1). Your scout_test_ sandbox key is unaffected and keeps returning deterministic fixtures at zero cost. |
| listing_not_found | not_found | The addressed listing / property does not exist upstream. |
| job_not_found | not_found | Unknown or expired jobId. |
| credit_balance_too_low | insufficient_credits | The balance is below the call's cost. |
| rate_limit_exceeded | rate_limited | The token bucket is exhausted (per-key for REST, per-user for MCP). |
| all_actors_failed | upstream_unavailable | Every primary + fallback actor for the request failed. |
| actor_blocked | upstream_unavailable | Upstream anti-bot / block detected with no usable rows. |
| actor_timeout | upstream_timeout | Synchronous upstream actor work timed out without producing data. |
| internal_error | internal_error | An unhandled ScoutingAPI-side fault. |