Authentication and limits
API keys, scopes, plan-based rate limits, X-RateLimit headers, the problem+json error format and the code table.
API keys
All /v1/* endpoints and /mcp require a key in the header:
Authorization: Bearer rv_live_...- The key is created by a company owner in Desk: Settings → API keys. Step by step: quick start.
- A key is bound to your company (tenant) and works only within it. Other companies' data is not reachable with it.
- A key is shown once. The database stores only the
sha256of the key and a prefix. API logs show only the prefixrv_live_XXXX, never the key itself. - A key can be limited by expiry and revoked at any moment: access stops immediately.
- Do not embed the key in browser code of a public site and do not commit it to a repository. Keep it in server environment variables.
Scopes (permissions)
A key has a set of permissions. An endpoint without the required permission answers 403 insufficient_scope.
| Scope | What it allows |
|---|---|
read:pricing | POST /v1/landed-cost, GET /v1/market-price, GET /v1/rules/{country}, GET /v1/fx; MCP: calculate_landed_cost, estimate_market_price, get_customs_rules |
read:listings | GET /v1/vin/{vin}, GET /v1/listings/search; MCP: decode_vin, search_listings |
mcp | access to POST /mcp. The tool's own permission is required in addition |
write:deals | reserved for future endpoints that create and update deals and contacts In development |
For an MCP client create a key with mcp, read:pricing and read:listings: tools the key has no permission for are simply not listed in tools/list.
Rate limits
The limit is counted per key per minute and depends on your company's plan:
| Plan | Requests per minute |
|---|---|
| trial | 30 |
| starter | 60 |
| pro | 300 |
| business | 1200 |
Every response carries these headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | requests in the window for your key's plan |
X-RateLimit-Remaining | how many are left |
X-RateLimit-Reset | unix time in seconds when the window frees up |
X-Request-Id | the request id, also request_id in the error body. Quote it when writing to support |
When the limit is exhausted the API answers 429 rate_limited with a Retry-After header (seconds). Wait that long and retry. The SDK can do this on its own.
The order of checks: 401, then 429, then 403. If the Redis that holds the counter is unavailable, the API answers 503 (fail-closed) instead of letting requests through uncounted.
Monthly quotas
Separate monthly API plan quotas (Free 100 / Dev 10,000 / Business 100,000 calls) are for now only counted, not enforced. Reconciling them with the billing plans needs an owner decision Waiting on the owner. API access by ImportOS plan: see the table in For brokers.
Error format: problem+json
All errors have the type application/problem+json (RFC 9457):
{
"type": "https://importos.app/problems/insufficient_scope",
"title": "Forbidden",
"status": 403,
"detail": "The API key does not have the required scope: read:pricing",
"instance": "/v1/landed-cost",
"code": "insufficient_scope",
"request_id": "..."
}The type field is the address <API_PROBLEM_BASE_URL>/<code>; on the server it is recommended to set API_PROBLEM_BASE_URL=https://docs.<domain>/problems, so that address leads to this code table.
Rely on code: it is stable, unlike detail. Validation errors add errors[] with a path and message per field, and a 429 also adds retry_after.
code | HTTP | When |
|---|---|---|
validation_error | 400 | Invalid parameters or body (errors[] shows which) |
unauthorized | 401 | No key, invalid, revoked or expired |
insufficient_scope | 403 | The key lacks the required scope |
origin_not_allowed | 403 | A browser Origin outside the allowlist (/mcp protection) |
not_found | 404 | No such resource |
unsupported_country | 404 | No rules for this country |
method_not_allowed | 405 | Method not supported (GET and DELETE on /mcp) |
unprocessable_input | 422 | The request is valid but cannot be calculated (for example no rules for that date) |
rate_limited | 429 | Limit exceeded, see Retry-After |
internal_error | 500 | Internal error |
upstream_unavailable | 502 | An external provider (FX, VIN decoder) is unavailable |
auth_unavailable | 503 | The key could not be verified |
rate_limiter_unavailable | 503 | The rate-limit counter is unavailable |
Retry 5xx and 429 with exponential backoff. There is no point in retrying 4xx errors (except 429) unchanged.
CORS and the browser
CORS is allowed only for addresses in API_CORS_ORIGINS (an empty list disables CORS). For /mcp a browser Origin outside the list gets 403: this protects against DNS rebinding. Call the API from your server, not from the user's browser, so the key never reaches the client.