Skip to main content

apiauto — Python client for the Encar, Che168, Copart and IAAI API

PyPI version Python versions License: MIT

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 Client and AsyncClient on top of httpx, the only dependency
  • Iterators that follow the cursor for you, with max_items that 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 ...
  • TypedDict types for every response, py.typed, checked with mypy --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)

Source distribution for apiauto-space 0.1.0
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)

Table of built distributions (wheels) for apiauto-space 0.1.0
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page