encarapi 1.1.0 → 1.2.0
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.
- package/README.md +9 -7
- package/package.json +2 -2
- package/src/china.js +1 -1
- package/src/index.d.ts +18 -2
- package/src/index.js +3 -3
- package/src/korea.js +31 -2
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# EnCarAPI: Node.js client for Korean and Chinese used car data
|
|
2
2
|
|
|
3
|
-
Official **Node.js / TypeScript client** for [EnCarAPI](https://encarapi.com), the REST
|
|
3
|
+
Official **Node.js / TypeScript client** for [EnCarAPI](https://encarapi.com/?utm_source=readme&utm_medium=encarapi-node), the REST
|
|
4
4
|
**Encar API** and **Korean Car API**. One package for:
|
|
5
5
|
|
|
6
6
|
- **Korea:** Encar.com, KB Chachacha (`source: "kbc"`) and K Car (`source: "kcar"`), with
|
|
7
7
|
photos, specs, options, inspection reports, accident and ownership records, condition
|
|
8
8
|
reports, price history, a change feed and full catalog export.
|
|
9
|
-
- **China:** Dongchedi and Che168 in English via [ChinaCarAPI](https://chinacarapi.com)
|
|
9
|
+
- **China:** Dongchedi and Che168 in English via [ChinaCarAPI](https://chinacarapi.com/?utm_source=readme&utm_medium=encarapi-node)
|
|
10
10
|
(separate key or the China add-on for EnCarAPI keys).
|
|
11
11
|
|
|
12
12
|
Built for car exporters, dealers and marketplaces that need reliable used car data without
|
|
13
13
|
scraping, proxies or geo-blocks.
|
|
14
14
|
|
|
15
15
|
> **An API key is required.** The data is a paid service. Get a key (5-day trial) at
|
|
16
|
-
> **[encarapi.com](https://encarapi.com)**.
|
|
16
|
+
> **[encarapi.com](https://encarapi.com/?utm_source=readme&utm_medium=encarapi-node)**.
|
|
17
17
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
@@ -82,7 +82,9 @@ saveCursor(client.china.lastCursor);
|
|
|
82
82
|
|---|---|---|
|
|
83
83
|
| `catalog(params)` | `GET /api/catalog` | Search & list. `source`: `encar` (default), `kbc`, `kcar`, `all` |
|
|
84
84
|
| `iterateCatalog(params)` | `GET /api/catalog` | Async iterator over all pages |
|
|
85
|
+
| `iterateCatalogById(params)` | `GET /api/catalog?sort=id` | Complete, stable traversal (keyset cursor, nothing repeated or skipped) |
|
|
85
86
|
| `leasing(params)` | `GET /api/catalog/leasing` | Lease takeovers and rentals |
|
|
87
|
+
| `iterateLeasingById(params)` | `GET /api/catalog/leasing?sort=id` | Complete, stable traversal of lease/rental listings |
|
|
86
88
|
| `nav(params)` | `GET /api/nav` | Filter facets with counts |
|
|
87
89
|
| `enums(params)` | `GET /api/enums` | Valid values for the flat filters |
|
|
88
90
|
| `modelSearch(search, params)` | `GET /api/model-search` | Model autocomplete |
|
|
@@ -94,7 +96,7 @@ saveCursor(client.china.lastCursor);
|
|
|
94
96
|
| `bulkVehicles(ids)` / `bulkInspections(ids)` / `bulkRecords(ids)` | `POST /api/.../bulk` | Up to 500 ids (Business/Scale) |
|
|
95
97
|
| `inspectionVersions(id)` / `recordVersions(id)` | `GET /api/.../versions` | Report history (Business/Scale) |
|
|
96
98
|
| `changes(params)` / `iterateChanges(params)` | `GET /api/catalog/changes` | Incremental sync (Business/Scale) |
|
|
97
|
-
| `exportCsv()` | `GET /api/catalog/export` | Full catalog CSV (Business/Scale) |
|
|
99
|
+
| `exportCsv(params)` | `GET /api/catalog/export` | Full catalog CSV (Business/Scale); `{ sell_type: "lease,rental" }` for lease/rental |
|
|
98
100
|
| `makesCsv()` / `modelsCsv()` / `badgesCsv()` | `GET /api/taxonomy/*.csv` | Reference data |
|
|
99
101
|
|
|
100
102
|
## China API (`client.china`)
|
|
@@ -128,10 +130,10 @@ try {
|
|
|
128
130
|
|
|
129
131
|
## Links
|
|
130
132
|
|
|
131
|
-
- Website and pricing: https://encarapi.com
|
|
132
|
-
- Documentation: https://encarapi.com/documentation
|
|
133
|
+
- Website and pricing: [encarapi.com](https://encarapi.com/?utm_source=readme&utm_medium=encarapi-node)
|
|
134
|
+
- Documentation: [encarapi.com/documentation](https://encarapi.com/documentation?utm_source=readme&utm_medium=encarapi-node)
|
|
133
135
|
- OpenAPI reference: https://api.encarapi.com/reference
|
|
134
|
-
- China data: https://chinacarapi.com
|
|
136
|
+
- China data: [chinacarapi.com](https://chinacarapi.com/?utm_source=readme&utm_medium=encarapi-node)
|
|
135
137
|
- Python client: https://github.com/ThatMojo/encarapi-python
|
|
136
138
|
|
|
137
139
|
EnCarAPI is an independent service and not affiliated with Encar, KB Chachacha, K Car,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "encarapi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Official Node.js client for EnCarAPI: Korean used car data (Encar, KB Chachacha, K Car) and Chinese used car data (Dongchedi, Che168) in one REST API.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"car export",
|
|
37
37
|
"vehicle data api"
|
|
38
38
|
],
|
|
39
|
-
"homepage": "https://encarapi.com",
|
|
39
|
+
"homepage": "https://encarapi.com/?utm_source=npm&utm_medium=encarapi-node",
|
|
40
40
|
"repository": {
|
|
41
41
|
"type": "git",
|
|
42
42
|
"url": "git+https://github.com/ThatMojo/encarapi-node.git"
|
package/src/china.js
CHANGED
|
@@ -16,7 +16,7 @@ class ChinaClient {
|
|
|
16
16
|
this.http = new HttpClient({
|
|
17
17
|
apiKey,
|
|
18
18
|
baseUrl: options.baseUrl || "https://api.chinacarapi.com",
|
|
19
|
-
signupUrl: "https://chinacarapi.com",
|
|
19
|
+
signupUrl: "https://chinacarapi.com/?utm_source=sdk&utm_medium=encarapi-node&utm_content=error#pricing",
|
|
20
20
|
product: "ChinaCarAPI",
|
|
21
21
|
fetchImpl: options.fetch,
|
|
22
22
|
});
|
package/src/index.d.ts
CHANGED
|
@@ -65,10 +65,17 @@ export interface KoreaCatalogParams {
|
|
|
65
65
|
exclude_prices?: string;
|
|
66
66
|
exclude_duplicates?: Bool;
|
|
67
67
|
min_first_seen?: string;
|
|
68
|
+
/** Region codes, comma = OR (seoul, gyeonggi, busan, ...). Encar listings only for now. */
|
|
69
|
+
region?: string;
|
|
70
|
+
/** Cities, comma = OR: value from korea.enums().city (e.g. "suwon"), English or Korean name. Encar only. */
|
|
71
|
+
city?: string;
|
|
72
|
+
/** Keyset cursor for sort "id": nextAfter of the previous response. */
|
|
73
|
+
after?: string;
|
|
68
74
|
/** Raw Encar q-grammar (alternative to the flat filters). */
|
|
69
75
|
q?: string;
|
|
70
76
|
lang?: "en";
|
|
71
|
-
|
|
77
|
+
/** "id" = stable listing-id order; page with `after` (see iterateCatalogById). */
|
|
78
|
+
sort?: "newest" | "price_asc" | "price_desc" | "mileage_asc" | "mileage_desc" | "year_desc" | "year_asc" | "id";
|
|
72
79
|
page?: number;
|
|
73
80
|
limit?: number;
|
|
74
81
|
count?: Bool;
|
|
@@ -77,6 +84,10 @@ export interface KoreaCatalogParams {
|
|
|
77
84
|
export interface KoreaCatalogResponse {
|
|
78
85
|
Count?: number;
|
|
79
86
|
SearchResults: Json[];
|
|
87
|
+
/** Only with sort "id": pass as `after` for the next page; null on the last page. */
|
|
88
|
+
nextAfter?: string | null;
|
|
89
|
+
/** Only with sort "id": true while more pages follow. */
|
|
90
|
+
hasMore?: boolean;
|
|
80
91
|
[key: string]: any;
|
|
81
92
|
}
|
|
82
93
|
|
|
@@ -115,6 +126,10 @@ export class KoreaClient {
|
|
|
115
126
|
enums(params?: { source?: KoreaSource }): Promise<Json>;
|
|
116
127
|
modelSearch(search: string, params?: { limit?: number; source?: KoreaSource; exclude_duplicates?: Bool; has_photos?: Bool; exclude_prices?: string; min_price?: number }): Promise<Json>;
|
|
117
128
|
iterateCatalog(params?: KoreaCatalogParams): AsyncGenerator<Json, void, unknown>;
|
|
129
|
+
/** Complete, stable traversal (sort=id + after): nothing repeated or skipped while the catalog changes. */
|
|
130
|
+
iterateCatalogById(params?: KoreaCatalogParams): AsyncGenerator<Json, void, unknown>;
|
|
131
|
+
/** iterateCatalogById() for lease-takeover and rental listings. */
|
|
132
|
+
iterateLeasingById(params?: KoreaCatalogParams): AsyncGenerator<Json, void, unknown>;
|
|
118
133
|
/** Ids: Encar id, "kbc:<id>" or "kcar:<id>". */
|
|
119
134
|
vehicle(id: string, params?: { lang?: "en" }): Promise<Json>;
|
|
120
135
|
inspection(id: string): Promise<Json>;
|
|
@@ -130,7 +145,8 @@ export class KoreaClient {
|
|
|
130
145
|
changes(params: KoreaChangesParams): Promise<KoreaChangesResponse>;
|
|
131
146
|
iterateChanges(params: KoreaChangesParams): AsyncGenerator<KoreaChange, void, unknown>;
|
|
132
147
|
/** CSV text. */
|
|
133
|
-
|
|
148
|
+
/** sell_type: "sale" (default), "lease", "rental", a comma list or "all". */
|
|
149
|
+
exportCsv(params?: { source?: KoreaSource; sell_type?: string }): Promise<string>;
|
|
134
150
|
makesCsv(params?: { source?: KoreaSource }): Promise<string>;
|
|
135
151
|
modelsCsv(params?: { source?: KoreaSource }): Promise<string>;
|
|
136
152
|
badgesCsv(params?: { source?: KoreaSource }): Promise<string>;
|
package/src/index.js
CHANGED
|
@@ -23,7 +23,7 @@ class EnCarAPI {
|
|
|
23
23
|
if (!apiKey && !chinaKey) {
|
|
24
24
|
throw new EnCarAPIError(
|
|
25
25
|
"An API key is required. Pass new EnCarAPI('YOUR_KEY') or set ENCARAPI_KEY " +
|
|
26
|
-
"(Korea) / CHINACARAPI_KEY (China). Get a key at https://encarapi.com"
|
|
26
|
+
"(Korea) / CHINACARAPI_KEY (China). Get a key at https://encarapi.com/?utm_source=sdk&utm_medium=encarapi-node&utm_content=error"
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
this._apiKey = apiKey;
|
|
@@ -35,7 +35,7 @@ class EnCarAPI {
|
|
|
35
35
|
get korea() {
|
|
36
36
|
if (!this._korea) {
|
|
37
37
|
if (!this._apiKey) {
|
|
38
|
-
throw new EnCarAPIError("An EnCarAPI key is required for Korean data. Get one at https://encarapi.com");
|
|
38
|
+
throw new EnCarAPIError("An EnCarAPI key is required for Korean data. Get one at https://encarapi.com/?utm_source=sdk&utm_medium=encarapi-node&utm_content=error");
|
|
39
39
|
}
|
|
40
40
|
this._korea = new KoreaClient(this._apiKey, { baseUrl: this._options.baseUrl, fetch: this._options.fetch });
|
|
41
41
|
}
|
|
@@ -68,7 +68,7 @@ class ChinaCarAPI extends ChinaClient {
|
|
|
68
68
|
if (!apiKey) {
|
|
69
69
|
throw new EnCarAPIError(
|
|
70
70
|
"A ChinaCarAPI key is required. Pass new ChinaCarAPI('YOUR_KEY') or set CHINACARAPI_KEY. " +
|
|
71
|
-
"Get a key at https://chinacarapi.com"
|
|
71
|
+
"Get a key at https://chinacarapi.com/?utm_source=sdk&utm_medium=encarapi-node&utm_content=error"
|
|
72
72
|
);
|
|
73
73
|
}
|
|
74
74
|
super(apiKey, options);
|
package/src/korea.js
CHANGED
|
@@ -15,7 +15,7 @@ class KoreaClient {
|
|
|
15
15
|
this.http = new HttpClient({
|
|
16
16
|
apiKey,
|
|
17
17
|
baseUrl: options.baseUrl || "https://api.encarapi.com",
|
|
18
|
-
signupUrl: "https://encarapi.com",
|
|
18
|
+
signupUrl: "https://encarapi.com/?utm_source=sdk&utm_medium=encarapi-node&utm_content=error#pricing",
|
|
19
19
|
product: "EnCarAPI",
|
|
20
20
|
fetchImpl: options.fetch,
|
|
21
21
|
});
|
|
@@ -49,6 +49,32 @@ class KoreaClient {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Complete, stable traversal via sort=id + after (keyset cursor): rows never
|
|
54
|
+
* shift between pages while the catalog changes, so nothing is repeated or
|
|
55
|
+
* skipped. Yields every matching listing once, in listing-id order.
|
|
56
|
+
* for await (const car of korea.iterateCatalogById({ manufacturer: "BMW" })) {}
|
|
57
|
+
*/
|
|
58
|
+
async *iterateCatalogById(params = {}) {
|
|
59
|
+
yield* this._iterateById("/api/catalog", params);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Same as iterateCatalogById() for lease-takeover and rental listings. */
|
|
63
|
+
async *iterateLeasingById(params = {}) {
|
|
64
|
+
yield* this._iterateById("/api/catalog/leasing", params);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async *_iterateById(path, params) {
|
|
68
|
+
const { page, ...rest } = params;
|
|
69
|
+
let query = { ...rest, sort: "id", limit: params.limit || 1000 };
|
|
70
|
+
for (;;) {
|
|
71
|
+
const res = await this.http.request("GET", path, { params: query });
|
|
72
|
+
for (const item of res.SearchResults || []) yield item;
|
|
73
|
+
if (!res.hasMore || !res.nextAfter) return;
|
|
74
|
+
query = { ...query, after: res.nextAfter };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
52
78
|
// -- one vehicle -------------------------------------------------------------
|
|
53
79
|
|
|
54
80
|
/** Full vehicle detail. Ids: Encar id, "kbc:<id>" or "kcar:<id>". */
|
|
@@ -105,7 +131,10 @@ class KoreaClient {
|
|
|
105
131
|
}
|
|
106
132
|
}
|
|
107
133
|
|
|
108
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Full catalog as CSV text. { sell_type: "lease,rental" } for the lease/rental
|
|
136
|
+
* inventory, { source: "kbc" } etc. for other marketplaces.
|
|
137
|
+
*/
|
|
109
138
|
exportCsv(params) { return this.http.request("GET", "/api/catalog/export", { params, text: true }); }
|
|
110
139
|
|
|
111
140
|
// -- reference data -------------------------------------------------------------
|