Your first request in five minutes.
apiauto.space is a JSON REST API: used-car catalogs of Korea (Encar), China (Che168) and US auctions (Copart, IAAI) in one record format. The base URL is https://api.apiauto.space/v1.
The full schema is in Swagger UI (you can send requests) and in Redoc; machine-readable at openapi.json.
Quick start
- Open the bot and take a demo key: one day, 100 data requests, up to 20 cars per request, all three markets.
- Save the key in the APIAUTO_KEY environment variable: it is shown once.
- Send a request:
curl -s "https://api.apiauto.space/v1/cars?market=kr&brand=Kia&limit=20" \
-H "X-API-Key: $APIAUTO_KEY"import os
import httpx
client = httpx.Client(
base_url="https://api.apiauto.space/v1",
headers={"X-API-Key": os.environ["APIAUTO_KEY"]},
timeout=30,
)
# Two pages of 20: every car returned counts against the quota.
params = {"market": "kr", "brand": "Kia", "limit": 20}
for _ in range(2):
page = client.get("/cars", params=params).raise_for_status().json()
for car in page["items"]:
print(car["id"], car["title"], car["price"])
if not page["next_cursor"]:
break
params["cursor"] = page["next_cursor"]const response = await fetch(
"https://api.apiauto.space/v1/cars?market=cn&limit=20",
{ headers: { "X-API-Key": process.env.APIAUTO_KEY } },
);
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const { items, next_cursor } = await response.json();
console.log(items.map((car) => car.title));
console.log("records left:", response.headers.get("X-Quota-Remaining"));<?php
$ch = curl_init("https://api.apiauto.space/v1/cars?market=us&limit=20");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("APIAUTO_KEY")],
CURLOPT_RETURNTRANSFER => true,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException("HTTP $status: $body");
}
foreach (json_decode($body, true)["items"] as $car) {
echo $car["id"], " ", $car["title"], PHP_EOL;
}The answer is a page of cars and the cursor of the next page. One record looks like this (shortened: two photo links and the first raw fields):
{
"id": "encar:42795070",
"market": "kr",
"source": "encar",
"source_id": "42795070",
"title": "2022 BMW 1 Series (F40) M135i xDrive",
"brand": "BMW",
"model": "1 Series (F40)",
"trim": "M135i xDrive",
"year": 2022,
"mileage_km": 49000,
"fuel": "gasoline",
"transmission": null,
"engine_cc": null,
"body_type": null,
"color": null,
"vin": null,
"price": {
"amount": "30000000.00",
"currency": "KRW"
},
"price_usd": "21920.18",
"location": {
"country": "KR",
"region": "Gyeonggi",
"city": null
},
"photo_count": 4,
"listing_url": "https://fem.encar.com/cars/detail/42795070",
"is_active": true,
"first_seen_at": "2026-09-25T14:04:10.804411Z",
"last_seen_at": "2026-09-25T14:04:10.804411Z",
"disappeared_at": null,
"updated_at": "2026-09-25T14:04:10.804411Z",
"source_mode": "replica",
"photos": [
"https://api.apiauto.space/img/encar:42795070/1.jpg?exp=…&sig=…",
"https://api.apiauto.space/img/encar:42795070/2.jpg?exp=…&sig=…"
],
"raw": {
"badge": "M135i xDrive",
"has_vin": false,
"form_year": "202209",
"sell_type": "normal",
"body_class": null,
"office_city": "경기",
"badge_detail": null,
"has_accident": false
}
}Keep the key on your server. The API does not allow browser requests (CORS is off), and a key in page code is visible to every visitor.
Key and headers
Every data request sends the key in the X-API-Key header. We keep only a hash of the key; a lost key is rotated in the bot — the old one works for 24 more hours and the quota is not reset.
| Response header | Meaning |
|---|---|
X-Quota-Limit | The period quota: cars a month (requests a day on the demo). |
X-Quota-Remaining | How much is left. |
X-Quota-Reset | When the quota renews, ISO 8601 UTC. |
X-RateLimit-Limit | Requests per second on the plan. |
X-RateLimit-Remaining | Left in the current second. |
X-RateLimit-Reset | Seconds until the counter resets (1). |
Retry-After | On 429 and some 503s: seconds to wait before retrying. |
X-Data-Age-Seconds | The age of the market copy in seconds. |
Endpoints
| Endpoint | What it does | Quota |
|---|---|---|
GET /v1/cars | A market's catalog with filters and a cursor. | cars returned |
GET /v1/cars/{id} | One car by id (encar:…, che168:…, copart:…, iaai:…). | 1 record |
GET /v1/changes | The change feed: new, removed, prices. Pro and above. | events returned |
GET /v1/references/brands | A market's makes with car counts. | free |
GET /v1/references/models | A make's models with car counts. | free |
GET /v1/references/dictionaries | Allowed values of fuel, transmission, body_type and other fields. | free |
GET /v1/references/markets | Market freshness, car counts, plan access. | free |
GET /v1/us/live | Live US search beyond the copy. Pro and above. | a separate counter |
GET /v1/usage | Quota usage by market. | free |
GET /img/{id}/{n}.jpg | A car photo by the signed link from the record. | free |
GET /healthz | Health: 503 when any market is past its freshness threshold. | free |
GET /v1/cars
Only market (kr, cn, us) is required. Other parameters narrow the selection:
| Parameter | Value |
|---|---|
brand, model, trim | An exact match; spelling as in /v1/references/brands and /models. |
year_min, year_max | Model year. |
price_min, price_max | Price in the market's currency (KRW, CNY, USD). |
price_usd_min, price_usd_max | Price in dollars at the ECB daily rate. |
mileage_max | Mileage, km. |
fuel, transmission, body_type | Values from /v1/references/dictionaries. |
has_photos | true / false |
is_active | true by default; false — taken off sale within the last 14 days. |
q | Up to 6 words of the title (year, make, model, trim); short words such as k5, x5 match whole. |
sort | -first_seen (default), first_seen, price, -price, year, -year, mileage, -mileage |
limit | 1–100, 20 by default (up to 20 on the demo). |
cursor | The next_cursor of the previous page. |
The answer: {"items": [...], "next_cursor": "…" | null, "count": 1834 | null}. count is how many cars match the filters when there are at most 10,000; above that it is null. Pagination is cursor-only, so a page does not shift while the catalog changes. offset is not supported — a request with it gets 400.
The car record
Every market has the same keys. What the source does not provide is null, never a guess. Whatever only the source has is in raw.
| Field | Meaning |
|---|---|
id | source:source_id, stable for good. |
market, source | kr · encar, cn · che168, us · copart | iaai |
title | Year, make, model and trim in one line. |
brand, model, trim | In Latin script; makes are spelled the same in every market. |
year, mileage_km, engine_cc | Numbers; mileage in kilometres. |
fuel, transmission, body_type, color | Canonical English values. |
vin | When the source provided it. |
price | {amount, currency} — the price in the market's currency (US: the bid or buy-now). |
price_usd | The same price in dollars at the ECB daily rate. |
location | {country, region, city} |
photos, photo_count | Signed photo links and their number. |
listing_url | The listing at the source (null for the US). |
is_active, first_seen_at, last_seen_at, disappeared_at, updated_at | Status and times in UTC. |
raw | Source fields as they are, including the original spellings (raw.source_values). |
Photos
Links in photos are signed and work for about a day — keep the car id, not the link, and fetch a fresh record when needed. Photos are served by our server from a cache; /img requests are free.
GET /v1/changes
Parameters: market, since (required, ISO 8601 with a time zone), type (added, disappeared, price_drop, price_up; repeatable), min_pct (the price change threshold in percent, 0.5 by default), limit, cursor. Each event costs one record. Pro, Max and the demo.
GET /v1/us/live
Search US auctions at the data provider — for lots that are not in the copy. Parameters: make, model, year_min, year_max, price_max, page. A separate monthly counter: Pro — 2,000, Max — 10,000 requests. When the provider is down or out of its own limit, the answer is 503 with retry_after.
Quota and limits
The catalog and the change feed count the cars and events returned, not requests: a response with 37 cars costs 37 records, an empty one costs nothing. The demo counts every data request.
| Plan | Quota | Markets | Req/s | limit | Feed | Live US |
|---|---|---|---|---|---|---|
| Demo | 100 requests a day | 3 | 1 | 20 | ✓ | — |
| Start | 100,000 cars a month | 1 | 2 | 100 | — | — |
| Pro | 500,000 cars a month | 2 | 5 | 100 | ✓ | 2,000 |
| Max | 2,000,000 cars a month | 3 | 10 | 100 | ✓ | 10,000 |
The monthly quota renews on the 1st at 00:00 UTC. After the paid period ends the key works for 3 more days at a fifth of the plan's rate (at least 1 request per second), then answers 401 — renewing within 90 days keeps the same key.
Market boundaries
| Market | Updated | VIN | Photos | Missing |
|---|---|---|---|---|
| Korea · Encar | every hour | rare (~0.1%) | usually 4 | transmission, engine size |
| China · Che168 | every hour | no | many | VIN |
| USA · Copart, IAAI | every hour | yes | many | past auctions; body type on some lots |
The age of the market copy comes in the X-Data-Age-Seconds header of every catalog answer. Current market freshness is on the status page and in /v1/references/markets. More about the markets — on the home page.
Errors
An error body is always JSON like {"error": "code", ...} with useful fields.
| HTTP | error | When |
|---|---|---|
| 400 | bad_request | A bad parameter, limit above the plan, offset given. The detail field explains. |
| 401 | unauthorized | No key, a wrong or revoked key, or the subscription has ended. |
| 402 | quota_exhausted | The period quota is spent; reset_at says when it renews. |
| 403 | market_not_in_plan | The market is not in the plan; markets lists those that are. |
| 403 | feature_not_in_plan | The change feed or live US search is not in the plan. |
| 403 | invalid_signature · link_expired | A photo link is forged or expired. |
| 404 | not_found | No such car or path. |
| 429 | rate_limited | More requests per second than the plan allows; retry_after. |
| 502 | image_upstream_unavailable · invalid_image_upstream | The source did not return the photo. |
| 503 | upstream_unavailable · upstream_quota_exhausted · upstream_invalid_response | Live US search: the provider is unavailable; retry_after. |
| 500 | internal_error | Our fault; admins are already alerted. |
Versioning
Within /v1 fields are never removed or renamed; new optional fields may appear — do not fail on unknown keys. Breaking changes will ship under /v2 with a transition period announced in the bot.
Changelog
2026-09 · v1
- Catalog, car by id, change feed, references, usage, live US search.
- Signed photo links through /img.
- Makes and models are spelled the same in every market (Ford, Mercedes-Benz, CX-5, XC60); the original spellings stay in raw.source_values.
- /v1/references/markets has data_age_seconds and stale: how fresh each market's copy is.