For business
Data API documentation
Everything you need to call the GCC Car Deals Data API: authentication, the endpoints with their parameters and responses, errors and limits.
Quick start
Subscribe to get an API key, keep it in an environment variable and send it as a Bearer token. This request asks for the market value of a Toyota Camry today.
Base URL: https://gcccardeals.com/api/b2b/v1
curl -s "https://gcccardeals.com/api/b2b/v1/valuation?make=Toyota&model=Camry&year=2019&km=90000" \
-H "Authorization: Bearer $GCC_API_KEY"Authentication
Send your API key in the Authorization header as a Bearer token, or in the X-API-Key header. Never put it in a URL: URLs are logged. Keys are confidential and issued per market, so a key for one market cannot query another. Starter and Dataset include a key for the market you choose; Pro and Business include a key for each market. You create your keys in your Data API dashboard, and the full key is shown only once.
Valuation
Available on every planGET https://gcccardeals.com/api/b2b/v1/valuation
The market value of one car on a date: our estimate from comparable ads, each adjusted to the car's model year and mileage, with the typical error and range of such estimates and the ads behind it.
Parameters
| Parameter | Required | Description |
|---|---|---|
make | Yes | The make as you would write it, for example Toyota. Names and common spellings are recognised. |
model | Yes | The model, for example Camry. |
year | Yes | The model year, from 1950 to next year. |
trim | No | The trim as written on the car, for example GXR. It narrows the comparison and never changes the car. |
km | No | The odometer reading in kilometres. When given, the value follows the car's mileage. |
as_of | No | The date to value the car on, as YYYY-MM-DD in UTC. Defaults to today. It must fall between the start of the market's price history and today, and within your plan's history window. |
condition | No | used (the default) or new. |
window_days | No | How many days back to look for comparable cars, counted from the valuation date (from today for the price table), from 30 to 180. Default 90. |
comparables | No | How many comparable ads to return as evidence, from 0 to 25. Default 10. |
market | No | Optional. It must match the market of your key; a mismatch is an error. |
Example request
curl -s "https://gcccardeals.com/api/b2b/v1/valuation?make=Toyota&model=Camry&year=2019&km=90000" \
-H "Authorization: Bearer $GCC_API_KEY"Example response
Illustrative values, shown to explain the format. They are not current prices, and the ad is an example.
{
"version": 1,
"method": "adjusted-comparables-v2",
"market": "ae",
"currency": "AED",
"as_of": "2026-09-15",
"window_days": 90,
"history_since": "2026-06-12",
"subject": {
"make": "toyota",
"model": "camry",
"model_year": 2019,
"trim": null,
"trim_label": null,
"km": 90000,
"condition": "used"
},
"valuation": {
"value": 52000,
"low": 47500,
"high": 56000,
"confidence": "high",
"indicative": false,
"typical_error_pct": 8.1,
"range_low": 43900,
"range_high": 61000,
"basis": "model_year",
"premium_trim_group": null,
"mileage_matched": true
},
"counts": {
"candidates_total": 31,
"truncated": false,
"candidates": 31,
"distinct_cars": 27,
"outliers_dropped": 1,
"pool": 26,
"used": 26,
"same_trim": 0,
"same_year": 22
},
"year_counts": {
"2018": 4,
"2019": 22
},
"pool": {
"median_price": 53000,
"p25": 48000,
"p75": 57000,
"median_km": 96000,
"sources": 4
},
"comparables": [
{
"source": "Example marketplace",
"url": "https://www.example.com/listing/12345",
"model_year": 2019,
"trim": null,
"km": 88000,
"asking_price": 51500,
"price_date": "2026-09-10",
"adjusted_price": 51400,
"first_seen": "2026-08-30",
"last_seen": "2026-09-16",
"still_listed": true,
"listed_on_as_of": true,
"city": "riyadh"
}
],
"reason": null,
"quota": {
"monthly": 3000,
"used_month": 12,
"daily": null,
"used_today": null
},
"notes": [
"Prices are asking prices from the listings we observe, not transaction prices."
]
}Response fields
version- The response format version. Within a version, fields are added and never removed or renamed.
method- The name and version of the method that produced the answer.
market- The market the answer is for: qa, ae or sa.
currency- The currency of every price in the answer.
as_of- The date the answer is for, as a UTC date.
window_days- How many days back from as_of the comparable cars were taken from.
history_since- The first date this market has price history for.
subject- The car as we understood it: make, model, model_year, trim, trim_label, km and condition.
valuation.value- The market value: our estimate of the car's market asking price on as_of, made from comparable ads, each adjusted to the car's model year and mileage. Null when no comparable car is found; reason says why.
valuation.low- The lower end of the range in which the middle half of the comparable cars' adjusted prices fall.
valuation.high- The upper end of that range.
valuation.confidence- How much evidence stands behind the value: high, medium or low.
valuation.indicative- True when there is very little evidence behind the value, which makes it a rough guide; its confidence is low then. Otherwise false.
valuation.typical_error_pct- How close values like this come to individual cars' asking prices, measured on our own data: half of such cars ask within this many percent of the value. Null when it has not been measured.
valuation.range_low- The lower end of the range most cars like this ask within, measured on our own data. Null when typical_error_pct is null.
valuation.range_high- The upper end of that range.
valuation.basis- What the value mainly rests on: trim (cars stating your trim), trim_group (cars of your price-moving trim group), model_year (cars of your car's model year) or adjusted_years (too few cars of your car's model year, so cars of nearby model years adjusted to it).
valuation.premium_trim_group- The price-moving trim group the car was valued against, when its trim belongs to one.
valuation.mileage_matched- True when the value follows your car's mileage: you gave km and the comparable ads state theirs.
counts.candidates_total- All matching ads we observed in the window.
counts.truncated- True when candidates_total is larger than the number of ads read.
counts.candidates- The ads read for this valuation.
counts.distinct_cars- The number of separate cars behind those ads; a car advertised more than once counts once.
counts.outliers_dropped- Cars set aside because their price is unusual for this comparison.
counts.pool- The cars left in the comparison after that.
counts.used- The cars the value was taken from.
counts.same_trim- How many cars of your car's model year share the trim you gave.
counts.same_year- How many of the cars used are of your car's model year.
year_counts- How many comparable cars there are per model year, which shows which model years the value draws on.
pool.median_price- The median asking price across the whole comparison set.
pool.p25- The lower end of the middle half of that set's asking prices.
pool.p75- The upper end of the middle half of that set's asking prices.
pool.median_km- The median mileage of the set, in kilometres.
pool.sources- How many marketplaces the set comes from.
comparables[].source- The marketplace the ad was found on.
comparables[].url- A link to the original ad, or null in the few cases where we withhold it for privacy.
comparables[].model_year- The ad's model year.
comparables[].trim- The ad's trim, when it states one.
comparables[].km- The ad's odometer reading in kilometres, when it states one.
comparables[].asking_price- The ad's asking price on price_date, in the market's currency.
comparables[].price_date- The date of the asking price used.
comparables[].adjusted_price- The ad's asking price moved to your car's model year and mileage. It is what this ad contributes to the value.
comparables[].first_seen- The date we first saw the ad.
comparables[].last_seen- The date we last saw the ad.
comparables[].still_listed- Whether the ad is still listed today.
comparables[].listed_on_as_of- Whether the ad was listed on the as_of date itself.
comparables[].city- The city the ad gives, when it gives one.
reason- When no value could be given, a plain-language reason. Otherwise null.
quota- Your quota and how much of it you have used: monthly, used_month, daily and used_today.
notes- Plain-language notes on how to read the numbers.
Market prices
Available on every planGET https://gcccardeals.com/api/b2b/v1/market-prices
The typical asking price and its range for one make and model, by model year, trim family and mileage band, plus our estimate of the price for each group, including groups too small to price.
Parameters
| Parameter | Required | Description |
|---|---|---|
make | Yes | The make as you would write it, for example Toyota. Names and common spellings are recognised. |
model | Yes | The model, for example Camry. |
condition | No | used (the default) or new. |
window_days | No | How many days back to look for comparable cars, counted from the valuation date (from today for the price table), from 30 to 180. Default 90. |
market | No | Optional. It must match the market of your key; a mismatch is an error. |
Example request
curl -s "https://gcccardeals.com/api/b2b/v1/market-prices?make=Toyota&model=Camry" \
-H "Authorization: Bearer $GCC_API_KEY"Example response
Illustrative values, shown to explain the format. They are not current prices, and the ad is an example.
{
"version": 1,
"method": "market-prices-v1",
"market": "ae",
"currency": "AED",
"as_of": "2026-09-29",
"window_days": 90,
"history_since": "2026-06-12",
"make": "toyota",
"model": "camry",
"condition": "used",
"cells": [
{
"model_year": 2019,
"trim_family": "base",
"mileage_band": "100-150k",
"cars": 12,
"median_price": 52000,
"p25_price": 48000,
"p75_price": 56000,
"median_km": 128000,
"estimated_price": 52300,
"estimate_range_low": 44100,
"estimate_range_high": 61400,
"estimate_error_pct": 8.1,
"estimate_confidence": "high"
}
],
"quota": {
"monthly": 3000,
"used_month": 12,
"daily": null,
"used_today": null
},
"notes": [
"Prices are asking prices from the listings we observe, not transaction prices."
]
}Response fields
version- The response format version. Within a version, fields are added and never removed or renamed.
method- The name and version of the method that produced the answer.
market- The market the answer is for: qa, ae or sa.
currency- The currency of every price in the answer.
as_of- The date the answer is for, as a UTC date.
window_days- How many days back from as_of the comparable cars were taken from.
history_since- The first date this market has price history for.
make- The make as we list it.
model- The model as we list it.
condition- used or new.
cells- The price table for this make and model: one entry per model year, trim family and mileage band.
cells[].model_year- The model year.
cells[].trim_family- The trim family: the name of a price-moving trim, or base for the base trims.
cells[].mileage_band- The mileage band the cars fall in.
cells[].cars- The number of separate cars in the group.
cells[].median_price- The typical asking price of the group. Null for a group too small to price.
cells[].p25_price- The lower end of the middle half of asking prices, or null.
cells[].p75_price- The upper end of the middle half of asking prices, or null.
cells[].median_km- The median mileage of the group, in kilometres.
cells[].estimated_price- Our estimate of the asking price of the group. Unlike the typical price, it is also given for a group too small to price. Null when we have no estimate for it.
cells[].estimate_range_low- The lower end of the range most cars like this ask within, measured on our own data. Null when it has not been measured.
cells[].estimate_range_high- The upper end of that range.
cells[].estimate_error_pct- How close estimates like this come to individual cars' asking prices, measured on our own data: half of such cars ask within this many percent of the estimate. Null when it has not been measured.
cells[].estimate_confidence- How much evidence the estimate rests on: high, medium, low or indicative (very little evidence).
quota- Your quota and how much of it you have used: monthly, used_month, daily and used_today.
notes- Plain-language notes on how to read the numbers.
Full market table (CSV)
Pro, Business and DatasetGET https://gcccardeals.com/api/b2b/v1/market-prices.csv
The whole market's price table, with our estimate for each group, as one CSV file, refreshed every day. A download counts as one call.
Parameters
| Parameter | Required | Description |
|---|---|---|
market | No | Optional. It must match the market of your key; a mismatch is an error. |
Example request
curl -s "https://gcccardeals.com/api/b2b/v1/market-prices.csv" \
-H "Authorization: Bearer $GCC_API_KEY" \
-o market-prices.csvThe file is streamed. If the connection breaks before the end, discard it: the X-Total-Rows header tells you how many rows to expect.
Columns
make- The make as we list it.
model- The model as we list it.
model_year- The model year.
trim_family- The trim family: the name of a price-moving trim, or base for the base trims.
condition- used or new.
mileage_band- The mileage band the cars fall in.
cars- The number of separate cars in the group.
median_price- The typical asking price of the group. Null for a group too small to price.
p25_price- The lower end of the middle half of asking prices, or null.
p75_price- The upper end of the middle half of asking prices, or null.
median_km- The median mileage of the group, in kilometres.
currency- The currency of every price in the answer.
computed_on- The date the table or file was computed, as a UTC date.
estimated_price- Our estimate of the asking price of the group. Unlike the typical price, it is also given for a group too small to price. Null when we have no estimate for it.
estimate_range_low- The lower end of the range most cars like this ask within, measured on our own data. Null when it has not been measured.
estimate_range_high- The upper end of that range.
estimate_error_pct- How close estimates like this come to individual cars' asking prices, measured on our own data: half of such cars ask within this many percent of the estimate. Null when it has not been measured.
estimate_confidence- How much evidence the estimate rests on: high, medium, low or indicative (very little evidence).
Listing-level file (CSV)
Dataset, and Business billed yearlyGET https://gcccardeals.com/api/b2b/v1/listings.csv
One row for each priced car behind the market price tables, with the facts from its public listing, as one CSV file rebuilt every day. It comes with Dataset and with Business billed yearly, and a download counts as one call.
Parameters
| Parameter | Required | Description |
|---|---|---|
market | No | Optional. It must match the market of your key; a mismatch is an error. |
Example request
curl -s "https://gcccardeals.com/api/b2b/v1/listings.csv" \
-H "Authorization: Bearer $GCC_API_KEY" \
-o listings.csvExample response
Illustrative values, shown to explain the format. They are not current prices, and the ad is an example.
make,model,model_year,trim,condition,mileage_km,asking_price,price_date,first_price,price_changes,first_seen,last_seen,status,city,source,url,currency,computed_on
toyota,camry,2019,GXR,used,88000,51500,2026-09-10,53000,1,2026-08-30,2026-09-16,listed,riyadh,Example marketplace,https://www.example.com/listing/12345,AED,2026-09-29The download is named gcc-car-deals-<market>-listings-<date>.csv, with the market code and the day the file was built. It is streamed: if the connection breaks before the end, discard it, since the X-Total-Rows header tells you how many rows to expect. A key whose plan does not include the file receives a plan_limit error.
Columns
make- The make as we list it.
model- The model as we list it.
model_year- The model year.
trim- The trim the listing states, or empty when it states none.
condition- used or new.
mileage_km- The odometer reading in kilometres, or empty when the listing does not state one.
asking_price- The latest asking price we observed for the car, in the market's currency.
price_date- The date of that latest asking price.
first_price- The first asking price we saw for the car.
price_changes- How many times the asking price changed while we watched the car.
first_seen- The date we first saw the ad.
last_seen- The date we last saw the ad.
status- listed while the ad is still up, removed once it is no longer up.
city- The city the listing gives, as a lower-case name, or empty when it gives none.
source- The marketplace the ad was found on.
url- A link to the original listing, or empty in the few cases where we withhold it for privacy.
currency- The currency of every price in the answer.
computed_on- The date the table or file was computed, as a UTC date.
Price index
Available on every planGET https://gcccardeals.com/api/b2b/v1/price-index
The GCC Car Deals Used-Car Price Index: how much the asking price of the same kind of used car moved from one month to the next in your market, with the range each move most likely lies in. The headline for the whole market is published free of charge. This endpoint gives you every breakdown, by car age, make origin and make, from the first month of the index. It is available on every plan, and a request counts as one call.
Parameters
| Parameter | Required | Description |
|---|---|---|
segment | No | Optional. The breakdown to return, from the values listed here; leave it out to receive every breakdown. A make is written make: followed by its lower-case name, for example make:toyota. In a URL, percent-encode the plus sign of the oldest age band.allage:0-3age:4-7age:8+origin:chineseorigin:japaneseorigin:germanorigin:koreanorigin:americanmake:<make> |
market | No | Optional. It must match the market of your key; a mismatch is an error. |
Example request
curl -s "https://gcccardeals.com/api/b2b/v1/price-index?segment=origin:japanese" \
-H "Authorization: Bearer $GCC_API_KEY"Example response
Illustrative values, shown to explain the format. They are not current prices, and the ad is an example.
{
"version": 1,
"index": "GCC Car Deals Used-Car Price Index",
"market": "ae",
"method": "new-listings-v2",
"base_month": "2026-07",
"rows": [
{
"segment": "origin:japanese",
"month": "2026-07",
"level": 100,
"change_pct": null,
"change_low_pct": null,
"change_high_pct": null,
"same_year_change_pct": null,
"cars": null,
"prev_cars": null,
"cohorts": null
},
{
"segment": "origin:japanese",
"month": "2026-08",
"level": 100.3,
"change_pct": 0.3,
"change_low_pct": 0,
"change_high_pct": 0.6,
"same_year_change_pct": -0.4,
"cars": 4390,
"prev_cars": 4180,
"cohorts": 902
},
{
"segment": "origin:japanese",
"month": "2026-09",
"level": 99.8,
"change_pct": -0.5,
"change_low_pct": -0.8,
"change_high_pct": -0.2,
"same_year_change_pct": -1.2,
"cars": 4460,
"prev_cars": 4325,
"cohorts": 915
}
],
"quota": {
"monthly": 3000,
"used_month": 12,
"daily": null,
"used_today": null
},
"notes": [
"The index follows the asking prices of used cars newly listed each month, comparing like with like. These are asking prices, not transaction prices. The base month is 100."
]
}Response fields
version- The response format version. Within a version, fields are added and never removed or renamed.
index- The name of the index.
market- The market the answer is for: qa, ae or sa.
method- The name and version of the method that produced the answer.
base_month- The first month of the index, as YYYY-MM. Its level is set to one hundred, and every later level is measured from it.
rows- The series: one row for each breakdown and month, the whole market first, then the breakdowns in alphabetical order, each month by month.
rows[].segment- The breakdown the row belongs to: all for the whole market, or the age band, make origin or make it covers.
rows[].month- The month the row is for, as YYYY-MM.
rows[].level- The index level: one hundred in the base month, then the previous level moved by each month's change. Where a month was too thin to publish, the series is carried over it by comparing directly with the last month that has a level. Null where that is not possible.
rows[].change_pct- How much the asking price of used cars newly listed this month moved from the month before, in percent, comparing like with like and allowing for mileage and for the cars being a month older. Null in the base month, and where the month was too thin to publish.
rows[].change_low_pct- The lower end of the range the change most likely lies in. Null when change_pct is null.
rows[].change_high_pct- The upper end of that range.
rows[].same_year_change_pct- The same comparison without allowing for the cars being a month older: how the asking price of the same model years moved. Null when change_pct is null.
rows[].cars- The newly listed cars compared this month, a car listed more than once counting once. Null in the base month, and where nothing could be compared.
rows[].prev_cars- The newly listed cars compared from the month before. Null in the base month, and where nothing could be compared.
rows[].cohorts- The like-for-like groups compared: cars of the same make, model, model year and trim family, on the same marketplace. Null in the base month, and where nothing could be compared.
quota- Your quota and how much of it you have used: monthly, used_month, daily and used_today.
notes- Plain-language notes on how to read the numbers.
Errors
A refusal returns the matching HTTP status and a JSON body with a code you can act on and a message a person can read.
| Status | Code | Meaning |
|---|---|---|
400 | bad_request | A parameter is missing or invalid. The message says which. No quota is used. |
401 | unauthorized | The key is missing, malformed, expired or revoked. |
403 | plan_limit | Your plan does not include this request, for example the full table on Starter, the listing-level file on a plan without it, or a date further back than your plan's history window. The message says what is missing. |
422 | unknown_vehicle | We do not recognise the make or model, and the answer lists some models we do know. A model we list that has no comparable cars in the window is not an error: the answer has no value and says why. |
429 | rate_limited | Too many requests in a short time. Wait the number of seconds in the Retry-After header. |
429 | busy | Another request with this key is still running. Send one request at a time. |
429 | quota_exceeded | Your monthly quota for this market is used up. It renews at the start of the next calendar month (UTC). |
503 | unavailable | The data could not be read right now. Retry after the number of seconds in the Retry-After header. It is never an answer about the car. |
Limits and quota headers
Each plan has a monthly quota of calls in every market it covers, counted from the start of the calendar month (UTC), on yearly plans too. A valuation, a market-price lookup, a price-index request and a download of the full table or the listing-level file each count as one call. Each client IP address can also send up to 120 requests a minute, and only one request per key can be in flight at a time.
A successful answer reports your quota in three headers. Two more are worth knowing.
| Header | Description |
|---|---|
X-RateLimit-Limit | Your quota for this market in the current period. |
X-RateLimit-Remaining | The calls left in the period after this one. |
X-RateLimit-Reset | When the quota renews, as a Unix timestamp in seconds (UTC). |
Retry-After | On a rate-limit or unavailable error: the seconds to wait before you retry. |
X-Total-Rows | On the CSV download: the number of data rows in the file, to check it arrived whole. |
Limits by plan
| Plan | Calls per market per month | Markets | Past-date valuations | Full daily price table (CSV) | Listing-level file (CSV) |
|---|---|---|---|---|---|
| Starter | 300 | One market of your choice | Last 90 days | Not included | Not included |
| Pro | 3,000 | All markets | Last 90 days | Included | Not included |
| Business | 15,000 | All markets | The whole history | Included | Included when billed yearly |
| DatasetBilled yearly only | 300 | One market of your choice | Last 90 days | Included | Included |
Price history
Each market's price history starts on a fixed date. A valuation can be asked for any date (as_of) from that day, within the history window of your plan.
| Market | History since |
|---|---|
| Qatar | Jun 7, 2026 |
| UAE | Jun 12, 2026 |
| Saudi | Jun 14, 2026 |
Get an API key
Sign-up, payment and API keys are handled at dealers.gcccardeals.com: subscribe, then create a key for each market in your Data API dashboard.
Try the shape of the data before you subscribe: a free CSV of the price tables for the UAE.