apiauto-space 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+
8
+ # Environments
9
+ .venv/
10
+ venv/
11
+ .env
12
+ .env.*
13
+
14
+ # Tools
15
+ .pytest_cache/
16
+ .mypy_cache/
17
+ .ruff_cache/
18
+ .coverage
19
+ htmlcov/
20
+
21
+ # Editors and OS
22
+ .idea/
23
+ .vscode/
24
+ .DS_Store
25
+ Thumbs.db
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.1.0] — 2026-10-27
8
+
9
+ First release.
10
+
11
+ ### Added
12
+
13
+ - `Client` and `AsyncClient` for the apiauto.space API: Encar (Korea), Che168 (China),
14
+ Copart and IAAI (USA).
15
+ - Catalog search with typed filters: `cars()`, `iter_cars()` with `max_items`, `car()`.
16
+ - Change feed: `changes()` and `iter_changes()`; `since` takes a `datetime` or an ISO 8601 string.
17
+ - References and usage: `brands()`, `models()`, `dictionaries()`, `markets()`, `usage()`.
18
+ - Live US search: `us_live()`.
19
+ - Exceptions per API error (`BadRequestError`, `AuthenticationError`, `QuotaExceededError`,
20
+ `PermissionDeniedError`, `NotFoundError`, `RateLimitError`, `ServerError`,
21
+ `ServiceUnavailableError`, `APIConnectionError`).
22
+ - Retries on 429 and 503 after `Retry-After`, and on 5xx and network errors with backoff.
23
+ - Quota and rate-limit headers in `client.last_response`.
24
+ - `TypedDict` types for every response and `py.typed`.
25
+
26
+ [0.1.0]: https://github.com/apiauto-space/apiauto-python/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 apiauto.space
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,378 @@
1
+ Metadata-Version: 2.5
2
+ Name: apiauto-space
3
+ Version: 0.1.0
4
+ Summary: Python client for the apiauto.space used-car API: Encar (Korea), Che168 (China), Copart and IAAI (USA) listings in one JSON format
5
+ Project-URL: Homepage, https://apiauto.space/en/
6
+ Project-URL: Documentation, https://apiauto.space/en/docs/
7
+ Project-URL: Repository, https://github.com/apiauto-space/apiauto-python
8
+ Project-URL: Issues, https://github.com/apiauto-space/apiauto-python/issues
9
+ Project-URL: Changelog, https://github.com/apiauto-space/apiauto-python/blob/main/CHANGELOG.md
10
+ Project-URL: Get a demo key, https://t.me/apiautospace_bot?start=demo
11
+ Author: apiauto.space
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Keywords: api client,car auction,car listings,che168,che168 api,chinese cars,copart,copart api,encar,encar api,iaai,korean cars,used cars,vehicle data
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: Internet :: WWW/HTTP
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.10
29
+ Requires-Dist: httpx<1,>=0.27
30
+ Description-Content-Type: text/markdown
31
+
32
+ # apiauto — Python client for the Encar, Che168, Copart and IAAI API
33
+
34
+ [![PyPI version](https://img.shields.io/pypi/v/apiauto-space.svg)](https://pypi.org/project/apiauto-space/)
35
+ [![Python versions](https://img.shields.io/pypi/pyversions/apiauto-space.svg)](https://pypi.org/project/apiauto-space/)
36
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/apiauto-space/apiauto-python/blob/main/LICENSE)
37
+
38
+ `apiauto` is a typed Python client for the [apiauto.space](https://apiauto.space/en/) REST API: used-car
39
+ listings from **Encar** (South Korea), **Che168** (China) and the **Copart** and **IAAI** auctions (USA) in
40
+ one JSON record format, without writing a scraper. Search the catalogs by make, model, year, price and
41
+ mileage, page through whole markets, follow a feed of new cars, sold cars and price changes, and read
42
+ quota headers — from sync or async code. It is an unofficial client: the project is not affiliated with
43
+ Encar, Che168/Autohome, Copart or IAAI; the data comes through the apiauto.space API.
44
+
45
+ - Sync `Client` and `AsyncClient` on top of [httpx](https://www.python-httpx.org/), the only dependency
46
+ - Iterators that follow the cursor for you, with `max_items` that never over-spends the quota
47
+ - Automatic retries on 429 and 503 (honouring `Retry-After`) and on 5xx and network errors
48
+ - One exception class per API error: `QuotaExceededError`, `RateLimitError`, `NotFoundError` ...
49
+ - `TypedDict` types for every response, `py.typed`, checked with `mypy --strict`
50
+ - Python 3.10+
51
+
52
+ ## Install
53
+
54
+ ```bash
55
+ pip install apiauto-space
56
+ ```
57
+
58
+ The distribution is called `apiauto-space` on PyPI; the module you import is `apiauto`.
59
+
60
+ ## Get a key
61
+
62
+ Open the Telegram bot [@apiautospace_bot](https://t.me/apiautospace_bot?start=demo) and take a free demo
63
+ key: one day, 100 data requests, up to 20 cars per request, all three markets. Paid plans are on the
64
+ [pricing page](https://apiauto.space/en/#pricing).
65
+
66
+ Save the key in the `APIAUTO_KEY` environment variable — the client reads it from there:
67
+
68
+ ```bash
69
+ export APIAUTO_KEY="your-key"
70
+ ```
71
+
72
+ Keep the key on your server. The API does not accept browser requests (CORS is off), and a key in page
73
+ code is visible to every visitor.
74
+
75
+ ## Quick start
76
+
77
+ ```python
78
+ import apiauto
79
+
80
+ client = apiauto.Client() # reads APIAUTO_KEY
81
+
82
+ page = client.cars("kr", brand="Kia", model="Sorento", year_min=2021, limit=20)
83
+ for car in page["items"]:
84
+ print(car["id"], car["title"], car["price"]["amount"], car["price"]["currency"])
85
+ ```
86
+
87
+ Markets are `kr` (Encar, Korea), `cn` (Che168, China) and `us` (Copart and IAAI, USA). Every record has
88
+ the same keys in every market; a value the source does not provide is `None`.
89
+
90
+ ## Examples
91
+
92
+ ### Search Encar by make, model, year and price
93
+
94
+ ```python
95
+ page = client.cars(
96
+ "kr",
97
+ brand="Hyundai",
98
+ model="Grandeur",
99
+ year_min=2020,
100
+ year_max=2023,
101
+ price_min=10_000_000, # in the market's currency: KRW for Korea; skips monthly lease payments
102
+ price_max=30_000_000,
103
+ mileage_max=80_000, # km
104
+ sort="-first_seen", # newest listings first (the default)
105
+ limit=20,
106
+ )
107
+ print(page["count"], "cars match") # None when more than 10,000 match
108
+ ```
109
+
110
+ Prices in dollars at the ECB rate work in every market: `price_usd_min=15000, price_usd_max=25000`.
111
+ The full list of filters: `brand`, `model`, `generation` (Korea), `trim`, `year_min`, `year_max`,
112
+ `price_min`, `price_max`, `price_usd_min`, `price_usd_max`, `mileage_max`, `fuel`, `transmission`,
113
+ `body_type`, `has_photos`, `is_active`, `q` (words of the title), `sort`, `limit`, `cursor`.
114
+
115
+ ### Search Che168 (China)
116
+
117
+ ```python
118
+ for car in client.cars("cn", brand="BYD", fuel="electric", has_photos=True, limit=10)["items"]:
119
+ print(car["title"], car["price"]["amount"], "CNY", car["location"]["city"])
120
+ ```
121
+
122
+ ### US auction lots: Copart and IAAI
123
+
124
+ ```python
125
+ for lot in client.cars("us", brand="Toyota", model="Camry", year_min=2020, limit=10)["items"]:
126
+ raw = lot["raw"] # auction-only fields as the source sends them
127
+ print(lot["id"], lot["vin"], lot["mileage_km"], raw.get("damage_main"), raw.get("sale_date"))
128
+ ```
129
+
130
+ Cars that are not in the copy yet can be searched live at the auction data provider (Pro and Max plans,
131
+ a separate monthly counter): `client.us_live(make="Toyota", model="Camry", year_min=2020)`.
132
+
133
+ ### Iterate over all pages
134
+
135
+ ```python
136
+ for car in client.iter_cars("kr", brand="Genesis", limit=100, max_items=1000):
137
+ save(car)
138
+ ```
139
+
140
+ `iter_cars` follows `next_cursor` until the last page. With `max_items` the last request asks only for
141
+ the cars still needed, so the quota is not spent on records that would be thrown away. The demo allows
142
+ `limit` up to 20; paid plans up to 100.
143
+
144
+ ### Get a car by id
145
+
146
+ ```python
147
+ car = client.car("encar:43020893")
148
+ print(car["is_active"], car["listing_url"])
149
+ ```
150
+
151
+ Ids look like `encar:…`, `che168:…`, `copart:…` and `iaai:…` and never change. A sold or removed car
152
+ stays available for 14 days with `is_active=False`.
153
+
154
+ ### Keep a catalog in sync with the change feed
155
+
156
+ ```python
157
+ import time
158
+ from datetime import datetime, timezone
159
+
160
+ since = datetime.now(timezone.utc)
161
+ while True:
162
+ time.sleep(3600) # the market copy is refreshed hourly
163
+ polled_at = datetime.now(timezone.utc)
164
+ for event in client.iter_changes("kr", since, limit=100):
165
+ if event["type"] == "added":
166
+ save(client.car(event["car_id"]))
167
+ elif event["type"] == "disappeared":
168
+ mark_sold(event["car_id"])
169
+ else: # price_drop or price_up
170
+ update_price(event["car_id"], event["new_price"])
171
+ since = polled_at
172
+ ```
173
+
174
+ `since` takes a `datetime` (a naive one is treated as UTC) or an ISO 8601 string. Filter by
175
+ `type="price_drop"` (or a list of types) and `min_pct=5`. The feed comes with the Pro, Max and demo
176
+ plans; each event counts as one record. A complete script is in
177
+ [examples/sync_changes.py](https://github.com/apiauto-space/apiauto-python/blob/main/examples/sync_changes.py).
178
+
179
+ ### References: makes, models, canonical values, freshness
180
+
181
+ These calls are free.
182
+
183
+ ```python
184
+ client.brands("cn") # [{"name": "Mercedes-Benz", "count": 20185}, ...]
185
+ client.models("kr", "Hyundai") # the make's models with car counts
186
+ client.dictionaries()["fuel"] # {"gasoline": ["gasoline", "petrol", ...], "diesel": [...], ...}
187
+ for market in client.markets():
188
+ print(market["market"], market["active_cars"], market["data_age_seconds"], market["stale"])
189
+ ```
190
+
191
+ ### Usage and quota headers
192
+
193
+ The quota counts returned cars (records), not requests; the demo counts data requests.
194
+
195
+ ```python
196
+ usage = client.usage() # free
197
+ print(usage["plan"], usage["quota_used"], "/", usage["quota_limit"], usage["quota_unit"])
198
+
199
+ client.cars("us", limit=5)
200
+ info = client.last_response
201
+ print(info.quota_remaining, info.quota_reset, info.rate_remaining, info.data_age_seconds)
202
+ ```
203
+
204
+ `client.last_response` is a frozen `ResponseInfo` read from the `X-Quota-*`, `X-RateLimit-*` and
205
+ `X-Data-Age-Seconds` headers of the last response; a header that was not sent is `None`.
206
+
207
+ ### Async
208
+
209
+ ```python
210
+ import asyncio
211
+ import apiauto
212
+
213
+
214
+ async def main() -> None:
215
+ async with apiauto.AsyncClient() as client:
216
+ page = await client.cars("cn", brand="Toyota", limit=10)
217
+ async for car in client.iter_cars("kr", brand="Kia", max_items=50):
218
+ print(car["title"])
219
+
220
+
221
+ asyncio.run(main())
222
+ ```
223
+
224
+ `AsyncClient` has the same methods as `Client`; `iter_cars` and `iter_changes` return async iterators.
225
+
226
+ ### Errors and retries
227
+
228
+ ```python
229
+ try:
230
+ car = client.car("encar:1")
231
+ except apiauto.NotFoundError:
232
+ car = None
233
+ except apiauto.QuotaExceededError as error:
234
+ print("quota renews at", error.reset_at)
235
+ except apiauto.ApiautoError as error:
236
+ print(error.status_code, error.code, error.detail)
237
+ ```
238
+
239
+ | HTTP | Exception | Retried |
240
+ | --- | --- | --- |
241
+ | 400 | `BadRequestError` | no |
242
+ | 401 | `AuthenticationError` | no |
243
+ | 402 | `QuotaExceededError` | no |
244
+ | 403 | `PermissionDeniedError` | no |
245
+ | 404 | `NotFoundError` | no |
246
+ | 429 | `RateLimitError` | yes, after `Retry-After` |
247
+ | 500, 502 | `ServerError` | yes, exponential backoff |
248
+ | 503 | `ServiceUnavailableError` | yes, after `Retry-After` |
249
+ | — | `APIConnectionError` (network, timeout) | yes, exponential backoff |
250
+
251
+ All of them derive from `ApiautoError`. Retries follow the
252
+ [API's error guide](https://apiauto.space/en/docs/errors/): `max_retries=2` by default, a wait of at most
253
+ 60 seconds; `Client(max_retries=0)` turns them off. Other options: `Client(api_key=..., base_url=...,
254
+ timeout=30.0, http_client=httpx.Client(...))`.
255
+
256
+ ### Photos
257
+
258
+ `car["photos"]` are signed links that work for about a day. Store the car's `id`, not the links, and
259
+ fetch a fresh record with `client.car(id)` when you need the photos again. Photo requests are free and
260
+ may redirect to the original host, so follow redirects when downloading them.
261
+
262
+ ### Prices are decimal strings
263
+
264
+ Prices, dollar prices and percentages come as strings such as `"23900000.00"` so no precision is lost.
265
+ Convert them with `Decimal`:
266
+
267
+ ```python
268
+ from decimal import Decimal
269
+
270
+ total = sum(Decimal(car["price_usd"]) for car in page["items"] if car["price_usd"])
271
+ ```
272
+
273
+ ## A record
274
+
275
+ A real Encar car from the [documentation](https://apiauto.space/en/docs/record/), shortened: two photo
276
+ links and a few `raw` fields.
277
+
278
+ ```json
279
+ {
280
+ "id": "encar:43020893",
281
+ "market": "kr",
282
+ "source": "encar",
283
+ "source_id": "43020893",
284
+ "title": "2020 Kia Mohave The Master 3.0 4WD 7-seater Masters",
285
+ "brand": "Kia",
286
+ "model": "Mohave",
287
+ "generation": "Mohave The Master",
288
+ "trim": "3.0 4WD 7-seater Masters",
289
+ "year": 2020,
290
+ "mileage_km": 93724,
291
+ "fuel": "diesel",
292
+ "transmission": null,
293
+ "engine_cc": null,
294
+ "body_type": null,
295
+ "color": null,
296
+ "vin": null,
297
+ "price": {"amount": "23900000.00", "currency": "KRW"},
298
+ "price_usd": "18033.96",
299
+ "location": {"country": "KR", "region": "Gyeonggi", "city": null},
300
+ "photos": [
301
+ "https://api.apiauto.space/img/encar:43020893/1.jpg?exp=…&sig=…",
302
+ "https://api.apiauto.space/img/encar:43020893/2.jpg?exp=…&sig=…"
303
+ ],
304
+ "photo_count": 4,
305
+ "listing_url": "https://fem.encar.com/cars/detail/43020893",
306
+ "is_active": true,
307
+ "first_seen_at": "2026-10-26T04:37:32.553458Z",
308
+ "last_seen_at": "2026-10-26T04:37:32.553458Z",
309
+ "disappeared_at": null,
310
+ "updated_at": "2026-10-26T04:37:32.553458Z",
311
+ "source_mode": "replica",
312
+ "raw": {"badge": "디젤 3.0 4WD 7인승", "form_year": "202004", "sell_type": "normal", "has_accident": false}
313
+ }
314
+ ```
315
+
316
+ The fields each source adds are described on the [Encar API](https://apiauto.space/en/encar-api/),
317
+ [Che168 API](https://apiauto.space/en/che168-api/), [Copart API](https://apiauto.space/en/copart-api/) and
318
+ [IAAI API](https://apiauto.space/en/iaai-api/) pages.
319
+
320
+ ## Methods
321
+
322
+ | Method | Endpoint | Returns | Quota |
323
+ | --- | --- | --- | --- |
324
+ | `cars(market, **filters)` | `GET /v1/cars` | `CarsPage`: `items`, `next_cursor`, `count` | per car |
325
+ | `iter_cars(market, *, max_items=None, **filters)` | `GET /v1/cars` | iterator of `Car` | per car |
326
+ | `car(car_id)` | `GET /v1/cars/{id}` | `Car` | 1 |
327
+ | `changes(market, since, *, type, min_pct, limit, cursor)` | `GET /v1/changes` | `ChangesPage` | per event |
328
+ | `iter_changes(market, since, *, type, min_pct, limit, max_items)` | `GET /v1/changes` | iterator of `Change` | per event |
329
+ | `brands(market)` | `GET /v1/references/brands` | list of `{name, count}` | free |
330
+ | `models(market, brand)` | `GET /v1/references/models` | list of `{name, count}` | free |
331
+ | `dictionaries()` | `GET /v1/references/dictionaries` | field → canonical value → synonyms | free |
332
+ | `markets()` | `GET /v1/references/markets` | list of `Market` | free |
333
+ | `usage()` | `GET /v1/usage` | `Usage` | free |
334
+ | `us_live(*, make, model, year_min, year_max, price_max, page)` | `GET /v1/us/live` | list of `Car` | own counter |
335
+
336
+ The API reference: [apiauto.space/en/docs](https://apiauto.space/en/docs/) and the
337
+ [OpenAPI schema](https://api.apiauto.space/openapi.json).
338
+
339
+ ## Other languages
340
+
341
+ | Language | Package | Repository |
342
+ | --- | --- | --- |
343
+ | Python | `pip install apiauto-space` | [apiauto-space/apiauto-python](https://github.com/apiauto-space/apiauto-python) |
344
+ | JavaScript, TypeScript | `npm install apiauto` | [apiauto-space/apiauto-js](https://github.com/apiauto-space/apiauto-js) |
345
+ | curl, PHP and more | — | [apiauto-space/encar-api-examples](https://github.com/apiauto-space/encar-api-examples) |
346
+
347
+ ## По-русски
348
+
349
+ `apiauto` — Python-клиент для API Encar, Che168, Copart и IAAI, без парсинга: объявления о подержанных
350
+ автомобилях из Южной Кореи, Китая и с аукционов США приходят в одном формате JSON через
351
+ [apiauto.space](https://apiauto.space/). Клиент умеет искать по марке, модели, году, цене и пробегу,
352
+ сам проходит по страницам курсором, повторяет запрос при 429 и 503 и работает в синхронном и асинхронном
353
+ коде. Бесплатный демо-ключ на сутки выдаёт Telegram-бот
354
+ [@apiautospace_bot](https://t.me/apiautospace_bot?start=demo); сохраните его в переменной `APIAUTO_KEY`
355
+ и держите на сервере, не в браузере. Документация на русском: [apiauto.space/docs](https://apiauto.space/docs/).
356
+
357
+ ```bash
358
+ pip install apiauto-space
359
+ ```
360
+
361
+ ```python
362
+ import apiauto
363
+
364
+ client = apiauto.Client() # ключ из APIAUTO_KEY
365
+ for car in client.cars("kr", brand="Kia", year_min=2021, limit=20)["items"]:
366
+ print(car["title"], car["price"]["amount"], car["price"]["currency"])
367
+ ```
368
+
369
+ ## License
370
+
371
+ [MIT](https://github.com/apiauto-space/apiauto-python/blob/main/LICENSE) © 2026 apiauto.space
372
+
373
+ ## Disclaimer
374
+
375
+ This is an unofficial client of the apiauto.space API. It is not affiliated with, endorsed by or
376
+ sponsored by Encar, Che168, Autohome, Copart or IAAI; their names are used only to describe where the
377
+ listings come from, and all trademarks belong to their owners. Use of the data is subject to the
378
+ [apiauto.space terms](https://apiauto.space/en/offer/).