Official Python client for EnCarAPI, the REST Encar API and Korean Car API. One package for:
- Korea: Encar.com, KB Chachacha (
source="kbc") and K Car (source="kcar"), with photos, specs, options, inspection reports, accident and ownership records, condition reports, Encar diagnoses, price history, a change feed and full catalog export. - China: Dongchedi and Che168 in English via ChinaCarAPI (separate key or the China add-on for EnCarAPI keys).
Built for car exporters, dealers and marketplaces that need reliable used car data without scraping, proxies or geo-blocks.
An API key is required. The data is a paid service. Get a key (5-day trial) at encarapi.com.
pip install encarapiRequires Python 3.8+ and requests.
from encarapi import EnCarAPI
client = EnCarAPI("YOUR_API_KEY") # or set ENCARAPI_KEY
# Search Korean listings with flat English filters
cars = client.korea.catalog(
manufacturer="Hyundai",
max_mileage=60000,
frame_clean=True, # accident-free chassis only
lang="en",
count=True,
)
print(cars["Count"], cars["SearchResults"][0])
# Full detail, inspection report and insurance record for one car
detail = client.korea.vehicle("12345678")
inspection = client.korea.inspection("12345678")
record = client.korea.record("12345678")
# KB Chachacha and K Car listings, or all three marketplaces deduplicated
kbc = client.korea.catalog(source="kbc", limit=20)
everything = client.korea.catalog(source="all", count=True)
# Chinese listings (Dongchedi + Che168)
byd = client.china.catalog(make="BYD", export_ready=True, limit=25)Every Korea call that takes lang="en" also takes lang="ru" or lang="de": fuel, transmission, colors,
body class, sell type, regions and options come in Russian or German (with "de" also model and trim names,
e.g. C-Klasse, 5er). Ids, codes and numbers stay the same, and the filters accept these values too.
ru = client.korea.catalog(fuel="дизель", lang="ru", limit=10)
de = client.korea.catalog(model_group="C-Klasse", fuel="Benzin", lang="de", limit=10)
enums_de = client.korea.enums(lang="de") # nameDe per valueChina calls take lang="zh" (original Chinese values) or lang="ru": Russian values for the fixed fields
(fuel, transmission, drivetrain, colours, seller type, condition, vehicle class), city names and equipment
names on catalog, vehicle, vehicle_by_url, bulk_vehicles and specs. Make, model and trim names stay
Latin. enums(lang="ru") adds nameRu to fuels and cities, export_csv(lang="ru") /
export_to_file(path, lang="ru") return the label columns in Russian.
cn = client.china.catalog(make="BYD", lang="ru", limit=10)Encar's own body diagnosis (엔카진단, Encar listings only, about three in four active ones): the 8 outer
panels with resultCode NORMAL or REPLACEMENT, plus CHECKER_COMMENT (the inspector's verdict, which
also states the frame result) and OUTER_PANEL_COMMENT (Korean free text, resultCode None).
diag = client.korea.diagnosis("41192872")
replaced = [i["name"] for i in diag["items"] if i["resultCode"] == "REPLACEMENT"]
# e.g. ["HOOD", "FRONT_FENDER_RIGHT"]
# The same block is in every Encar vehicle detail (None = Encar has no diagnosis for the car)
report = client.korea.vehicle("41192872")["diagnosisReport"]Without a diagnosis, diagnosis() raises EnCarAPIError with status 404; its JSON body has
diagnosisAvailable: false (Encar has no diagnosis for the listing) or detailPending: true (the listing's
detail is not collected yet, retry later). bulk_diagnoses(ids) takes up to 500 ids and lists such ids under
noDiagnosis / pending. A new or changed diagnosis does not create an event in the change feed. The
response shapes are available as TypedDicts for type hints: DiagnosisItem, DiagnosisReport,
DiagnosisResponse, DiagnosisBulkResponse.
Encar's vehicle history (차량이력, Encar listings only, about 8 in 10 of them): the timeline of inspections,
repairs, owner changes, insurance claims and recalls, collected from the Ministry of Land, Infrastructure and
Transport and the Korea Insurance Development Institute. Keys and enums are English; Korean source text stays
in the *Ko fields and is not translated with lang.
hist = client.korea.history("41483842")
print(hist["summaryKo"], hist["eventCount"], "events")
# Odometer over time (oldest first) - a reading lower than an earlier one is worth a closer look
mileage = [f'{r["date"]}: {r["mileageKm"]} km' for r in hist["mileageReadings"]]
# All insurance claims on the timeline
claims = [e for e in hist["events"] if e["type"] == "insurance_claim"]Without a history, history() raises EnCarAPIError with status 404; its JSON body has
historyAvailable: false (Encar shows no public history for this listing) or historyPending: true (Encar
has one but we have not collected it yet, retry later). Encar ids only (no bulk endpoint); prefixed kbc: /
kcar: ids answer 404 with a hint. A new or changed history does not create an event in the change feed. The
response shape is available as the VehicleHistory TypedDict (with HistoryEvent, HistoryMileageReading,
HistoryOwnershipPeriod, HistoryFlags).
Three steps: export to a file, start the change feed at the export's timestamp, then keep the cursor.
# 1) Baseline: stream the full catalog to a file (not held in memory)
export = client.korea.export_to_file("catalog.csv") # {"path", "bytes", "generated_at"}
import_csv(export["path"]) # your import: upsert by listing_id
# 2) First sync: start the change feed at the export's generated_at
# (X-Encarapi-Generated-At, not the time of your download)
for change in client.korea.iterate_changes(since=export["generated_at"]):
# change["type"]: "added" | "updated" | "reappeared" | "removed"; apply idempotently
...
save_cursor(client.korea.last_cursor)
# 3) Every later sync: continue from the saved cursor
for change in client.korea.iterate_changes(cursor=load_cursor()):
...
save_cursor(client.korea.last_cursor)generated_at lies a few minutes before the file's data snapshot, so the first feed page can repeat a few
events that are already in the file: apply them as upserts. Use the same source / sell_type for the
export and the feed (e.g. export_to_file("kbc.csv", source="kbc"), then
iterate_changes(source="kbc", since=...)). export_csv() still returns the CSV as one string, but without
the timestamp; for large catalogs use export_to_file(). Trucks and excavators work the same way with
export_trucks_to_file() / export_equipment_to_file() and source="truck" / "equipment" on the feed.
The file is written to <path>.part first and renamed when complete, so an aborted download never leaves
a truncated file under the final name.
The China API works the same way (client.china, header x-generated-at); its events are
{"id", "source", "vehicleId", "type": "new" | "price" | "removed" | "relisted", "oldPrice", "newPrice", "at"}:
export = client.china.export_to_file("china-catalog.csv")
for change in client.china.iterate_changes(since=export["generated_at"]):
# change["type"]: "new" | "price" | "removed" | "relisted"
...
save_cursor(client.china.last_cursor) # later: iterate_changes(cursor=load_cursor())| Method | Endpoint | Notes |
|---|---|---|
catalog(**params) |
GET /api/catalog |
Search & list. source: encar (default), kbc, kcar, all |
iterate_catalog(**params) |
GET /api/catalog |
Generator over all pages |
iterate_catalog_by_id(**params) |
GET /api/catalog?sort=id |
Complete, stable traversal (keyset cursor, nothing repeated or skipped) |
leasing(**params) |
GET /api/catalog/leasing |
Lease takeovers and rentals |
iterate_leasing_by_id(**params) |
GET /api/catalog/leasing?sort=id |
Complete, stable traversal of lease/rental listings |
vehicle_by_vin(vin) |
GET /api/vehicle/by-vin/{vin} |
All listings of one VIN incl. removed ones (Business/Scale) |
trucks(**params) |
GET /api/catalog/trucks |
Trucks, buses and special vehicles (Business/Scale) |
truck(id) |
GET /api/catalog/trucks/{id} |
One truck with detail, inspection report, cargo box, VIN and plate |
truck_enums() |
GET /api/catalog/trucks/enums |
Truck filter values with counts |
trucks_csv() |
GET /api/catalog/trucks/export |
Full truck inventory CSV (Business/Scale) |
export_trucks_to_file(path, **params) |
GET /api/catalog/trucks/export |
Same, streamed to a file: {"path", "bytes", "generated_at"} |
iterate_trucks_by_id(**params) |
GET /api/catalog/trucks?sort=id |
Complete, stable traversal of the truck catalog |
equipment(**params) |
GET /api/catalog/equipment |
Used excavators (Business/Scale) |
equipment_enums() |
GET /api/catalog/equipment/enums |
Equipment filter values with counts |
equipment_csv() |
GET /api/catalog/equipment/export |
Full equipment inventory CSV (Business/Scale) |
export_equipment_to_file(path, **params) |
GET /api/catalog/equipment/export |
Same, streamed to a file: {"path", "bytes", "generated_at"} |
iterate_equipment_by_id(**params) |
GET /api/catalog/equipment?sort=id |
Complete, stable traversal of the equipment catalog |
nav(**params) |
GET /api/nav |
Filter facets with counts |
enums(**params) |
GET /api/enums |
Valid values for the flat filters |
model_search(search, **params) |
GET /api/model-search |
Model autocomplete |
vehicle(id) |
GET /api/vehicle/{id} |
Ids: Encar id, kbc:<id>, kcar:<id> |
inspection(id) |
GET /api/inspection/{id} |
Official inspection report |
record(id) |
GET /api/record/{id} |
Accident & ownership record |
diagnosis(id) |
GET /api/diagnosis/{id} |
Encar diagnosis (엔카진단): outer panels replaced or not, inspector verdict; Encar ids only |
history(id) |
GET /api/history/{id} |
Encar vehicle history (차량이력): inspections, repairs, owner changes, insurance claims, recalls; Encar ids only |
refresh(id) |
POST /api/vehicle/{id}/refresh |
On-demand refresh (daily quota) |
price_check(id) |
GET /api/price-check/{id} |
Market estimate (beta, Scale) |
bulk_vehicles(ids) / bulk_inspections(ids) / bulk_records(ids) / bulk_diagnoses(ids) |
POST /api/.../bulk |
Up to 500 ids (Business/Scale) |
inspection_versions(id) / record_versions(id) |
GET /api/.../versions |
Report history (Business/Scale) |
changes(**params) / iterate_changes(**params) |
GET /api/catalog/changes |
Incremental sync (Business/Scale) |
export_csv(**params) |
GET /api/catalog/export |
Full catalog CSV (Business/Scale); sell_type="lease,rental" for lease/rental |
export_to_file(path, **params) |
GET /api/catalog/export |
Same export streamed to a file: {"path", "bytes", "generated_at"} (start of the change feed) |
makes_csv() / models_csv() / badges_csv() |
GET /api/taxonomy/*.csv |
Reference data |
| Method | Endpoint | Notes |
|---|---|---|
catalog(**params) |
GET /api/catalog |
make, price_min/max (CNY), power_min/max (hp), export_ready, sort, lang (zh, ru), ... |
iterate_catalog(**params) |
GET /api/catalog |
Generator over all pages (up to the 10,000-result depth limit) |
vehicle(id) |
GET /api/vehicle/{id} |
Price history, photos, seller, export status |
vehicle_by_url(url) |
GET /api/vehicle/by-url |
Same record by Che168/Dongchedi listing link (desktop, mobile or app) |
inspection(id) |
GET /api/inspection/{id} |
Accident, flood and fire checks, EV battery data |
specs(trim_id, **params) |
GET /api/specs/{trimId} |
Autohome trim sheet of a Che168 car: cc, kW, hp, torque, emission standard, weights, EV battery |
bulk_vehicles(ids) |
POST /api/vehicle/bulk |
Up to 500 ids |
changes(**params) / iterate_changes(**params) |
GET /api/catalog/changes |
Change feed (Business/Scale) |
export_csv(**params) |
GET /api/catalog/export |
Full catalog CSV; lang="ru" for Russian label columns |
export_to_file(path, **params) |
GET /api/catalog/export |
Same export streamed to a file: {"path", "bytes", "generated_at"} (start of the change feed) |
enums(**params) / models(make) |
GET /api/enums, /api/models |
Filter values (lang="ru" adds nameRu), models per make |
China only? from encarapi import ChinaCarAPI (also available as the
chinacarapi package).
Every failed request raises EnCarAPIError with status and the raw body. A 403 on a
plan-gated endpoint includes an upgrade hint from the API.
from encarapi import EnCarAPIError
try:
client.korea.changes(cursor=0)
except EnCarAPIError as e:
if e.status == 403:
print("Plan does not include this endpoint:", e.body)- Website and pricing: encarapi.com
- Documentation: encarapi.com/documentation
- OpenAPI reference: https://api.encarapi.com/reference
- China data: chinacarapi.com
- Node.js client: https://github.com/ThatMojo/encarapi-node
EnCarAPI is an independent service and not affiliated with Encar, KB Chachacha, K Car, Dongchedi or Che168.
MIT