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.
Market & demand
Understand the market for a car type in one country: how many are on sale, what they cost, how fast listings leave the market, how long that takes, whether the type moves faster or slower than the market as a whole, and how that has changed over the last 12 months. Field tables on this page are generated from the OpenAPI contract.
| Endpoint | MCP tool | Answers |
|---|---|---|
POST /v1/market |
get_market |
How big and how fast the market for a car type is |
POST /v1/selling-times |
forecast_selling_time |
How long one specific car takes to sell at each price level. See selling-time forecasts |
Use both together: POST /v1/market for the type (a 2019 Volvo XC60 diesel automatic), then POST /v1/selling-times for the one car you are pricing.
What the figures are
Every figure is an aggregate over the listings AutoUncle tracks in the requested market, never a single listing.
- Listings, not registrations. The counts describe the listings AutoUncle tracks, not every car registered or sold.
- Left the market is not always a sale. A listing that left the market was removed from sale, which is usually, but not always, a sale. Its price is the last asking price, not a transaction price.
- The same segment as the similar-cars link. The segment is the search behind a valuation's
similar_cars_url, andsegment.similar_cars_urlopens it on the market's own AutoUncle site. It is make and model plus whatever you send that narrows it. A model year gives that year and one either side, kept within the model generation; in Germany and Italy it gives the exact year. Mileage narrows the segment only in Germany and Italy. Fuel, gearbox and body narrow it too. - Small numbers are withheld. A figure built from fewer than 10 listings is left out, and
suppressedsays which block or month. Prices are rounded to three significant figures and mileage to 1,000 km or mi (100 Scandinavian mil), so no listing can be picked out. - Freshness. On-sale and left-the-market figures can be up to three hours old, because they come from a shared cache.
as_ofsays when the request read them.
| Block | What it says | Window |
|---|---|---|
supply |
Listings on sale now: the count, asking price and mileage (5th percentile, median, 95th percentile), model years, the share in each price rating, the share whose price has been lowered and the most listed equipment variants | Now |
flow |
Listings that left the market per day, and listings that went on sale in the window and are still on sale, per day | Last 30 days |
turnover |
Median and mean days from going on sale to leaving the market | Last 60 days |
demand |
Days of supply, turnovers per year, and the level against the whole market | Now and the last 30 days |
trend |
Per month: listings that left the market, median days to leave and median last asking price | Last 12 complete months |
Demand measures
days_of_supply= listings on sale ÷ listings leaving the market per day. It is how long today's stock would last at the current pace.turnovers_per_year= 12 × listings that left in 30 days ÷ listings on sale.relative_to_market= the segment'sdays_of_supply÷ the same measure for the market's whole used-car stock. Below 1 the type moves faster than the market.levelishighwhenrelative_to_marketis below 0.8,lowabove 1.25, andaveragein between. Comparing with the market itself keeps the level meaningful across countries, and it cancels most of the effect of listings removed for reasons other than a sale.
flow.new_listings_still_on_sale_per_day counts only listings that are still on sale. A listing that went on sale and left within the window is not in it. So it is not total inflow, and subtracting left_market_per_day from it does not give a net change.
Status and cost
status |
Means | Live cost |
|---|---|---|
measured |
Figures were published | One credit |
partial |
A data store could not answer, so some blocks are null; warnings says which (market_supply_unavailable, market_flow_unavailable, market_trend_unavailable, market_baseline_unavailable) |
Free; request it again |
insufficient_data |
Too few listings match, so every figure was withheld | Free |
Test keys return deterministic example figures (synthetic: true) for free. Nothing is stored: a replay with the same Idempotency-Key reads the market again without spending a credit, and there is no read-back endpoint. See credits.
POST /v1/market
Market and demand figures for a car type.
Describes the market for a car type in one country: how many are on sale and at what prices and mileages, the price-rating mix, how fast listings arrive and leave the market, how long they take to leave, days of supply and a demand level relative to the whole market, and a 12-month trend of listings leaving the market. Send the same vehicle input as a valuation; at least a make and model must be recognised, and model year, mileage, fuel, gearbox and body narrow the segment exactly as the valuation's similar_cars_url does. Every figure is an aggregate over the listings AutoUncle tracks in that market (not registrations or confirmed sales); left the market means removed from sale. A figure built from fewer than 10 listings is withheld and listed in suppressed. One credit for a measured live result; partial and insufficient_data results, refusals, failures, idempotent replays and test keys are free. Needs the valuations:write scope and shares the valuation rate and concurrency limits. Nothing is stored: a replay with the same Idempotency-Key reads the market again without spending a credit.
Nothing is stored: a replay with the same Idempotency-Key reads the market again without spending a credit, and there is no read-back endpoint. Counts against the valuation rate limit and needs a valuation concurrency slot, like POST /v1/valuations.
- Scope:
valuations:write - Cost: One credit for a
measuredlive result.partialandinsufficient_dataresults, test keys, refusals, failures and idempotent replays are free. - Rate limit: valuations bucket, default 60 requests per minute. See limits.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
Idempotency-Key |
header | string | No | Workspace- and mode-scoped retry key. A replay of a finished request spends no credit. 1 to 200 characters. |
Request body
application/json, a MarketRequest: A car type in one market: the same vehicle input as a valuation (a VehicleInput, a description or photos), of which at least a make and model must be recognised. Mileage and model year are optional and only narrow the segment as the similar-cars link does. Pricing does not apply. Unknown fields are rejected.
| Field | Type | Description |
|---|---|---|
country required |
string | Market as an ISO 3166-1 alpha-2 code, such as DK, DE or GB. Every figure comes from that market's own listings only. 2 to 10 characters. |
vehicle |
VehicleInput | The vehicle, as a VehicleInput: an AVS vehicle document (any vehicle an endpoint returned, posted back unchanged) or the vehicle's attributes as a listing gives them. |
description |
string | Unstructured listing text describing the vehicle. Existing parsers and deployment-configured optional AI can propose attributes; the response identifies assumptions and unresolved inputs. 1 to 20000 characters. |
images |
array | Up to eight vehicle photos, each supplied by HTTPS URL or inline base64 data, to identify the car type. AI image resolution is deployment-configured and never runs for test keys. Photos cost nothing extra on a market request. At most 8 items. |
images[].url |
string (uri) | Public HTTPS image URL on port 443, without credentials or fragments. Private network destinations and redirects are rejected. Remote images must be supported image types and at most 8 MB each. At most 2048 characters. |
images[].data |
string | Strict base64-encoded image bytes, without a data-URL prefix. Inline data must also fit the total 1 MB API request limit. At most 1000000 characters. |
images[].media_type |
string | MIME type for inline image bytes: image/jpeg, image/png, image/webp or image/gif. The actual image signature is checked. At most 100 characters. |
options |
object | How the vehicle is resolved. |
options.resolution |
string | auto uses deterministic resolution and optional configured AI; no_ai and strict use deterministic resolution only. One of auto, no_ai, strict. |
Structured example:
{
"country": "SE",
"vehicle": {
"make": "Volvo",
"model": "XC60",
"model_year": 2019,
"fuel": "diesel",
"transmission": "automatic"
}
}
Text example:
{
"country": "DE",
"description": "VW Golf 1.5 TSI 2020"
}
Response
application/json, a MarketResponse: Market & demand figures for a car type in one market: aggregates over the listings AutoUncle tracks there, never a listing. Errors return a Problem as application/problem+json.
| Field | Type | Description |
|---|---|---|
request_id |
string | The request's ID, shown in the console request log. Pattern ^mkt_[A-Za-z0-9]+$. |
object |
string | Always market. One of market. |
status |
string | measured: figures were published; one credit on a live key. partial: a data store could not answer, so some figures are missing (see warnings); not charged. insufficient_data: every figure was withheld because too few listings match; not charged. One of measured, partial, insufficient_data. |
country |
string | Market as an ISO 3166-1 alpha-2 code, normalized from your request (for example SE). Pattern ^[A-Z]{2}$. |
mode |
string | test for a test key (synthetic figures) or live for a live key. One of live, test. |
synthetic |
boolean | True for test keys: deterministic example figures, no market data. |
vehicle |
Vehicle | The identified vehicle. An AVS vehicle document; left out when the vehicle could not be identified to a brand and a model, with the reason in identification. |
identification |
VehicleIdentification | How the vehicle was identified: status, depth, candidates and the provenance of every value in vehicle. |
segment |
object | The listings the figures describe. |
segment.criteria required |
object | What a listing must match to be in the segment, in the public vocabulary. Null means not restricted. |
segment.criteria.make required |
string | Make every listing in the segment has. |
segment.criteria.model required |
string | Model every listing in the segment has. |
segment.criteria.model_year required |
object or null | Model years the segment covers. Null when not restricted. |
segment.criteria.model_year.from required |
integer or null | Earliest model year. |
segment.criteria.model_year.to required |
integer or null | Latest model year. |
segment.criteria.mileage required |
object or null | Mileage band the segment covers (Germany and Italy only, when you send a mileage). Null when not restricted. |
segment.criteria.mileage.from required |
integer or null | Lowest mileage. |
segment.criteria.mileage.to required |
integer or null | Highest mileage. |
segment.criteria.mileage.unit required |
string | km, mi or mil (Scandinavian mil). One of km, mi, mil. |
segment.criteria.fuel required |
string or null | Fuel every listing has. Null when not restricted. |
segment.criteria.transmission required |
string or null | automatic or manual when the gearbox is restricted, otherwise null. One of automatic, manual. |
segment.criteria.body required |
string or null | Body type every listing has. Null when not restricted. |
segment.criteria.engine_size_l required |
number or null | Engine size in litres every listing has. Null when not restricted. |
segment.similar_cars_url required |
string or null (uri) | The same segment as a search on the market's own AutoUncle site. |
as_of |
string (date-time) | When the figures were read. On-sale and left-the-market figures can be up to three hours old (shared cache). |
supply |
object or null | Listings on sale now. Null when those figures could not be read (market_supply_unavailable warning). |
supply.on_sale required |
integer or null | Listings on sale now in the segment. Null when fewer than 10, with on_sale_band set. Minimum 0. |
supply.on_sale_band required |
string or null | fewer_than_10 when the count is withheld; every other supply figure is then left out. One of fewer_than_10. |
supply.price |
object | Asking prices of the listings on sale. |
supply.price.p5 required |
integer or null | 5th percentile asking price, rounded to three significant figures. Minimum 0. |
supply.price.median required |
integer or null | Median asking price, rounded to three significant figures. Minimum 0. |
supply.price.p95 required |
integer or null | 95th percentile asking price, rounded to three significant figures. Minimum 0. |
supply.price.currency required |
string | ISO 4217 currency of the market. Pattern ^[A-Z]{3}$. |
supply.mileage |
object | Mileage of the listings on sale, in the market's unit. |
supply.mileage.p5 required |
integer or null | 5th percentile mileage, rounded to 1,000 km or mi (100 Scandinavian mil). Minimum 0. |
supply.mileage.median required |
integer or null | Median mileage, rounded the same way. Minimum 0. |
supply.mileage.p95 required |
integer or null | 95th percentile mileage, rounded the same way. Minimum 0. |
supply.mileage.unit required |
string | km, mi or mil (Scandinavian mil): the market's unit. One of km, mi, mil. |
supply.model_years |
object or null | Model years of the listings on sale. Null when the store returned none. |
supply.model_years.most_common required |
integer or null | The most listed model year. Minimum 1900. |
supply.model_years.from required |
integer or null | Earliest model year of all but the outer 2% of listings. Minimum 1900. |
supply.model_years.to required |
integer or null | Latest model year of all but the outer 2% of listings. Minimum 1900. |
supply.price_ratings |
object | Share of listings on sale in each AutoUncle price rating (5 is super price, 1 is expensive), to two decimals. |
supply.price_ratings.5 required |
number or null | Share of listings on sale with price rating 5. 0 to 1. |
supply.price_ratings.4 required |
number or null | Share of listings on sale with price rating 4. 0 to 1. |
supply.price_ratings.3 required |
number or null | Share of listings on sale with price rating 3. 0 to 1. |
supply.price_ratings.2 required |
number or null | Share of listings on sale with price rating 2. 0 to 1. |
supply.price_ratings.1 required |
number or null | Share of listings on sale with price rating 1. 0 to 1. |
supply.price_ratings.unrated required |
number or null | Share of listings on sale without a price rating. 0 to 1. |
supply.share_with_price_drop |
number or null | Share of listings on sale whose asking price has been lowered. 0 to 1. |
supply.top_equipment_variants |
array | The three most listed equipment-variant names, most listed first. At most 3 items. |
flow |
object or null | Pace over the last 30 days. Null when withheld (fewer than 10 listings left the market in the window) or unavailable. |
flow.window_days required |
integer | Days the per-day rates are averaged over. Always 30. |
flow.left_market_per_day required |
number or null | Listings that left the market per day over the window (on sale at least one day). Left the market means removed from sale, usually but not always a sale. Minimum 0. |
flow.new_listings_still_on_sale_per_day required |
number or null | Listings that went on sale within the window and are still on sale, per day. Listings that came and went within the window are not counted, so this is not total inflow. Minimum 0. |
turnover |
object or null | How long listings of this type take to leave the market. Null when withheld or unavailable. |
turnover.window_days required |
integer | Days back the listings that left the market are taken from. Always 60. |
turnover.median_days_to_leave required |
integer or null | Median days from going on sale to leaving the market, for listings that left in the window. Minimum 0. |
turnover.mean_days_to_leave required |
integer or null | Mean of the same. Minimum 0. |
demand |
object or null | Derived demand measures. Null when on_sale or flow is withheld or unavailable. |
demand.days_of_supply required |
integer or null | Listings on sale ÷ listings leaving the market per day: how long today's stock lasts at the current pace. Minimum 0. |
demand.turnovers_per_year required |
number or null | 12 × listings that left in 30 days ÷ listings on sale. Minimum 0. |
demand.relative_to_market required |
number or null | days_of_supply ÷ the same measure for the market's whole used-car stock. Below 1 the type moves faster than the market. Minimum 0. |
demand.level required |
string or null | high below 0.8 relative_to_market, low above 1.25, otherwise average. Null when the whole market's figures could not be read. One of high, average, low. |
trend |
object or null | The last 12 complete months of listings that left the market. Null when it could not be read (market_trend_unavailable warning). |
trend.months required |
integer | Complete calendar months in series: 12, or fewer where the store holds less history. 0 to 12. |
trend.currency required |
string | ISO 4217 currency of median_last_asking_price. Pattern ^[A-Z]{3}$. |
trend.series required |
array | Oldest month first. At most 12 items. |
trend.series[].month required |
string | Calendar month as YYYY-MM. Pattern ^[0-9]{4}-[0-9]{2}$. |
trend.series[].left_market required |
integer or null | Listings that left the market that month (on sale at least one day). Minimum 0. |
trend.series[].median_days_to_leave required |
integer or null | Median days from going on sale to leaving the market. Minimum 0. |
trend.series[].median_last_asking_price required |
integer or null | Median last asking price before leaving the market, rounded to three significant figures. Not a transaction price. Minimum 0. |
trend.series[].suppressed required |
boolean | True when fewer than 10 listings left the market that month; its figures are then null. |
trend.current_month required |
object | The month in progress, which is never in series. |
trend.current_month.month required |
string | The month in progress, as YYYY-MM. Pattern ^[0-9]{4}-[0-9]{2}$. |
trend.current_month.partial required |
boolean | Always true: the month in progress is never in series. |
suppressed |
array | Every block or trend month withheld because it would be built from fewer than 10 listings. |
suppressed[].section required |
string | supply, flow, turnover, demand or trend. One of supply, flow, turnover, demand, trend. |
suppressed[].month |
string | For trend, the month withheld, as YYYY-MM. Pattern ^[0-9]{4}-[0-9]{2}$. |
suppressed[].reason required |
string | Why it was withheld. Always fewer_than_10_listings. One of fewer_than_10_listings. |
warnings |
array | Problems with this request, for example market_flow_unavailable (status partial) or model_not_recognized. Match on code. |
warnings[].code required |
string | Stable machine-readable warning code. Pattern ^[a-z][a-z0-9_]*$. |
warnings[].message required |
string | Plain-language explanation. |
usage |
object | Billing for this call. |
usage.billable required |
boolean | true when this result captured a credit (status measured on a live key). |
usage.credits_used required |
integer | Credits this call consumed. Always 0 on an idempotent replay. 0 to 1. |
usage.original_credits_used required |
integer | Credits the original request consumed (0 or 1), also on a replay. 0 to 1. |
usage.charges required |
object | What original_credits_used paid for. Empty when nothing was charged. |
usage.charges.base |
integer | The market figures, 1 credit. Minimum 1. |
usage.credits_remaining required |
integer | Workspace credits left after this call. Minimum 0. |
usage.cache_hit required |
boolean | Reserved. Always false in this version. |
usage.idempotent_replay required |
boolean | true when the same Idempotency-Key and body were sent before; the market is read again for free. |
usage.mode required |
string | test or live. One of live, test. |
notes |
Note array | Standing limitations and notices, including market_figures_describe_listings. Always present. |
attributions |
Attribution array | Data sources that contributed to this result. Always present. |
Response headers
| Header | Description |
|---|---|
X-Request-Id |
The HTTP request ID, on every response. Use it to correlate one HTTP exchange in proxy and application logs; it echoes a valid X-Request-Id you send. It is not the valuation ID. |
Retry-After |
Seconds to wait before retrying. |
Errors
| Code | HTTP | Retryable | Meaning |
|---|---|---|---|
invalid_request |
400 | No | Send a request matching the documented schema. |
country_required |
400 | No | Include country, the valuation market, in the request. |
unsupported_country |
400 | No | Choose a supported ISO country code. |
unauthenticated |
401 | No | Supply an API key as a Bearer token. |
invalid_api_key |
401 | No | Supply a valid AutoUncle Data Services API key. |
key_revoked |
401 | No | This key has expired or been revoked. Create a new key. |
insufficient_credits |
402 | No | The organization has no credits available. Contact sales for a package. |
key_spend_limit_reached |
402 | No | This API key has reached its monthly credit limit. |
insufficient_scope |
403 | No | Use a key with the required scope. |
country_not_enabled |
403 | No | This country is not enabled for this organization or key. |
organization_pending |
403 | No | This workspace is waiting for AutoUncle to approve it. |
organization_suspended |
403 | No | This organization is inactive. Contact support. |
unknown_catalogue_id |
404 | No | The catalogue ID is unknown. Fetch current IDs from the catalogue API. |
idempotency_conflict |
409 | No | This idempotency key was used with different input. Choose a new key. |
request_in_progress |
409 | Yes | A request with this idempotency key is still running. Retry later with the same key. |
payload_too_large |
413 | No | The request exceeds the maximum supported size of 1 MB. |
vehicle_not_identified |
422 | No | Provide more vehicle attributes or select catalogue IDs. |
catalogue_id_conflict |
422 | No | The catalogue IDs do not describe the same vehicle hierarchy. |
vehicle_out_of_range |
422 | No | The vehicle is outside the supported valuation range. |
images_unreadable |
422 | No | Provide readable supported images. |
rate_limited |
429 | Yes | The request rate limit was reached. Retry after the current minute. |
test_limit_reached |
429 | Yes | This workspace has used its fair-use allowance of test-key requests for now. |
concurrency_limited |
429 | Yes | Too many valuations are running at the same time for this organization. |
rate_limit_unavailable |
503 | Yes | Request limiting is temporarily unavailable. Retry later. |
engine_busy |
503 | Yes | The valuation engine is at capacity. Retry after a short delay. |
request_timeout |
504 | Yes | The valuation request exhausted its processing time budget. |
resolution_timeout |
504 | Yes | Vehicle interpretation did not finish within its time budget. |
resolution_unavailable |
503 | Yes | Vehicle interpretation is temporarily unavailable. |
resolution_invalid_response |
502 | Yes | Vehicle interpretation returned an invalid response. |
image_timeout |
504 | Yes | Image retrieval did not finish within its time budget. |
image_unavailable |
503 | Yes | The image service is temporarily unavailable. |
image_invalid_response |
502 | Yes | The image service returned an invalid response. |
market_data_unavailable |
503 | Yes | Market figures are temporarily unavailable. |
internal_error |
500 | No | The valuation could not be completed. Contact support with the request ID. |
Examples
curl https://data-services.autouncle.com/v1/market \
-H "Authorization: Bearer $AUTOUNCLE_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: market-001' \
-d @- <<'JSON'
{
"country": "SE",
"vehicle": {
"make": "Volvo",
"model": "XC60",
"model_year": 2019,
"fuel": "diesel",
"transmission": "automatic"
}
}
JSON
require "json"
require "net/http"
uri = URI("https://data-services.autouncle.com/v1/market")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("AUTOUNCLE_API_KEY")}"
request["Content-Type"] = "application/json"
request["Idempotency-Key"] = "market-001"
request.body = JSON.generate({
"country" => "SE",
"vehicle" => {
"make" => "Volvo",
"model" => "XC60",
"model_year" => 2019,
"fuel" => "diesel",
"transmission" => "automatic"
}
})
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts JSON.pretty_generate(JSON.parse(response.body))
import json
import os
import requests
response = requests.post(
"https://data-services.autouncle.com/v1/market",
headers={
"Authorization": f"Bearer {os.environ['AUTOUNCLE_API_KEY']}",
"Idempotency-Key": "market-001",
},
json={
"country": "SE",
"vehicle": {
"make": "Volvo",
"model": "XC60",
"model_year": 2019,
"fuel": "diesel",
"transmission": "automatic"
}
},
timeout=30,
)
print(json.dumps(response.json(), indent=2))
const response = await fetch("https://data-services.autouncle.com/v1/market", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTOUNCLE_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "market-001",
},
body: JSON.stringify({
country: "SE",
vehicle: {
make: "Volvo",
model: "XC60",
model_year: 2019,
fuel: "diesel",
transmission: "automatic"
}
}),
});
console.log(JSON.stringify(await response.json(), null, 2));