Skip to content
Concepts / Rate & concurrency limitsView Markdown ↗
Beta

AutoUncle Data Services is in beta. The API, MCP tools and agent skill may change often, including breaking changes, until version 1.0. Read the changelog before you upgrade an integration.

Rate & concurrency limits

Two kinds of limits keep the platform fast and fair: a rate limit on how many requests your workspace sends per minute, and a concurrency limit on how many valuations, selling-time forecasts and normalizations run at the same time. This page explains both, how to read them and how to back off.

Rate limits

Each workspace has three per-minute buckets, each with its own counter:

Bucket Default Counts
Valuations 60 requests per minute POST /v1/valuations and POST /v1/selling-times (including replays), GET /v1/valuations/{id}, GET /v1/account, and the MCP valuate_car, forecast_selling_time, get_valuation and get_account tools
Vehicles 60 requests per minute POST /v1/vehicles/normalize, /decode, /hsn-tsn and /lookup, and the MCP normalize_vehicle, decode_vin, decode_hsn_tsn and lookup_plate tools
Catalogue 120 requests per minute Every GET /v1/catalogue/... endpoint, the MCP search_vehicles tool and the console's catalogue browser

The valuations and vehicles buckets share one setting, rate_limit_per_minute, but are counted separately: 60 valuation requests and 60 vehicle requests can run in the same minute. A selling-time forecast is a valuation with a forecast added, so POST /v1/selling-times spends the valuations bucket and needs the same concurrency slots as a valuation.

Test-key requests that identify a vehicle (valuations, selling-time forecasts, normalization and VIN, HSN/TSN and plate lookups) also share a fair-use allowance per workspace: 10 per minute, 100 per hour and 300 per day. When a window is used up they are refused with 429 test_limit_reached and retry_after, the seconds until that window resets. See testing.

The limits apply to the whole workspace, across all its keys, REST and MCP. GET /v1/account returns rate_limit_per_minute, the limit of the valuations bucket and of the vehicles bucket. AutoUncle can raise the limits for your workspace: contact sales.

Windows are fixed calendar minutes: a counter starts at zero at the start of each minute. Every rate-limited response carries headers:

Header Meaning
RateLimit-Limit Requests allowed in the current minute for this bucket.
RateLimit-Remaining Requests left in the current minute.
RateLimit-Reset Seconds until the next minute starts.

When a bucket is empty, the request is rejected with 429 rate_limited before anything is recorded or charged. The Problem has retry_after, the seconds until the window resets, and REST also sends it as the Retry-After header. Wait that long, then send the same request with the same idempotency key.

{
  "type": "https://data-services.autouncle.com/docs/errors#rate_limited",
  "title": "Rate limited",
  "status": 429,
  "code": "rate_limited",
  "detail": "The request rate limit was reached. Retry after the current minute.",
  "errors": [],
  "fix": ["Wait for the rate limit to reset, then retry the same request with its original idempotency key."],
  "request_id": "3f0f8c3e-6a1e-4d39-9f53-1f7f0b8d2c11",
  "docs_url": "https://data-services.autouncle.com/docs/errors",
  "billable": false,
  "retryable": true,
  "retry_after": 37
}

Concurrency limits

Valuations, selling-time forecasts and normalizations also need a free slot while they run, from one shared pool: a forecast and a valuation compete for the same slots. VIN, HSN/TSN and plate lookups do not need one. Live requests need a slot at three levels at once:

Scope Default When it is full
Your workspace (live keys) 4 valuations at once 429 concurrency_limited
The market (for example all DK traffic) 16 at once 503 engine_busy
The whole platform 128 at once 503 engine_busy

Test-key valuations never reach the live scorer. They use a separate limit of 4 at once per workspace, so your tests behave like production but never block live traffic. AutoUncle tunes the market and platform limits to the scorer capacity, so they may differ from these defaults.

When no slot is free, the request waits up to about two seconds for one. If none frees up, it is rejected before anything is reserved, charged or recorded. Both rejections are retryable and carry retry_after (2 seconds by default) in the body and the Retry-After header on REST. Retry with the same idempotency key and body. Finished replays do not need a slot.

concurrency_limited means your own integration is sending too many valuations in parallel: lower your concurrency. engine_busy means the market or the platform is busy: back off and retry.

When limiting is unavailable

The limits are enforced with a shared store. If it is unavailable, requests fail closed with 503 rate_limit_unavailable rather than running unlimited. It is retryable and was not started, so retry the same request with the same key after a short delay.

Size limits

Limit Value
Request body 1 MB, including inline images. Larger requests return 413 payload_too_large.
description 20,000 characters
images 1 to 8 per request; remote images up to 8 MB each
vehicle.equipment 150 keys
vehicle.flags 30 entries
Idempotency-Key 200 characters

Staying within the limits

  • Use a bounded worker pool: at most 4 concurrent live valuations per workspace by default.
  • Pace with the headers: when RateLimit-Remaining reaches 0, sleep for RateLimit-Reset seconds.
  • Always honor retry_after, add a little random jitter, and cap the number of retries.
  • Cache catalogue lists rather than fetching them for every valuation; see catalogue caching.
  • Keep batch jobs and interactive traffic apart, or let batches yield when interactive traffic is high.

Batch valuations shows a complete worker loop.

Over MCP

MCP tool calls share the same buckets and slots as REST. MCP has no response headers, so the delay is only in the tool result's retry_after field, inside a result with isError: true. get_account and get_valuation count against the valuation bucket exactly like GET /v1/account and GET /v1/valuations/{id}, and return the same rate_limited Problem with retry_after when it is empty.