apiauto — Python client for the Encar, Che168, Copart and IAAI API
apiauto is a typed Python client for the apiauto.space REST API: used-car
listings from Encar (South Korea), Che168 (China) and the Copart and IAAI auctions (USA) in
one JSON record format, without writing a scraper. Search the catalogs by make, model, year, price and
mileage, page through whole markets, follow a feed of new cars, sold cars and price changes, and read
quota headers — from sync or async code. It is an unofficial client: the project is not affiliated with
Encar, Che168/Autohome, Copart or IAAI; the data comes through the apiauto.space API.
- Sync
ClientandAsyncClienton top of httpx, the only dependency - Iterators that follow the cursor for you, with
max_itemsthat never over-spends the quota - Automatic retries on 429 and 503 (honouring
Retry-After) and on 5xx and network errors - One exception class per API error:
QuotaExceededError,RateLimitError,NotFoundError... TypedDicttypes for every response,py.typed, checked withmypy --strict- Python 3.10+
Install
pip install apiauto-space
The distribution is called apiauto-space on PyPI; the module you import is apiauto.
Get a key
Open the Telegram bot @apiautospace_bot and take a free demo key: one day, 100 data requests, up to 20 cars per request, all three markets. Paid plans are on the pricing page.
Save the key in the APIAUTO_KEY environment variable — the client reads it from there:
export APIAUTO_KEY="your-key"
Keep the key on your server. The API does not accept browser requests (CORS is off), and a key in page code is visible to every visitor.
Quick start
import apiauto
client = apiauto.Client() # reads APIAUTO_KEY
page = client.cars("kr", brand="Kia", model="Sorento", year_min=2021, limit=20)
for car in page["items"]:
print(car["id"], car["title"], car["price"]["amount"], car["price"]["currency"])
Markets are kr (Encar, Korea), cn (Che168, China) and us (Copart and IAAI, USA). Every record has
the same keys in every market; a value the source does not provide is None.
Examples
Search Encar by make, model, year and price
page = client.cars(
"kr",
brand="Hyundai",
model="Grandeur",
year_min=2020,
year_max=2023,
price_min=10_000_000, # in the market's currency: KRW for Korea; skips monthly lease payments
price_max=30_000_000,
mileage_max=80_000, # km
sort="-first_seen", # newest listings first (the default)
limit=20,
)
print(page["count"], "cars match") # None when more than 10,000 match
Prices in dollars at the ECB rate work in every market: price_usd_min=15000, price_usd_max=25000.
The full list of filters: brand, model, generation (Korea), trim, year_min, year_max,
price_min, price_max, price_usd_min, price_usd_max, mileage_max, fuel, transmission,
body_type, has_photos, is_active, q (words of the title), sort, limit, cursor.
Search Che168 (China)
for car in client.cars("cn", brand="BYD", fuel="electric", has_photos=True, limit=10)["items"]:
print(car["title"], car["price"]["amount"], "CNY", car["location"]["city"])
US auction lots: Copart and IAAI
for lot in client.cars("us", brand="Toyota", model="Camry", year_min=2020, limit=10)["items"]:
raw = lot["raw"] # auction-only fields as the source sends them
print(lot["id"], lot["vin"], lot["mileage_km"], raw.get("damage_main"), raw.get("sale_date"))
Cars that are not in the copy yet can be searched live at the auction data provider (Pro and Max plans,
a separate monthly counter): client.us_live(make="Toyota", model="Camry", year_min=2020).
Iterate over all pages
for car in client.iter_cars("kr", brand="Genesis", limit=100, max_items=1000):
save(car)
iter_cars follows next_cursor until the last page. With max_items the last request asks only for
the cars still needed, so the quota is not spent on records that would be thrown away. The demo allows
limit up to 20; paid plans up to 100.
Get a car by id
car = client.car("encar:43020893")
print(car["is_active"], car["listing_url"])
Ids look like encar:…, che168:…, copart:… and iaai:… and never change. A sold or removed car
stays available for 14 days with is_active=False.
Keep a catalog in sync with the change feed
import time
from datetime import datetime, timezone
since = datetime.now(timezone.utc)
while True:
time.sleep(3600) # the market copy is refreshed hourly
polled_at = datetime.now(timezone.utc)
for event in client.iter_changes("kr", since, limit=100):
if event["type"] == "added":
save(client.car(event["car_id"]))
elif event["type"] == "disappeared":
mark_sold(event["car_id"])
else: # price_drop or price_up
update_price(event["car_id"], event["new_price"])
since = polled_at
since takes a datetime (a naive one is treated as UTC) or an ISO 8601 string. Filter by
type="price_drop" (or a list of types) and min_pct=5. The feed comes with the Pro, Max and demo
plans; each event counts as one record. A complete script is in
examples/sync_changes.py.
References: makes, models, canonical values, freshness
These calls are free.
client.brands("cn") # [{"name": "Mercedes-Benz", "count": 20185}, ...]
client.models("kr", "Hyundai") # the make's models with car counts
client.dictionaries()["fuel"] # {"gasoline": ["gasoline", "petrol", ...], "diesel": [...], ...}
for market in client.markets():
print(market["market"], market["active_cars"], market["data_age_seconds"], market["stale"])
Usage and quota headers
The quota counts returned cars (records), not requests; the demo counts data requests.
usage = client.usage() # free
print(usage["plan"], usage["quota_used"], "/", usage["quota_limit"], usage["quota_unit"])
client.cars("us", limit=5)
info = client.last_response
print(info.quota_remaining, info.quota_reset, info.rate_remaining, info.data_age_seconds)
client.last_response is a frozen ResponseInfo read from the X-Quota-*, X-RateLimit-* and
X-Data-Age-Seconds headers of the last response; a header that was not sent is None.
Async
import asyncio
import apiauto
async def main() -> None:
async with apiauto.AsyncClient() as client:
page = await client.cars("cn", brand="Toyota", limit=10)
async for car in client.iter_cars("kr", brand="Kia", max_items=50):
print(car["title"])
asyncio.run(main())
AsyncClient has the same methods as Client; iter_cars and iter_changes return async iterators.
Errors and retries
try:
car = client.car("encar:1")
except apiauto.NotFoundError:
car = None
except apiauto.QuotaExceededError as error:
print("quota renews at", error.reset_at)
except apiauto.ApiautoError as error:
print(error.status_code, error.code, error.detail)
| HTTP | Exception | Retried |
|---|---|---|
| 400 | BadRequestError |
no |
| 401 | AuthenticationError |
no |
| 402 | QuotaExceededError |
no |
| 403 | PermissionDeniedError |
no |
| 404 | NotFoundError |
no |
| 429 | RateLimitError |
yes, after Retry-After |
| 500, 502 | ServerError |
yes, exponential backoff |
| 503 | ServiceUnavailableError |
yes, after Retry-After |
| — | APIConnectionError (network, timeout) |
yes, exponential backoff |
All of them derive from ApiautoError. Retries follow the
API's error guide: max_retries=2 by default, a wait of at most
60 seconds; Client(max_retries=0) turns them off. Other options: Client(api_key=..., base_url=..., timeout=30.0, http_client=httpx.Client(...)).
Photos
car["photos"] are signed links that work for about a day. Store the car's id, not the links, and
fetch a fresh record with client.car(id) when you need the photos again. Photo requests are free and
may redirect to the original host, so follow redirects when downloading them.
Prices are decimal strings
Prices, dollar prices and percentages come as strings such as "23900000.00" so no precision is lost.
Convert them with Decimal:
from decimal import Decimal
total = sum(Decimal(car["price_usd"]) for car in page["items"] if car["price_usd"])
A record
A real Encar car from the documentation, shortened: two photo
links and a few raw fields.
{
"id": "encar:43020893",
"market": "kr",
"source": "encar",
"source_id": "43020893",
"title": "2020 Kia Mohave The Master 3.0 4WD 7-seater Masters",
"brand": "Kia",
"model": "Mohave",
"generation": "Mohave The Master",
"trim": "3.0 4WD 7-seater Masters",
"year": 2020,
"mileage_km": 93724,
"fuel": "diesel",
"transmission": null,
"engine_cc": null,
"body_type": null,
"color": null,
"vin": null,
"price": {"amount": "23900000.00", "currency": "KRW"},
"price_usd": "18033.96",
"location": {"country": "KR", "region": "Gyeonggi", "city": null},
"photos": [
"https://api.apiauto.space/img/encar:43020893/1.jpg?exp=…&sig=…",
"https://api.apiauto.space/img/encar:43020893/2.jpg?exp=…&sig=…"
],
"photo_count": 4,
"listing_url": "https://fem.encar.com/cars/detail/43020893",
"is_active": true,
"first_seen_at": "2026-10-26T04:37:32.553458Z",
"last_seen_at": "2026-10-26T04:37:32.553458Z",
"disappeared_at": null,
"updated_at": "2026-10-26T04:37:32.553458Z",
"source_mode": "replica",
"raw": {"badge": "디젤 3.0 4WD 7인승", "form_year": "202004", "sell_type": "normal", "has_accident": false}
}
The fields each source adds are described on the Encar API, Che168 API, Copart API and IAAI API pages.
Methods
| Method | Endpoint | Returns | Quota |
|---|---|---|---|
cars(market, **filters) |
GET /v1/cars |
CarsPage: items, next_cursor, count |
per car |
iter_cars(market, *, max_items=None, **filters) |
GET /v1/cars |
iterator of Car |
per car |
car(car_id) |
GET /v1/cars/{id} |
Car |
1 |
changes(market, since, *, type, min_pct, limit, cursor) |
GET /v1/changes |
ChangesPage |
per event |
iter_changes(market, since, *, type, min_pct, limit, max_items) |
GET /v1/changes |
iterator of Change |
per event |
brands(market) |
GET /v1/references/brands |
list of {name, count} |
free |
models(market, brand) |
GET /v1/references/models |
list of {name, count} |
free |
dictionaries() |
GET /v1/references/dictionaries |
field → canonical value → synonyms | free |
markets() |
GET /v1/references/markets |
list of Market |
free |
usage() |
GET /v1/usage |
Usage |
free |
us_live(*, make, model, year_min, year_max, price_max, page) |
GET /v1/us/live |
list of Car |
own counter |
The API reference: apiauto.space/en/docs and the OpenAPI schema.
Other languages
| Language | Package | Repository |
|---|---|---|
| Python | pip install apiauto-space |
apiauto-space/apiauto-python |
| JavaScript, TypeScript | npm install apiauto |
apiauto-space/apiauto-js |
| curl, PHP and more | — | apiauto-space/encar-api-examples |
По-русски
apiauto — Python-клиент для API Encar, Che168, Copart и IAAI, без парсинга: объявления о подержанных
автомобилях из Южной Кореи, Китая и с аукционов США приходят в одном формате JSON через
apiauto.space. Клиент умеет искать по марке, модели, году, цене и пробегу,
сам проходит по страницам курсором, повторяет запрос при 429 и 503 и работает в синхронном и асинхронном
коде. Бесплатный демо-ключ на сутки выдаёт Telegram-бот
@apiautospace_bot; сохраните его в переменной APIAUTO_KEY
и держите на сервере, не в браузере. Документация на русском: apiauto.space/docs.
pip install apiauto-space
import apiauto
client = apiauto.Client() # ключ из APIAUTO_KEY
for car in client.cars("kr", brand="Kia", year_min=2021, limit=20)["items"]:
print(car["title"], car["price"]["amount"], car["price"]["currency"])
License
MIT © 2026 apiauto.space
Disclaimer
This is an unofficial client of the apiauto.space API. It is not affiliated with, endorsed by or sponsored by Encar, Che168, Autohome, Copart or IAAI; their names are used only to describe where the listings come from, and all trademarks belong to their owners. Use of the data is subject to the apiauto.space terms.
Metadata
Release files for apiauto-space 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| apiauto_space-0.1.0.tar.gz | 25.2 kB | With provenance | Details for apiauto_space-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| apiauto_space-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for apiauto_space-0.1.0-py3-none-any.whl |
Total release size: 48.2 kB
Release files / apiauto_space-0.1.0.tar.gz
| Download URL | apiauto_space-0.1.0.tar.gz |
|---|---|
| Size | 25.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f7facea5b76cc99f72689902cf0ac654a82de92ca8a9b428688ad53351188277
|
|
BLAKE2b-256 checksum How to use checksums |
0f53cc69a67ee7ede63035ef6d6f7a38c58669ba65359f8363fc8c160d295eda
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logRelease files / apiauto_space-0.1.0-py3-none-any.whl
| Download URL | apiauto_space-0.1.0-py3-none-any.whl |
|---|---|
| Size | 23.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f74cacabaf9078c9c88c82f18c437e981dd41bd452a86798b43895bec82a29b1
|
|
BLAKE2b-256 checksum How to use checksums |
23a364aa9c71d419e31cebd2a259d30f0e26203e6edc28579dfc72c40006d878
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logSecurity for apiauto-space 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for apiauto_space-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.