Partner API
Avtoorder API for partners
A read-only JSON API to the cars available on avtoorder.com, including vehicles located in China, with turnkey delivery estimates.
What the API provides
Everything is read-only and returned as JSON. Base URL:
https://avtoorder.com/api/v1- Car list with filters, sorting and pagination.
- Car detail with a turnkey price estimate for the destination you choose.
- Brands and the models available for each brand.
- Photos served through our own CDN links, in four widths (384, 640, 1080 and 1920 px).
Car list filters (an unknown parameter, or a minimum greater than its maximum, is rejected with 400):
GET /cars
page (default 1), pageSize (1-100, default 24),
brand, model (needs brand),
yearMin, yearMax, priceMinCny, priceMaxCny,
mileageMinKm, mileageMaxKm,
fuel (gasoline|diesel|electric|hybrid|plugin_hybrid),
condition (new|used),
sort (recommended|price_asc|price_desc|mileage_asc|year_desc|newest),
default recommended
locale (en|ru|zh), default ru
GET /cars/{id} destination (default RU_VLA),
locale (default ru)
GET /brands
GET /brands/{brand}/models
GET /me
GET /media/{token} w=384|640|1080|1920 (default 1080),
no API key neededGetting access
API keys are issued by our team, there is no self-service sign-up. Tell us about your company and how you plan to use the data, and we will agree on the key scope and limits.
- Open the contact form and choose the "Partnership Opportunity" subject.
- Write "API access", your company, your use case and the server IP addresses you will call from.
- We create a client for you and send the key over a private channel. It is shown once, so store it safely.
- Your key has a scope: the full catalog, or only the cars Avtoorder holds in its own stock. Call GET /me to see it.
Quick start
Send the key in the Authorization header. Replace the placeholder with your key.
List cars of a brand
curl -H "Authorization: Bearer avto_live_YOUR_KEY" \
"https://avtoorder.com/api/v1/cars?brand=BRAND_KEY&pageSize=24&sort=price_asc"Get one car with a turnkey estimate
curl -H "Authorization: Bearer avto_live_YOUR_KEY" \
"https://avtoorder.com/api/v1/cars/CAR_ID?locale=en"Take brand keys from GET /brands and car ids from the list response.
Valid values of destination for GET /cars/{id} (default RU_VLA):
RU_VLA, RU_NSK, RU_MOW, KZ_ALA, UZ_TAS, BY_MSK, KG_FRUThe full schema of every endpoint is in the interactive reference.
Authentication and security
- Send the key only as Authorization: Bearer avto_live_… in the request header.
- A key in the URL query string is rejected with 400 key-in-query and the key is flagged as exposed, because URLs end up in logs and browser history. Rotate it if this happens.
- Keys can be restricted to a list of IP addresses.
- Keys can be rotated with a grace period during which the old key still works, and revoked immediately at any time.
- Keep keys on your server. Never ship them in a browser or mobile app.
Rate limits
Every key has a per-minute rate limit and a daily quota (the day is UTC). Your limits are returned by GET /me. There is also a per-IP limit at our edge.
- Each authenticated response carries RateLimit and RateLimit-Policy headers. RateLimit covers the per-minute window only; your daily usage is in GET /me.
- When a limit is exceeded you get 429 with a Retry-After header (seconds). Wait that long before retrying.
- Failed authentication attempts are throttled separately.
- Retry 429 and 503 with exponential backoff. Do not retry other 4xx errors unchanged.
- Some uncached queries can take 10–15 s; allow up to 30 s per request.
Errors
Errors use the application/problem+json format (RFC 9457). The type field is a link to the matching row below. The detail text is in English and meant for developers. Only GET and HEAD are supported: other methods are rejected (405 with an empty body, 403 on photo links); OPTIONS on the JSON endpoints lists the allowed methods.
Example
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 12
RateLimit-Policy: "minute";q=60;w=60, "day";q=20000;w=86400
RateLimit: "minute";r=0;t=12
{
"type": "https://avtoorder.com/en/developers#error-rate-limited",
"title": "Rate limit exceeded",
"status": 429,
"requestId": "0f3c…"
}| Code | HTTP | What it means |
|---|---|---|
| missing-credentials | 401 | No Authorization header, or the scheme is not Bearer. |
| invalid-credentials | 401 | The key is unknown or malformed. |
| key-revoked | 401 | The key was revoked. Ask us for a new one. |
| key-expired | 401 | The key passed its expiry date. |
| client-disabled | 401 | Your account is disabled. Contact us. |
| ip-not-allowed | 403 | The request came from an IP address that is not on the key's allowlist. |
| key-in-query | 400 | The key was sent in the URL. Move it to the Authorization header and rotate the key. |
| invalid-request | 400 | A parameter is unknown or invalid, or a minimum is greater than its maximum (for example yearMin > yearMax). The errors array lists each problem by path. |
| not-found | 404 | The car or brand does not exist, or is outside your key's scope. |
| rate-limited | 429 | Per-minute limit exceeded. See Retry-After. |
| quota-exceeded | 429 | Daily quota exceeded. It resets at 00:00 UTC. |
| auth-throttled | 429 | Too many failed authentication attempts from your IP address. Wait and retry. |
| upstream-busy | 503 | The catalog is temporarily busy. Retry with backoff. |
| unavailable | 503 | The API is not available right now: a deployment is in progress or the API is not configured. Retry shortly. |
| internal | 500 | Unexpected error on our side. Quote the requestId when you contact us. |
| edge-rate-limited | 429 | Too many requests from one IP address, rejected at our network edge before reaching the API. Slow down and retry later. |
Syncing the full catalog
A single query never returns the whole catalog, so walk it brand by brand, in slices.
- Call GET /brands to list all brand keys.
- For each brand, call GET /cars?brand=KEY&pageSize=100 and increase page until data is empty or page.hasMore is false.
- Split each brand into slices: one year at a time (yearMin and yearMax set to the same year), then price bands (for example 10 000 CNY wide, with priceMinCny and priceMaxCny). Page through every slice the same way. Narrower slices give better coverage.
- truncated: true means the result may be incomplete because some feeds could only be scanned in part. Narrowing the filters improves coverage, but truncated can stay true even on an empty or complete slice, so do not keep narrowing until it turns false: it may never do so.
- Car ids are stable: use them as your primary key and update a car whenever it comes back. A car missing from one response is not proof that it is gone: remove a car only after it has been absent across a full sync.
- fetchedAt is the time of your response, not of the data. Data can be up to 24 hours old, so syncing more often than once an hour gains little.
Data notes
- The turnkey price (turnkeyEstimate) is an estimate, not a quote. It has isEstimate: true and the exchange rate used, with its source (fx.rateSource) and the time it was captured (fx.capturedAt).
- Every assumption the estimate had to make is listed in customs.assumptions. totalIsLowerBound: true (engine power or the customs duty unknown) means the real total can be higher. An unknown registration date is flagged (registrationUnknown) but does not make the total a lower bound.
- When the price of a car is unknown, priceCny is null and priceOnRequest is true. It is never 0. Such cars have no estimate and turnkeyUnavailableReason is price_on_request. For some listings we do not quote a turnkey price at all (for example, listings priced in USD, see askingPrice.currency); turnkeyUnavailableReason is then not_offered_for_listing.
- totalEstimate in list responses is approximate, can change between pages and can be 0 while truncated is true. Do not use it to decide when to stop paging.
- sort=recommended (the default) is a mixed ranking that interleaves the inventories; newest is also a mixed ranking across inventories that puts newer cars first within each inventory, not a strict order by listing date; price_asc and price_desc sort by price, low to high and high to low; mileage_asc puts the lowest mileage first; year_desc the highest year first. year is the car's year as the listing reports it: for most used cars the year of first registration, for some the model year. firstRegistration is the month and year of first registration when known.
- fuel is read from the listing — its stated energy type where that is reliable, otherwise an unambiguous technology name or engine code in the title (for example DM-i is plugin_hybrid, 280TSI is gasoline) — and is null when neither states it. A car with a null fuel never matches a fuel filter, so a fuel-filtered list holds the cars known to have that drivetrain, not necessarily all of them. Extended-range EVs (EREV) are reported as hybrid. condition: used means not known to be new, and cars with condition: null can appear in results, including under condition=used.
- locale sets the language of titles and labels where a translation exists; car titles, trims and some spec values are often in English or Chinese regardless of locale.
- GET /brands lists the brand keys known to the catalog: some may currently have no cars, and count is null when unknown. Brand and model keys can be non-ASCII (for example Chinese), so URL-encode them.
- Photos are served through our own image links (sizes 384, 640, 1080 and 1920 via ?w=). Links are stable but not guaranteed permanent: once your key is revoked they stop fetching new photos, although images that are already cached may keep loading for a while, and a link stops working when its photo is removed. Refresh them from the API when you re-sync.
- VINs are masked except for cars held in Avtoorder's own stock.
Terms of use
- Do not resell or redistribute the raw API data as a data product. Using it to show cars to your own customers is fine.
- Attribution is not required.
- We may revoke a key at any time if these terms are breached, if a key leaks, or if it abuses the service.
- Rate limits and quotas are enforced.
- Keep your key secret and tell us immediately if it is exposed.
- The API may change. Additive changes are not announced; breaking changes go to a new version path.
Interactive reference
Try every endpoint in the browser and download the machine-readable OpenAPI specification.