keepa-api 0.3.2 → 0.4.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/dist/index.d.cts CHANGED
@@ -41,6 +41,56 @@ type Marketplace = keyof typeof MARKETPLACE_DOMAINS;
41
41
  type DomainId = (typeof MARKETPLACE_DOMAINS)[Marketplace];
42
42
  declare function resolveDomainId(marketplace: string | undefined): DomainId;
43
43
 
44
+ declare const PRODUCT_PATH = "/product";
45
+ declare const PRODUCT_LIST_CONTEXT = "products.list";
46
+ declare const DEFAULT_DAYS = 1;
47
+ declare const AMAZON_IMAGE_BASE = "https://m.media-amazon.com/images/I";
48
+ declare const KEEPA_NO_DATA_SENTINEL = -1;
49
+ declare const CsvType: {
50
+ readonly AMAZON: 0;
51
+ readonly NEW: 1;
52
+ readonly USED: 2;
53
+ readonly SALES: 3;
54
+ readonly LISTPRICE: 4;
55
+ readonly COLLECTIBLE: 5;
56
+ readonly REFURBISHED: 6;
57
+ readonly NEW_FBM_SHIPPING: 7;
58
+ readonly LIGHTNING_DEAL: 8;
59
+ readonly WAREHOUSE: 9;
60
+ readonly NEW_FBA: 10;
61
+ readonly COUNT_NEW: 11;
62
+ readonly COUNT_USED: 12;
63
+ readonly COUNT_REFURBISHED: 13;
64
+ readonly COUNT_COLLECTIBLE: 14;
65
+ readonly EXTRA_INFO_UPDATES: 15;
66
+ readonly RATING: 16;
67
+ readonly COUNT_REVIEWS: 17;
68
+ readonly BUY_BOX_SHIPPING: 18;
69
+ readonly USED_NEW_SHIPPING: 19;
70
+ readonly USED_VERY_GOOD_SHIPPING: 20;
71
+ readonly USED_GOOD_SHIPPING: 21;
72
+ readonly USED_ACCEPTABLE_SHIPPING: 22;
73
+ readonly COLLECTIBLE_NEW_SHIPPING: 23;
74
+ readonly COLLECTIBLE_VERY_GOOD_SHIPPING: 24;
75
+ readonly COLLECTIBLE_GOOD_SHIPPING: 25;
76
+ readonly COLLECTIBLE_ACCEPTABLE_SHIPPING: 26;
77
+ readonly REFURBISHED_SHIPPING: 27;
78
+ readonly EBAY_NEW_SHIPPING: 28;
79
+ readonly EBAY_USED_SHIPPING: 29;
80
+ readonly TRADE_IN: 30;
81
+ readonly RENTAL: 31;
82
+ readonly BUY_BOX_USED_SHIPPING: 32;
83
+ readonly PRIME_EXCL: 33;
84
+ readonly COUNT_NEW_FBA: 34;
85
+ readonly COUNT_NEW_FBM: 35;
86
+ };
87
+ type CsvType = (typeof CsvType)[keyof typeof CsvType];
88
+ declare const SavingBasisType: {
89
+ readonly LIST_PRICE: 0;
90
+ readonly WAS_PRICE: 1;
91
+ };
92
+ type SavingBasisType = (typeof SavingBasisType)[keyof typeof SavingBasisType];
93
+
44
94
  interface ProductRequestOptions {
45
95
  /** Case-insensitive at runtime. Defaults to 'US'. */
46
96
  marketplace?: Marketplace;
@@ -49,6 +99,9 @@ interface ProductRequestOptions {
49
99
  /** When true, populates `history.price.*` and the scalar `price` / `listPrice`
50
100
  * fields on each returned product. Increases token cost. */
51
101
  history?: boolean;
102
+ /** When true, populates the `stats` namespace (buy-box saving basis, etc.).
103
+ * Increases token cost. */
104
+ stats?: boolean;
52
105
  }
53
106
  interface ProductListParams extends ProductRequestOptions {
54
107
  asins: string[];
@@ -98,6 +151,11 @@ interface KeepaProduct {
98
151
  newPrice: number | null;
99
152
  /** Latest list price / MSRP (csv[4]) in the marketplace's major unit. */
100
153
  listPrice: number | null;
154
+ /** Keepa's monthly-sold estimate from Amazon's "X+ bought in past month"
155
+ * widget. Null when Amazon doesn't show the widget for this ASIN — common
156
+ * for lower-velocity / non-US listings. Distinguishable from a genuine
157
+ * zero, which surfaces as `0`. */
158
+ monthlySold: number | null;
101
159
  history: {
102
160
  price: {
103
161
  /** Empty when `history: false`. Sentinel-filtered. */
@@ -108,6 +166,14 @@ interface KeepaProduct {
108
166
  list: PriceHistoryEntry[];
109
167
  };
110
168
  };
169
+ stats: {
170
+ /** Buy box strikethrough reference price in the marketplace's major unit.
171
+ * Null when `stats: false` was used or Keepa has no saving-basis data. */
172
+ buyBoxSavingBasis: number | null;
173
+ /** Reference type for the strikethrough price (`LIST_PRICE` or `WAS_PRICE`).
174
+ * Null when unavailable. */
175
+ buyBoxSavingBasisType: SavingBasisType | null;
176
+ };
111
177
  }
112
178
 
113
179
  declare class Products extends APIResource {
@@ -116,6 +182,96 @@ declare class Products extends APIResource {
116
182
  retrieve(params: ProductRetrieveParams): Promise<KeepaProduct>;
117
183
  }
118
184
 
185
+ interface CategoryListParams {
186
+ /** Browse-node ids to look up. Sent as a comma-separated list to Keepa. */
187
+ ids: number[];
188
+ /** Case-insensitive at runtime. Defaults to 'US'. */
189
+ marketplace?: Marketplace;
190
+ /** When true, requests Keepa to include ancestor records in the response.
191
+ * Empirically the response shape doesn't change for most categories — the
192
+ * flag is exposed for completeness but most consumers will leave it off
193
+ * (the default) and walk the breadcrumb from the product's `categoryTree`. */
194
+ withParents?: boolean;
195
+ }
196
+ interface CategorySearchParams {
197
+ /** Free-text query (e.g. "dog hat", "yoga mat"). Keepa runs its own
198
+ * category-name fuzzy match. */
199
+ term: string;
200
+ /** Case-insensitive at runtime. Defaults to 'US'. */
201
+ marketplace?: Marketplace;
202
+ }
203
+ interface KeepaCategory {
204
+ catId: number;
205
+ /** Leaf-only name (e.g. "Coats, Jackets & Gilets"). Collides across
206
+ * departments — use `contextFreeName` for the disambiguated form. */
207
+ name: string;
208
+ /** Keepa's pre-built disambiguated label (e.g. "Women's Coats, Jackets &
209
+ * Gilets"). Missing on very old / experimental nodes; callers should fall
210
+ * back to `name`. */
211
+ contextFreeName?: string;
212
+ children: number[] | null;
213
+ /** Parent browse-node id. `0` marks a root category. */
214
+ parent: number;
215
+ productCount: number;
216
+ }
217
+ interface CategorySearchHit {
218
+ catId: number;
219
+ name: string;
220
+ /** Lowest BSR seen for any product currently in the category — a rough
221
+ * proxy for "how competitive is this category at the top". */
222
+ lowestRank: number;
223
+ /** Highest BSR (i.e. weakest top performer) in the category. */
224
+ highestRank: number;
225
+ productCount: number;
226
+ }
227
+
228
+ declare class Categories extends APIResource {
229
+ /**
230
+ * Resolve category metadata by browse-node id. Returns a map keyed by
231
+ * `catId` for easy lookup; missing ids in the response (Keepa silently
232
+ * omits unknown nodes) are simply absent from the result.
233
+ */
234
+ list(params: CategoryListParams): Promise<Record<number, KeepaCategory>>;
235
+ /**
236
+ * Free-text search against Keepa's category index. Hits the
237
+ * `/search?type=category` endpoint — Keepa's `/search` is category-
238
+ * specific despite the generic-sounding path, so it lives here as a
239
+ * Categories method rather than a separate resource.
240
+ *
241
+ * Empty array on "no matches" — Keepa doesn't distinguish that from a
242
+ * missing `categories` field in the response.
243
+ */
244
+ search(params: CategorySearchParams): Promise<CategorySearchHit[]>;
245
+ }
246
+
247
+ interface BestSellerRetrieveParams {
248
+ /** Browse-node id of the category to fetch the best-seller list for. */
249
+ categoryId: number;
250
+ /** Case-insensitive at runtime. Defaults to 'US'. */
251
+ marketplace?: Marketplace;
252
+ /** When true, asks Keepa for the sub-category top list (shorter, faster).
253
+ * Default true — full lists run into thousands of ASINs per category and
254
+ * are rarely what callers want. */
255
+ sublist?: boolean;
256
+ }
257
+ interface KeepaBestSellerList {
258
+ categoryId: number;
259
+ /** Ordered top-to-bottom. May be empty when Keepa has no list for the
260
+ * category — non-leaf nodes and some sparse leaves return null at the
261
+ * response level (see `BestSellers.retrieve` return type). */
262
+ asinList: string[];
263
+ }
264
+
265
+ declare class BestSellers extends APIResource {
266
+ /**
267
+ * Fetch the best-seller list for a category. Returns null when Keepa has
268
+ * no list (typical for non-leaf nodes and sparse leaves) rather than
269
+ * throwing — callers usually want to surface "no data" as a row state,
270
+ * not an error.
271
+ */
272
+ retrieve(params: BestSellerRetrieveParams): Promise<KeepaBestSellerList | null>;
273
+ }
274
+
119
275
  interface ClientOptions {
120
276
  /** Falls back to `process.env.KEEPA_API_KEY` when omitted. */
121
277
  apiKey?: string;
@@ -127,6 +283,8 @@ declare class KeepaClient {
127
283
  readonly baseURL: string;
128
284
  readonly fetch: typeof globalThis.fetch;
129
285
  readonly products: Products;
286
+ readonly categories: Categories;
287
+ readonly bestSellers: BestSellers;
130
288
  /** Latest rate-limit snapshot from Keepa, updated after every response.
131
289
  * Null until the first request completes. */
132
290
  rateLimit: RateLimitInfo | null;
@@ -166,6 +324,16 @@ declare const ASIN_REGEX: RegExp;
166
324
  declare function isValidAsin(value: string): boolean;
167
325
  declare function normalizeAsins(asins: string[]): string[];
168
326
 
327
+ /** Keepa's smallest-currency unit (cents/pence/…) → marketplace's major unit.
328
+ * Keepa scales JPY/INR/BRL by 100 too, so /100 produces the right unit across
329
+ * every supported region. Returns null for missing values, non-finite numbers,
330
+ * and the `-1` no-data sentinel. */
331
+ declare function parsePrice(value: unknown): number | null;
332
+ /** Keepa returns `-1` when Amazon doesn't show the "bought in past month"
333
+ * widget for the ASIN; surface that as null so callers can distinguish "no
334
+ * data" from a genuine zero. */
335
+ declare function parseMonthlySold(value: unknown): number | null;
336
+ declare function parseSavingBasisType(value: unknown): SavingBasisType | null;
169
337
  declare function parsePriceHistory(series: number[] | undefined): PriceHistoryEntry[];
170
338
  /** Stubs for unknown ASINs come back with `title: null`. */
171
339
  declare function isFoundProduct(product: KeepaProduct): boolean;
@@ -180,51 +348,6 @@ declare class ProductNotFoundError extends KeepaError {
180
348
  constructor(asin: string);
181
349
  }
182
350
 
183
- declare const PRODUCT_PATH = "/product";
184
- declare const PRODUCT_LIST_CONTEXT = "products.list";
185
- declare const DEFAULT_DAYS = 1;
186
- declare const AMAZON_IMAGE_BASE = "https://m.media-amazon.com/images/I";
187
- declare const KEEPA_NO_DATA_SENTINEL = -1;
188
- declare const CsvType: {
189
- readonly AMAZON: 0;
190
- readonly NEW: 1;
191
- readonly USED: 2;
192
- readonly SALES: 3;
193
- readonly LISTPRICE: 4;
194
- readonly COLLECTIBLE: 5;
195
- readonly REFURBISHED: 6;
196
- readonly NEW_FBM_SHIPPING: 7;
197
- readonly LIGHTNING_DEAL: 8;
198
- readonly WAREHOUSE: 9;
199
- readonly NEW_FBA: 10;
200
- readonly COUNT_NEW: 11;
201
- readonly COUNT_USED: 12;
202
- readonly COUNT_REFURBISHED: 13;
203
- readonly COUNT_COLLECTIBLE: 14;
204
- readonly EXTRA_INFO_UPDATES: 15;
205
- readonly RATING: 16;
206
- readonly COUNT_REVIEWS: 17;
207
- readonly BUY_BOX_SHIPPING: 18;
208
- readonly USED_NEW_SHIPPING: 19;
209
- readonly USED_VERY_GOOD_SHIPPING: 20;
210
- readonly USED_GOOD_SHIPPING: 21;
211
- readonly USED_ACCEPTABLE_SHIPPING: 22;
212
- readonly COLLECTIBLE_NEW_SHIPPING: 23;
213
- readonly COLLECTIBLE_VERY_GOOD_SHIPPING: 24;
214
- readonly COLLECTIBLE_GOOD_SHIPPING: 25;
215
- readonly COLLECTIBLE_ACCEPTABLE_SHIPPING: 26;
216
- readonly REFURBISHED_SHIPPING: 27;
217
- readonly EBAY_NEW_SHIPPING: 28;
218
- readonly EBAY_USED_SHIPPING: 29;
219
- readonly TRADE_IN: 30;
220
- readonly RENTAL: 31;
221
- readonly BUY_BOX_USED_SHIPPING: 32;
222
- readonly PRIME_EXCL: 33;
223
- readonly COUNT_NEW_FBA: 34;
224
- readonly COUNT_NEW_FBM: 35;
225
- };
226
- type CsvType = (typeof CsvType)[keyof typeof CsvType];
227
-
228
351
  declare const VERSION = "0.1.0";
229
352
 
230
- export { AMAZON_IMAGE_BASE, APIError, APIResource, ASIN_LENGTH, ASIN_REGEX, AuthenticationError, type ClientOptions, CsvType, DEFAULT_DAYS, type DomainId, KEEPA_NO_DATA_SENTINEL, type KeepaCategoryNode, KeepaClient, KeepaError, type KeepaProduct, type KeepaVariation, type KeepaVariationAttribute, MARKETPLACE_DOMAINS, type Marketplace, NetworkError, PRODUCT_LIST_CONTEXT, PRODUCT_PATH, type PriceHistoryEntry, type ProductListParams, ProductNotFoundError, type ProductRequestOptions, type ProductRetrieveParams, Products, RateLimitError, type RateLimitInfo, VERSION, KeepaClient as default, extractBsr, isFoundProduct, isValidAsin, normalizeAsins, parsePriceHistory, resolveDomainId };
353
+ export { AMAZON_IMAGE_BASE, APIError, APIResource, ASIN_LENGTH, ASIN_REGEX, AuthenticationError, type BestSellerRetrieveParams, BestSellers, Categories, type CategoryListParams, type CategorySearchHit, type CategorySearchParams, type ClientOptions, CsvType, DEFAULT_DAYS, type DomainId, KEEPA_NO_DATA_SENTINEL, type KeepaBestSellerList, type KeepaCategory, type KeepaCategoryNode, KeepaClient, KeepaError, type KeepaProduct, type KeepaVariation, type KeepaVariationAttribute, MARKETPLACE_DOMAINS, type Marketplace, NetworkError, PRODUCT_LIST_CONTEXT, PRODUCT_PATH, type PriceHistoryEntry, type ProductListParams, ProductNotFoundError, type ProductRequestOptions, type ProductRetrieveParams, Products, RateLimitError, type RateLimitInfo, SavingBasisType, VERSION, KeepaClient as default, extractBsr, isFoundProduct, isValidAsin, normalizeAsins, parseMonthlySold, parsePrice, parsePriceHistory, parseSavingBasisType, resolveDomainId };
package/dist/index.d.ts CHANGED
@@ -41,6 +41,56 @@ type Marketplace = keyof typeof MARKETPLACE_DOMAINS;
41
41
  type DomainId = (typeof MARKETPLACE_DOMAINS)[Marketplace];
42
42
  declare function resolveDomainId(marketplace: string | undefined): DomainId;
43
43
 
44
+ declare const PRODUCT_PATH = "/product";
45
+ declare const PRODUCT_LIST_CONTEXT = "products.list";
46
+ declare const DEFAULT_DAYS = 1;
47
+ declare const AMAZON_IMAGE_BASE = "https://m.media-amazon.com/images/I";
48
+ declare const KEEPA_NO_DATA_SENTINEL = -1;
49
+ declare const CsvType: {
50
+ readonly AMAZON: 0;
51
+ readonly NEW: 1;
52
+ readonly USED: 2;
53
+ readonly SALES: 3;
54
+ readonly LISTPRICE: 4;
55
+ readonly COLLECTIBLE: 5;
56
+ readonly REFURBISHED: 6;
57
+ readonly NEW_FBM_SHIPPING: 7;
58
+ readonly LIGHTNING_DEAL: 8;
59
+ readonly WAREHOUSE: 9;
60
+ readonly NEW_FBA: 10;
61
+ readonly COUNT_NEW: 11;
62
+ readonly COUNT_USED: 12;
63
+ readonly COUNT_REFURBISHED: 13;
64
+ readonly COUNT_COLLECTIBLE: 14;
65
+ readonly EXTRA_INFO_UPDATES: 15;
66
+ readonly RATING: 16;
67
+ readonly COUNT_REVIEWS: 17;
68
+ readonly BUY_BOX_SHIPPING: 18;
69
+ readonly USED_NEW_SHIPPING: 19;
70
+ readonly USED_VERY_GOOD_SHIPPING: 20;
71
+ readonly USED_GOOD_SHIPPING: 21;
72
+ readonly USED_ACCEPTABLE_SHIPPING: 22;
73
+ readonly COLLECTIBLE_NEW_SHIPPING: 23;
74
+ readonly COLLECTIBLE_VERY_GOOD_SHIPPING: 24;
75
+ readonly COLLECTIBLE_GOOD_SHIPPING: 25;
76
+ readonly COLLECTIBLE_ACCEPTABLE_SHIPPING: 26;
77
+ readonly REFURBISHED_SHIPPING: 27;
78
+ readonly EBAY_NEW_SHIPPING: 28;
79
+ readonly EBAY_USED_SHIPPING: 29;
80
+ readonly TRADE_IN: 30;
81
+ readonly RENTAL: 31;
82
+ readonly BUY_BOX_USED_SHIPPING: 32;
83
+ readonly PRIME_EXCL: 33;
84
+ readonly COUNT_NEW_FBA: 34;
85
+ readonly COUNT_NEW_FBM: 35;
86
+ };
87
+ type CsvType = (typeof CsvType)[keyof typeof CsvType];
88
+ declare const SavingBasisType: {
89
+ readonly LIST_PRICE: 0;
90
+ readonly WAS_PRICE: 1;
91
+ };
92
+ type SavingBasisType = (typeof SavingBasisType)[keyof typeof SavingBasisType];
93
+
44
94
  interface ProductRequestOptions {
45
95
  /** Case-insensitive at runtime. Defaults to 'US'. */
46
96
  marketplace?: Marketplace;
@@ -49,6 +99,9 @@ interface ProductRequestOptions {
49
99
  /** When true, populates `history.price.*` and the scalar `price` / `listPrice`
50
100
  * fields on each returned product. Increases token cost. */
51
101
  history?: boolean;
102
+ /** When true, populates the `stats` namespace (buy-box saving basis, etc.).
103
+ * Increases token cost. */
104
+ stats?: boolean;
52
105
  }
53
106
  interface ProductListParams extends ProductRequestOptions {
54
107
  asins: string[];
@@ -98,6 +151,11 @@ interface KeepaProduct {
98
151
  newPrice: number | null;
99
152
  /** Latest list price / MSRP (csv[4]) in the marketplace's major unit. */
100
153
  listPrice: number | null;
154
+ /** Keepa's monthly-sold estimate from Amazon's "X+ bought in past month"
155
+ * widget. Null when Amazon doesn't show the widget for this ASIN — common
156
+ * for lower-velocity / non-US listings. Distinguishable from a genuine
157
+ * zero, which surfaces as `0`. */
158
+ monthlySold: number | null;
101
159
  history: {
102
160
  price: {
103
161
  /** Empty when `history: false`. Sentinel-filtered. */
@@ -108,6 +166,14 @@ interface KeepaProduct {
108
166
  list: PriceHistoryEntry[];
109
167
  };
110
168
  };
169
+ stats: {
170
+ /** Buy box strikethrough reference price in the marketplace's major unit.
171
+ * Null when `stats: false` was used or Keepa has no saving-basis data. */
172
+ buyBoxSavingBasis: number | null;
173
+ /** Reference type for the strikethrough price (`LIST_PRICE` or `WAS_PRICE`).
174
+ * Null when unavailable. */
175
+ buyBoxSavingBasisType: SavingBasisType | null;
176
+ };
111
177
  }
112
178
 
113
179
  declare class Products extends APIResource {
@@ -116,6 +182,96 @@ declare class Products extends APIResource {
116
182
  retrieve(params: ProductRetrieveParams): Promise<KeepaProduct>;
117
183
  }
118
184
 
185
+ interface CategoryListParams {
186
+ /** Browse-node ids to look up. Sent as a comma-separated list to Keepa. */
187
+ ids: number[];
188
+ /** Case-insensitive at runtime. Defaults to 'US'. */
189
+ marketplace?: Marketplace;
190
+ /** When true, requests Keepa to include ancestor records in the response.
191
+ * Empirically the response shape doesn't change for most categories — the
192
+ * flag is exposed for completeness but most consumers will leave it off
193
+ * (the default) and walk the breadcrumb from the product's `categoryTree`. */
194
+ withParents?: boolean;
195
+ }
196
+ interface CategorySearchParams {
197
+ /** Free-text query (e.g. "dog hat", "yoga mat"). Keepa runs its own
198
+ * category-name fuzzy match. */
199
+ term: string;
200
+ /** Case-insensitive at runtime. Defaults to 'US'. */
201
+ marketplace?: Marketplace;
202
+ }
203
+ interface KeepaCategory {
204
+ catId: number;
205
+ /** Leaf-only name (e.g. "Coats, Jackets & Gilets"). Collides across
206
+ * departments — use `contextFreeName` for the disambiguated form. */
207
+ name: string;
208
+ /** Keepa's pre-built disambiguated label (e.g. "Women's Coats, Jackets &
209
+ * Gilets"). Missing on very old / experimental nodes; callers should fall
210
+ * back to `name`. */
211
+ contextFreeName?: string;
212
+ children: number[] | null;
213
+ /** Parent browse-node id. `0` marks a root category. */
214
+ parent: number;
215
+ productCount: number;
216
+ }
217
+ interface CategorySearchHit {
218
+ catId: number;
219
+ name: string;
220
+ /** Lowest BSR seen for any product currently in the category — a rough
221
+ * proxy for "how competitive is this category at the top". */
222
+ lowestRank: number;
223
+ /** Highest BSR (i.e. weakest top performer) in the category. */
224
+ highestRank: number;
225
+ productCount: number;
226
+ }
227
+
228
+ declare class Categories extends APIResource {
229
+ /**
230
+ * Resolve category metadata by browse-node id. Returns a map keyed by
231
+ * `catId` for easy lookup; missing ids in the response (Keepa silently
232
+ * omits unknown nodes) are simply absent from the result.
233
+ */
234
+ list(params: CategoryListParams): Promise<Record<number, KeepaCategory>>;
235
+ /**
236
+ * Free-text search against Keepa's category index. Hits the
237
+ * `/search?type=category` endpoint — Keepa's `/search` is category-
238
+ * specific despite the generic-sounding path, so it lives here as a
239
+ * Categories method rather than a separate resource.
240
+ *
241
+ * Empty array on "no matches" — Keepa doesn't distinguish that from a
242
+ * missing `categories` field in the response.
243
+ */
244
+ search(params: CategorySearchParams): Promise<CategorySearchHit[]>;
245
+ }
246
+
247
+ interface BestSellerRetrieveParams {
248
+ /** Browse-node id of the category to fetch the best-seller list for. */
249
+ categoryId: number;
250
+ /** Case-insensitive at runtime. Defaults to 'US'. */
251
+ marketplace?: Marketplace;
252
+ /** When true, asks Keepa for the sub-category top list (shorter, faster).
253
+ * Default true — full lists run into thousands of ASINs per category and
254
+ * are rarely what callers want. */
255
+ sublist?: boolean;
256
+ }
257
+ interface KeepaBestSellerList {
258
+ categoryId: number;
259
+ /** Ordered top-to-bottom. May be empty when Keepa has no list for the
260
+ * category — non-leaf nodes and some sparse leaves return null at the
261
+ * response level (see `BestSellers.retrieve` return type). */
262
+ asinList: string[];
263
+ }
264
+
265
+ declare class BestSellers extends APIResource {
266
+ /**
267
+ * Fetch the best-seller list for a category. Returns null when Keepa has
268
+ * no list (typical for non-leaf nodes and sparse leaves) rather than
269
+ * throwing — callers usually want to surface "no data" as a row state,
270
+ * not an error.
271
+ */
272
+ retrieve(params: BestSellerRetrieveParams): Promise<KeepaBestSellerList | null>;
273
+ }
274
+
119
275
  interface ClientOptions {
120
276
  /** Falls back to `process.env.KEEPA_API_KEY` when omitted. */
121
277
  apiKey?: string;
@@ -127,6 +283,8 @@ declare class KeepaClient {
127
283
  readonly baseURL: string;
128
284
  readonly fetch: typeof globalThis.fetch;
129
285
  readonly products: Products;
286
+ readonly categories: Categories;
287
+ readonly bestSellers: BestSellers;
130
288
  /** Latest rate-limit snapshot from Keepa, updated after every response.
131
289
  * Null until the first request completes. */
132
290
  rateLimit: RateLimitInfo | null;
@@ -166,6 +324,16 @@ declare const ASIN_REGEX: RegExp;
166
324
  declare function isValidAsin(value: string): boolean;
167
325
  declare function normalizeAsins(asins: string[]): string[];
168
326
 
327
+ /** Keepa's smallest-currency unit (cents/pence/…) → marketplace's major unit.
328
+ * Keepa scales JPY/INR/BRL by 100 too, so /100 produces the right unit across
329
+ * every supported region. Returns null for missing values, non-finite numbers,
330
+ * and the `-1` no-data sentinel. */
331
+ declare function parsePrice(value: unknown): number | null;
332
+ /** Keepa returns `-1` when Amazon doesn't show the "bought in past month"
333
+ * widget for the ASIN; surface that as null so callers can distinguish "no
334
+ * data" from a genuine zero. */
335
+ declare function parseMonthlySold(value: unknown): number | null;
336
+ declare function parseSavingBasisType(value: unknown): SavingBasisType | null;
169
337
  declare function parsePriceHistory(series: number[] | undefined): PriceHistoryEntry[];
170
338
  /** Stubs for unknown ASINs come back with `title: null`. */
171
339
  declare function isFoundProduct(product: KeepaProduct): boolean;
@@ -180,51 +348,6 @@ declare class ProductNotFoundError extends KeepaError {
180
348
  constructor(asin: string);
181
349
  }
182
350
 
183
- declare const PRODUCT_PATH = "/product";
184
- declare const PRODUCT_LIST_CONTEXT = "products.list";
185
- declare const DEFAULT_DAYS = 1;
186
- declare const AMAZON_IMAGE_BASE = "https://m.media-amazon.com/images/I";
187
- declare const KEEPA_NO_DATA_SENTINEL = -1;
188
- declare const CsvType: {
189
- readonly AMAZON: 0;
190
- readonly NEW: 1;
191
- readonly USED: 2;
192
- readonly SALES: 3;
193
- readonly LISTPRICE: 4;
194
- readonly COLLECTIBLE: 5;
195
- readonly REFURBISHED: 6;
196
- readonly NEW_FBM_SHIPPING: 7;
197
- readonly LIGHTNING_DEAL: 8;
198
- readonly WAREHOUSE: 9;
199
- readonly NEW_FBA: 10;
200
- readonly COUNT_NEW: 11;
201
- readonly COUNT_USED: 12;
202
- readonly COUNT_REFURBISHED: 13;
203
- readonly COUNT_COLLECTIBLE: 14;
204
- readonly EXTRA_INFO_UPDATES: 15;
205
- readonly RATING: 16;
206
- readonly COUNT_REVIEWS: 17;
207
- readonly BUY_BOX_SHIPPING: 18;
208
- readonly USED_NEW_SHIPPING: 19;
209
- readonly USED_VERY_GOOD_SHIPPING: 20;
210
- readonly USED_GOOD_SHIPPING: 21;
211
- readonly USED_ACCEPTABLE_SHIPPING: 22;
212
- readonly COLLECTIBLE_NEW_SHIPPING: 23;
213
- readonly COLLECTIBLE_VERY_GOOD_SHIPPING: 24;
214
- readonly COLLECTIBLE_GOOD_SHIPPING: 25;
215
- readonly COLLECTIBLE_ACCEPTABLE_SHIPPING: 26;
216
- readonly REFURBISHED_SHIPPING: 27;
217
- readonly EBAY_NEW_SHIPPING: 28;
218
- readonly EBAY_USED_SHIPPING: 29;
219
- readonly TRADE_IN: 30;
220
- readonly RENTAL: 31;
221
- readonly BUY_BOX_USED_SHIPPING: 32;
222
- readonly PRIME_EXCL: 33;
223
- readonly COUNT_NEW_FBA: 34;
224
- readonly COUNT_NEW_FBM: 35;
225
- };
226
- type CsvType = (typeof CsvType)[keyof typeof CsvType];
227
-
228
351
  declare const VERSION = "0.1.0";
229
352
 
230
- export { AMAZON_IMAGE_BASE, APIError, APIResource, ASIN_LENGTH, ASIN_REGEX, AuthenticationError, type ClientOptions, CsvType, DEFAULT_DAYS, type DomainId, KEEPA_NO_DATA_SENTINEL, type KeepaCategoryNode, KeepaClient, KeepaError, type KeepaProduct, type KeepaVariation, type KeepaVariationAttribute, MARKETPLACE_DOMAINS, type Marketplace, NetworkError, PRODUCT_LIST_CONTEXT, PRODUCT_PATH, type PriceHistoryEntry, type ProductListParams, ProductNotFoundError, type ProductRequestOptions, type ProductRetrieveParams, Products, RateLimitError, type RateLimitInfo, VERSION, KeepaClient as default, extractBsr, isFoundProduct, isValidAsin, normalizeAsins, parsePriceHistory, resolveDomainId };
353
+ export { AMAZON_IMAGE_BASE, APIError, APIResource, ASIN_LENGTH, ASIN_REGEX, AuthenticationError, type BestSellerRetrieveParams, BestSellers, Categories, type CategoryListParams, type CategorySearchHit, type CategorySearchParams, type ClientOptions, CsvType, DEFAULT_DAYS, type DomainId, KEEPA_NO_DATA_SENTINEL, type KeepaBestSellerList, type KeepaCategory, type KeepaCategoryNode, KeepaClient, KeepaError, type KeepaProduct, type KeepaVariation, type KeepaVariationAttribute, MARKETPLACE_DOMAINS, type Marketplace, NetworkError, PRODUCT_LIST_CONTEXT, PRODUCT_PATH, type PriceHistoryEntry, type ProductListParams, ProductNotFoundError, type ProductRequestOptions, type ProductRetrieveParams, Products, RateLimitError, type RateLimitInfo, SavingBasisType, VERSION, KeepaClient as default, extractBsr, isFoundProduct, isValidAsin, normalizeAsins, parseMonthlySold, parsePrice, parsePriceHistory, parseSavingBasisType, resolveDomainId };