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 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.1.0",
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
- sort?: "newest" | "price_asc" | "price_desc" | "mileage_asc" | "mileage_desc" | "year_desc" | "year_asc";
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
- exportCsv(params?: { source?: KoreaSource }): Promise<string>;
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
- /** Full catalog as CSV text. */
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 -------------------------------------------------------------