scrapeunblocker 0.14.1 → 0.15.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fcf8149d49a7e9bea7fe09d3cd6641adcd1915580280238579a9af68819162da
4
- data.tar.gz: 74f6c9109e659f7d84231f723160a8f708ad5031d40a768c0e4d203e2f84d95c
3
+ metadata.gz: 3f62e1ad297dc0829c5aad20c1c09208595bb459b733b87d862c585fe64e12e5
4
+ data.tar.gz: 9ce26e1db98023c5373d90229f0f9545fc11f71f28ba143e82710305aab6a811
5
5
  SHA512:
6
- metadata.gz: 02ebb973075735fd92af9ef53a23e000216122cf1a7d9550f65435a8e925f988660305aeed2f77c0c1f2f593849b9b3ed978e52b374830b571ea88075d7b0649
7
- data.tar.gz: 371d0ce6d960436f84264fe9e0111797870e658764be0f8ec8538614dbf8546a1379c02dcc82d7315f4e4cac358ba4a6ffbd76313ef14e660b9c8ef72a25f601
6
+ metadata.gz: 150e385abf9ae36cde1653f4c411ab43d3735f6267fb6c8aab24d43caf37aa3da4dd8c7f6833dd6b207f45b374d01e6112f4a27e4a7007b4bcba2c812ce83b3e
7
+ data.tar.gz: 2a7c1c9b01e7821b80dfd590e650a2766b56ecccaa28ae16c1b219f6ce2022d7b4d33c2108e33eed17319f794e9c052dbe6977a00964a201b15853fdadeefbf2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.15.1 (2026-10-29)
4
+
5
+ - `shopee_product` and `shopee_search` docs: describe `availability` and `inStock`, sold-out variations (`models[].available`, `tierIndex`), `categories` as the breadcrumb path plus `browseCategories`, `isPixPrice` (Brazilian Pix prices) and the JSON `{"status": "not_found", ...}` answer for a product that does not exist. Signatures are unchanged.
6
+
7
+ ## 0.15.0 (2026-10-29)
8
+
9
+ - 23 new plugin methods on `Client`:
10
+ - Groceries: `kroger_search(query)` and `kroger_product(product)` (Kroger and its 18 banner stores such as Ralphs and Fred Meyer, priced for one store), `instacart_search(query, retailer: ...)` and `instacart_product(product)` (any retailer on Instacart, priced for a ZIP code), `publix_search(query)` and `publix_product(product)`.
11
+ - E-commerce: `amazon_offers` (every seller's offer for a product, by `asin:` or `url:`), `amazon_seller` (a seller's public profile and feedback, by `seller_id:` or `url:`), `costco_search(query)` and `costco_product(product)`, `flipkart_search(query)` and `flipkart_product(product_id)`, `lazada_search(query)` and `lazada_product(item_id)` (six markets, `country:`), `allegro_search(query)` and `allegro_product(offer_id)`, `falabella_search(query)` and `falabella_product` (Chile, Colombia and Peru), `magazineluiza_search(query)` and `magazineluiza_product`.
12
+ - Google: `google_ai_mode(q)` (the AI Mode answer with the pages it cites) and `google_lens(url)` (Google Lens visual matches for an image URL).
13
+ - AI: `gemini(prompt)` (Google Gemini's answer with its citations and sources).
14
+ - The first argument of `flipkart_product`, `lazada_product`, `allegro_product`, `falabella_search` and `magazineluiza_search` is optional, for calls that pass `url:` instead. `falabella_product` and `magazineluiza_product` take `product_id:` or `url:`.
15
+ - `instacart_search` needs `retailer:` (the retailer's Instacart slug, e.g. `"costco"`); the API answers 400 without it. `instacart_product` takes it too, unless `product` is a link that names the retailer.
16
+ - `shein_search` docs: results also carry `reviewCountText`, and the price filters apply to `price`.
17
+
18
+ No breaking changes.
19
+
3
20
  ## 0.14.1 (2026-10-25)
4
21
 
5
22
  - `serp(captcha_pause:)` docs: the API ignores `captcha_pause`; it is kept for compatibility. CAPTCHAs are handled server-side by retrying from other exits.
data/README.md CHANGED
@@ -149,11 +149,12 @@ jobs = su.career_site_jobs("https://jobs.ashbyhq.com/ramp", keyword: "engineer")
149
149
  | Category | Methods |
150
150
  | --- | --- |
151
151
  | Travel | `skyscanner.flights`, `skyscanner.hotels`, `skyscanner.carhire`, `booking_hotels`, `booking_reviews`, `airbnb_search`, `airbnb_room`, `airbnb_reviews`, `tripadvisor_list`, `tripadvisor_reviews` |
152
- | Google | `google_local`, `google_images`, `maps_place_details`, `google_trends`, `google_shopping`, `google_jobs`, `google_hotels`, `google_hotels_property`, `google_news`, `google_ads_transparency`, `google_ads_transparency_advertisers` |
152
+ | Google | `google_local`, `google_images`, `maps_place_details`, `google_trends`, `google_shopping`, `google_jobs`, `google_hotels`, `google_hotels_property`, `google_news`, `google_ads_transparency`, `google_ads_transparency_advertisers`, `google_ai_mode`, `google_lens` |
153
+ | AI | `gemini` |
153
154
  | Search engines | `bing_search`, `bing_images`, `duckduckgo_search`, `yahoo_search`, `yahoo_images`, `brave_search`, `brave_images` |
154
- | E-commerce | `amazon_product`, `amazon_search`, `amazon_bestsellers`, `amazon_reviews`, `walmart_search`, `target_search`, `bestbuy_search`, `homedepot_search`, `etsy_search`, `shein_search`, `zalando_search`, `aliexpress_search`, `alibaba_search`, `alibaba_product`, `alibaba1688_search`, `alibaba1688_product`, `tiktok_shop_search`, `tiktok_shop_product`, `tiktok_shop_store`, `oopbuy_search`, `smyths_inventory`, `smyths_details`, `smyths_search`, `smyths_category`, `wish_search`, `wish_product`, `shopee_search`, `shopee_product`, `taobao_search`, `taobao_product` |
155
+ | E-commerce | `amazon_product`, `amazon_search`, `amazon_bestsellers`, `amazon_reviews`, `amazon_offers`, `amazon_seller`, `walmart_search`, `target_search`, `bestbuy_search`, `homedepot_search`, `costco_search`, `costco_product`, `etsy_search`, `shein_search`, `zalando_search`, `aliexpress_search`, `alibaba_search`, `alibaba_product`, `alibaba1688_search`, `alibaba1688_product`, `tiktok_shop_search`, `tiktok_shop_product`, `tiktok_shop_store`, `oopbuy_search`, `smyths_inventory`, `smyths_details`, `smyths_search`, `smyths_category`, `wish_search`, `wish_product`, `shopee_search`, `shopee_product`, `taobao_search`, `taobao_product`, `flipkart_search`, `flipkart_product`, `lazada_search`, `lazada_product`, `allegro_search`, `allegro_product`, `falabella_search`, `falabella_product`, `magazineluiza_search`, `magazineluiza_product` |
155
156
  | Classifieds & Resale | `ebay_search`, `vinted_search`, `leboncoin_search` |
156
- | Groceries | `woolworths_search`, `woolworths_category`, `woolworths_specials`, `woolworths_product`, `coles_search`, `coles_category`, `coles_specials`, `coles_product`, `aldi_au_search`, `aldi_au_category`, `aldi_au_specials`, `aldi_au_product` |
157
+ | Groceries | `woolworths_search`, `woolworths_category`, `woolworths_specials`, `woolworths_product`, `coles_search`, `coles_category`, `coles_specials`, `coles_product`, `aldi_au_search`, `aldi_au_category`, `aldi_au_specials`, `aldi_au_product`, `kroger_search`, `kroger_product`, `instacart_search`, `instacart_product`, `publix_search`, `publix_product` |
157
158
  | Real estate | `zillow_search`, `zillow_detail`, `redfin_search`, `redfin_detail`, `rightmove_search`, `idealista_search`, `idealista_detail`, `immobilienscout24_search` |
158
159
  | Vehicles | `mobile_de`, `autoscout24_search` |
159
160
  | Jobs | `indeed_jobs`, `glassdoor_jobs`, `glassdoor_reviews`, `glassdoor_salaries`, `naukri_jobs`, `wellfound_jobs`, `wellfound_job`, `wellfound_company`, `wellfound_company_search`, `career_site_jobs`, `upwork_jobs` |
@@ -105,6 +105,102 @@ module ScrapeUnblocker
105
105
  })
106
106
  end
107
107
 
108
+ # Scrape every offer (all sellers) for an Amazon product.
109
+ #
110
+ # Returns the offers Amazon lists for a product under "All buying options", as JSON. Each
111
+ # offer has the seller (`sellerName`, `sellerId`, `sellerUrl`, `sellerRating`,
112
+ # `sellerRatingCount`, `sellerPositivePercent`), `price` + `currency`, `listPrice` and
113
+ # `savingsPercent`, `shippingPrice` and `totalPrice`, `condition` (as Amazon words it) with
114
+ # `conditionType` (new, used, collectible, refurbished) and the seller's `conditionNote`,
115
+ # `delivery` / `deliveryDate`, `alternativeDelivery` (the faster or Prime-member option
116
+ # Amazon also offers), `shipsFrom`, `soldByAmazon`, `fulfilledByAmazon`, `prime`, the
117
+ # `offerId` and a `buyBoxWinner` flag. The response also carries the product `title`,
118
+ # `rating`, `reviewCount`, `image` and `totalOffers`. Page 1 starts with the Buy Box
119
+ # (featured) offer, also returned as `buyBox`; Amazon shows it whatever filter is set.
120
+ # Amazon lists 10 other offers per page: follow `nextPage`, or read up to 5 pages in one
121
+ # call with `pages`. Filter with `condition`, `free_shipping` and `prime`. `shippingPrice`
122
+ # is 0 for free delivery and null when Amazon shows no delivery promise for the location.
123
+ # `proxy_country` defaults to the marketplace's home country so prices and delivery are the
124
+ # ones a local shopper sees; the proxy that served the request is reported as `exitCountry`
125
+ # and `exitProvider`.
126
+ #
127
+ # @param asin [String] Amazon product id (10-char ASIN, e.g. 'B07VGRJDFY'; books use their
128
+ # ISBN-10). Give either `asin` (with `marketplace`) or a product `url`.
129
+ # @param url [String] Full Amazon product url, e.g. 'https://www.amazon.de/dp/1847941834'.
130
+ # The ASIN and marketplace are read from it.
131
+ # @param marketplace [String] Regional Amazon site whose offers to list. Prices, sellers and
132
+ # delivery differ per marketplace. One of: amazon.com, amazon.co.uk, amazon.de, amazon.fr,
133
+ # amazon.it, amazon.es, amazon.nl, amazon.se, amazon.pl, amazon.com.be, amazon.com.tr,
134
+ # amazon.ca, amazon.com.mx, amazon.com.br, amazon.co.jp, amazon.in, amazon.ae, amazon.sa,
135
+ # amazon.sg, amazon.com.au. API default: amazon.com.
136
+ # @param condition [String] Only offers in this condition: all, new, used, used_like_new,
137
+ # used_very_good, used_good, used_acceptable. Default 'all'. API default: all.
138
+ # @param free_shipping [Boolean] Only offers with free delivery. API default: false.
139
+ # @param prime [Boolean] Only Prime-eligible offers. API default: false.
140
+ # @param page [Integer] 1-based offers page to start at. Amazon lists 10 offers per page
141
+ # (page 1 also carries the Buy Box offer and the product header). API default: 1.
142
+ # @param pages [Integer] How many consecutive pages to read in this call (1-5). Reading
143
+ # stops early at the last page. Each page delivered is billed as one request. API default:
144
+ # 1.
145
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'US',
146
+ # 'DE'). Defaults to the marketplace's home country, so prices, shipping costs and delivery
147
+ # dates are the ones a local shopper sees.
148
+ # Calls POST /marketplace/amazon-offers.
149
+ def amazon_offers(asin: nil, url: nil, marketplace: nil, condition: nil, free_shipping: nil, prime: nil, page: nil, pages: nil, proxy_country: nil)
150
+ post_json("/marketplace/amazon-offers", {
151
+ asin: asin,
152
+ url: url,
153
+ marketplace: marketplace,
154
+ condition: condition,
155
+ free_shipping: free_shipping,
156
+ prime: prime,
157
+ page: page,
158
+ pages: pages,
159
+ proxy_country: proxy_country,
160
+ })
161
+ end
162
+
163
+ # Scrape an Amazon seller profile.
164
+ #
165
+ # Returns an Amazon third-party seller's public profile as JSON: `name`, `logo`,
166
+ # `storefrontUrl`, the headline `rating`, `ratingCount` and `positivePercent` Amazon shows
167
+ # (the last 12 months, or lifetime for a seller with few recent ratings; `ratingPeriod` says
168
+ # which), and `feedbackSummary` for `30d`, `90d`, `12m` and `lifetime` - each with the
169
+ # average `rating`, `ratingCount`, `positivePercent` / `neutralPercent` / `negativePercent`
170
+ # (4-5, 3 and 1-2 stars), the matching counts and the 5..1-star histogram. Also `about` (the
171
+ # seller's own description), `businessName`, `businessAddress` (+ `businessAddressLines`),
172
+ # `businessDetails` (every row of Amazon's "Detailed Seller Information", e.g. business
173
+ # type, trade register and VAT number where the marketplace shows them), `contactUrl` and
174
+ # `recentFeedback` (each with `rating`, `text`, `author`, `date`, `struckThrough` and
175
+ # `amazonMessage` when Amazon took responsibility for the order). Identify the seller with
176
+ # `seller_id` (plus `marketplace`), e.g. the `sellerId` of an offer from
177
+ # `/marketplace/amazon-offers`, or with any Amazon `url` naming the seller. Ratings are per
178
+ # marketplace. Amazon's own retail sellers show no ratings; those fields are null.
179
+ #
180
+ # @param seller_id [String] Amazon seller (merchant) id, e.g. 'A3PEV2IAOYFKM6' - the
181
+ # `sellerId` of an offer from /marketplace/amazon-offers. Give either `seller_id` (with
182
+ # `marketplace`) or a `url`.
183
+ # @param url [String] Any Amazon url that names the seller: the profile
184
+ # ('https://www.amazon.de/sp?seller=A1J6G6R957S11K'), a storefront ('/s?me=...') or a sold-
185
+ # by link. The seller id and marketplace are read from it.
186
+ # @param marketplace [String] Regional Amazon site the seller sells on. Ratings and feedback
187
+ # are per marketplace. One of: amazon.com, amazon.co.uk, amazon.de, amazon.fr, amazon.it,
188
+ # amazon.es, amazon.nl, amazon.se, amazon.pl, amazon.com.be, amazon.com.tr, amazon.ca,
189
+ # amazon.com.mx, amazon.com.br, amazon.co.jp, amazon.in, amazon.ae, amazon.sa, amazon.sg,
190
+ # amazon.com.au. API default: amazon.com.
191
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'US',
192
+ # 'DE'). Optional: a seller profile is the same from every country, so by default any
193
+ # country is used.
194
+ # Calls POST /marketplace/amazon-seller.
195
+ def amazon_seller(seller_id: nil, url: nil, marketplace: nil, proxy_country: nil)
196
+ post_json("/marketplace/amazon-seller", {
197
+ seller_id: seller_id,
198
+ url: url,
199
+ marketplace: marketplace,
200
+ proxy_country: proxy_country,
201
+ })
202
+ end
203
+
108
204
  # Scrape Walmart product search.
109
205
  #
110
206
  # Searches Walmart for `query` and returns the products as JSON: `name`, `price` and
@@ -221,6 +317,72 @@ module ScrapeUnblocker
221
317
  })
222
318
  end
223
319
 
320
+ # Scrape Costco product search.
321
+ #
322
+ # Searches Costco (costco.com, or costco.ca with `domain=ca`) for `query` and returns
323
+ # `products`: `productId`, `itemNumber`, `title`, `brand`, `url`, `image`, `price` (and
324
+ # `priceMax` for a price range), `wasPrice`, `currency`, `promotions`, `marketingStatement`,
325
+ # `rating`, `reviewCount`, `memberOnly`, `onlineOnly`, `warehouseOnly`, `badges`,
326
+ # `availability`, `inWarehouse`, `shipIt`, `buyableOnline`, `priceShownInCartOnly`, `model`,
327
+ # `features`, `category`, `categoryPath` and `variantCount`, plus `totalResults`. A keyword
328
+ # Costco sends to a category page (e.g. 'tv') returns that category, named in
329
+ # `redirectedTo`. `availability` is for the delivery location in `deliveryLocation`. Sort
330
+ # with `sort`, start at `page` and collect up to `max_results`.
331
+ #
332
+ # @param query [String] What to search for, e.g. 'paper towels', 'kirkland coffee' or 'tv'.
333
+ # @param sort [String] Result order: relevance, price_low, price_high, rating, newest,
334
+ # brand. API default: relevance.
335
+ # @param page [Integer] 1-based results page to start from (24 products per page, as on
336
+ # costco.com). API default: 1.
337
+ # @param max_results [Integer] Products to collect from `page` on, 1-240. Up to 120 are
338
+ # billed as one request. API default: 24.
339
+ # @param include_out_of_stock [Boolean] Also return products that are out of stock (Costco
340
+ # hides them by default). API default: false.
341
+ # @param domain [String] Costco storefront: com, ca ('com' is costco.com, 'ca' is
342
+ # costco.ca). API default: com.
343
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'US').
344
+ # Leave blank to let the API pick. Costco computes `availability` for the delivery location
345
+ # it derives from the visitor; it is named in `deliveryLocation`.
346
+ # Calls POST /marketplace/costco-search.
347
+ def costco_search(query, sort: nil, page: nil, max_results: nil, include_out_of_stock: nil, domain: nil, proxy_country: nil)
348
+ post_json("/marketplace/costco-search", {
349
+ query: query,
350
+ sort: sort,
351
+ page: page,
352
+ max_results: max_results,
353
+ include_out_of_stock: include_out_of_stock,
354
+ domain: domain,
355
+ proxy_country: proxy_country,
356
+ })
357
+ end
358
+
359
+ # Scrape a Costco product.
360
+ #
361
+ # Returns one Costco product (costco.com, or costco.ca with `domain=ca`) as JSON:
362
+ # `productId`, `itemNumber`, `title`, `brand`, `url`, `price`, `discount`, `currency`,
363
+ # `rating`, `reviewCount`, `memberOnly`, `onlineOnly`, `buyable`, `inWarehouse`, `shipIt`,
364
+ # `description`, `details`, `features`, `marketingStatement`, `promotionalStatement`,
365
+ # `shipping`, `manufacturer`, `manufacturerPartNumber`, `upc`, `minQuantity`, `maxQuantity`,
366
+ # `categoryPath`, `specifications`, `images`, `options` (e.g. every color and size) and
367
+ # `variants` (item number, option values, UPC). `product` takes a product id, an item number
368
+ # or a product link.
369
+ #
370
+ # @param product [String] A Costco product id ('100361434'), item number ('17767') or
371
+ # product link (https://www.costco.com/p/-/kirkland-signature-colombian-coffee-dark-
372
+ # roast-3-lbs/100361434).
373
+ # @param domain [String] Costco storefront: com, ca ('com' is costco.com, 'ca' is
374
+ # costco.ca). Defaults to the link's storefront, else 'com'.
375
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'US').
376
+ # Leave blank to let the API pick.
377
+ # Calls POST /marketplace/costco-product.
378
+ def costco_product(product, domain: nil, proxy_country: nil)
379
+ post_json("/marketplace/costco-product", {
380
+ product: product,
381
+ domain: domain,
382
+ proxy_country: proxy_country,
383
+ })
384
+ end
385
+
224
386
  # Scrape Etsy product search.
225
387
  #
226
388
  # Searches Etsy for `query` and returns the products as JSON: `title`, `price` + `currency`
@@ -258,9 +420,10 @@ module ScrapeUnblocker
258
420
  #
259
421
  # Searches SHEIN (US store) for `query` and returns the products as JSON: `id`, `sku`,
260
422
  # `title`, `url`, `price`, `originalPrice`, `currency`, `discountPercent`, `rating`,
261
- # `reviewCount`, `category`, `image`, `soldOut` and `storeCode`. Order with `sort`, filter
262
- # with `min_price`/`max_price`. Returns SHEIN's first results page (up to 20 products);
263
- # SHEIN does not serve deeper pages to automated clients.
423
+ # `reviewCount`, `reviewCountText`, `category`, `image`, `soldOut` and `storeCode`. Order
424
+ # with `sort`, filter with `min_price`/`max_price` (enforced on `price`). Returns SHEIN's
425
+ # first results page (up to 20 products); SHEIN does not serve deeper pages to automated
426
+ # clients.
264
427
  #
265
428
  # @param query [String] What to search, e.g. 'summer dress' or 'phone case'.
266
429
  # @param sort [String] Ordering: relevance, price_low, price_high. API default: relevance.
@@ -630,15 +793,18 @@ module ScrapeUnblocker
630
793
  # Searches a Shopee market (Singapore, Indonesia, Malaysia, Thailand, Vietnam, Philippines,
631
794
  # Taiwan or Brazil - `country`) for `q` and returns one page of results: up to 40
632
795
  # `products`, each with `position`, `itemId`, `shopId`, `name`, `url`, `image`, `price`,
633
- # `originalPrice` (when Shopee shows the crossed-out price), `discountPercent`, `currency`,
634
- # `rating` (average stars), `location` (where the seller ships from) and `labels` (promotion
635
- # badges). `sort` orders the results, `min_price` / `max_price` filter them, `shop_id` keeps
636
- # one shop's products (or lists the shop without `q`), and `url` takes a Shopee search or
637
- # category link instead. Shopee shows one page per search, so run several sort orders or
638
- # price ranges for more products. `price` is what Shopee shows a signed-out shopper, which
639
- # can be a promotional or new-customer price (see `labels`). Not available: stock, units
640
- # sold, rating counts and reviews (Shopee does not publish them on the pages these endpoints
641
- # read). One unit per call.
796
+ # `originalPrice` (when Shopee shows the crossed-out price), `discountPercent`,
797
+ # `isPixPrice`, `currency`, `rating` (average stars), `location` (where the seller ships
798
+ # from) and `labels` (promotion badges). `sort` orders the results, `min_price` /
799
+ # `max_price` filter them, `shop_id` keeps one shop's products (or lists the shop without
800
+ # `q`), and `url` takes a Shopee search or category link instead. Shopee shows one page per
801
+ # search, so run several sort orders or price ranges for more products. `price` is what
802
+ # Shopee shows a signed-out shopper, which can be a promotional or new-customer price (see
803
+ # `labels`). In Brazil a card often shows the Pix price (`isPixPrice: true`): `price` is
804
+ # then the Pix price and `discountPercent` is measured against the list price, which Shopee
805
+ # does not show on such a card, so `originalPrice` is null. Not available: per-variation
806
+ # prices, stock counts, units sold, rating counts and reviews (Shopee does not publish them
807
+ # on the pages these endpoints read). One unit per call.
642
808
  #
643
809
  # @param q [String] What to search for, e.g. 'iphone case' or 'air fryer'. Optional with
644
810
  # `shop_id`.
@@ -678,14 +844,22 @@ module ScrapeUnblocker
678
844
  # (average stars), `labels`, `images`, `shopLocation` and `shop` (`name`, `username`, `url`,
679
845
  # `rating`, `ratingCount`, `followers`, `itemCount`, `responseRatePercent`,
680
846
  # `isOfficialShop`, `isPreferredPlusSeller`, `location`, `joinedAt`). With `dataLevel: full`
681
- # it also has `description`, `brand`, `condition`, `categories`, `videos`, `variations`
682
- # (option names), `models` (`modelId`, `name`), `attributes`, `isOfficialShop`,
683
- # `isPreOrder`, `estimatedShippingDays` and `createdAt`; `dataLevel: meta` means Shopee
684
- # showed only the product's title and main image (some markets), with price, rating and shop
685
- # still included. `price` is what Shopee shows a signed-out shopper, which can be a
686
- # promotional or new-customer price (see `labels`). Not available: stock, units sold, rating
687
- # counts and reviews (Shopee does not publish them on the pages these endpoints read). One
688
- # unit per call.
847
+ # it also has `availability` (`in_stock`, `sold_out` or `unavailable` for a withdrawn
848
+ # listing) and `inStock`, `description`, `brand`, `condition`, `categories` (the path the
849
+ # product page's breadcrumb shows, with ids), `browseCategories` (the browse tree, whose ids
850
+ # Shopee's category links use), `videos`, `variations` (option names), `models` (`modelId`,
851
+ # `name`, `available` - false for a sold-out variation - and `tierIndex`, the model's option
852
+ # in each variation), `attributes`, `isOfficialShop`, `isPreOrder`, `estimatedShippingDays`
853
+ # and `createdAt`; `dataLevel: meta` means Shopee showed only the product's title and main
854
+ # image (some markets), with price, rating and shop still included. A product Shopee says
855
+ # does not exist (removed, or never existed) answers 404 with JSON `{"status": "not_found",
856
+ # ...}`; a 502 means the call could not get an answer from Shopee and can be retried.
857
+ # `price` is what Shopee shows a signed-out shopper, which can be a promotional or new-
858
+ # customer price (see `labels`). In Brazil a card often shows the Pix price (`isPixPrice:
859
+ # true`): `price` is then the Pix price and `discountPercent` is measured against the list
860
+ # price, which Shopee does not show on such a card, so `originalPrice` is null. Not
861
+ # available: per-variation prices, stock counts, units sold, rating counts and reviews
862
+ # (Shopee does not publish them on the pages these endpoints read). One unit per call.
689
863
  #
690
864
  # @param url [String] A Shopee product link, e.g. https://shopee.sg/Beats-
691
865
  # iPhone-17-Case-i.442800909.40270108836 or
@@ -777,6 +951,341 @@ module ScrapeUnblocker
777
951
  })
778
952
  end
779
953
 
954
+ # Scrape Flipkart product search.
955
+ #
956
+ # Searches Flipkart (India) for `query` and returns `products`, each with `productId` (the
957
+ # FSN, e.g. MOBHQV9YCUG4ZQZK), `itemId`, `listingId`, `title`, `brand`, `price`,
958
+ # `originalPrice` (MRP when discounted), `discountPercent`, `currency` (INR), `rating`,
959
+ # `ratingCount`, `reviewCount`, `assured` (F-Assured), `sponsored`, `badge` (e.g.
960
+ # Bestseller), `availability` / `inStock`, `keySpecs`, `category`, `image`, `images` and
961
+ # `url`. Also `total` (results Flipkart reports), `totalPages` and `nextPage`. `sort` orders
962
+ # the results, `min_price` / `max_price` filter by price, `page` picks the first page and
963
+ # `max_results` collects across pages. One unit per Flipkart page read.
964
+ #
965
+ # @param query [String] What to search for, e.g. 'iphone 17' or 'running shoes'.
966
+ # @param sort [String] Result order: relevance, popularity, price_asc, price_desc, newest.
967
+ # API default: relevance.
968
+ # @param page [Integer] First Flipkart results page to read (1-100). API default: 1.
969
+ # @param min_price [Integer] Lowest price in rupees (INR).
970
+ # @param max_price [Integer] Highest price in rupees (INR).
971
+ # @param max_results [Integer] Products to collect across pages, 1-240. A page holds 24
972
+ # products (40 in fashion grids); each page is one unit. API default: 24.
973
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'US',
974
+ # 'DE'). Leave blank to let the API pick.
975
+ # Calls POST /marketplace/flipkart-search.
976
+ def flipkart_search(query, sort: nil, page: nil, min_price: nil, max_price: nil, max_results: nil, proxy_country: nil)
977
+ post_json("/marketplace/flipkart-search", {
978
+ query: query,
979
+ sort: sort,
980
+ page: page,
981
+ min_price: min_price,
982
+ max_price: max_price,
983
+ max_results: max_results,
984
+ proxy_country: proxy_country,
985
+ })
986
+ end
987
+
988
+ # Scrape a Flipkart product.
989
+ #
990
+ # Returns one Flipkart (India) product by `product_id` (FSN or `itm` id) or `url`:
991
+ # `productId`, `itemId`, `listingId`, `title`, `brand`, `description`, `category`,
992
+ # `breadcrumbs`, `price`, `originalPrice` (MRP), `discountPercent`, `bestPrice` (with bank
993
+ # offers), `currency`, `availability` / `inStock`, `rating`, `ratingCount`, `reviewCount`,
994
+ # `ratingBreakdown` (1-5 stars), `aspectRatings` (e.g. Camera, Battery), `assured`,
995
+ # `selectedAttributes`, `highlights`, `specifications` (group / name / value), `images`,
996
+ # `variants` (dimension, value, productId, price, available, url), `seller` (name, id,
997
+ # rating, years on Flipkart), `offerCount`, `offers` (bank and exchange offers),
998
+ # `deliveryDays`, `cashOnDelivery`, `returnPolicy`, `reviews` (top reviews) and `url`. One
999
+ # unit per call.
1000
+ #
1001
+ # @param product_id [String] Flipkart product id: the FSN (`pid`, e.g. MOBHQV9YCUG4ZQZK) or
1002
+ # the `itm...` id from a product link.
1003
+ # @param url [String] A Flipkart product link, e.g. https://www.flipkart.com/apple-
1004
+ # iphone-17-black-256-gb/p/itmc61b40459dd98?pid=MOBHQV9YCUG4ZQZK. Used instead of
1005
+ # `product_id`.
1006
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'US',
1007
+ # 'DE'). Leave blank to let the API pick.
1008
+ # Calls POST /marketplace/flipkart-product.
1009
+ def flipkart_product(product_id = nil, url: nil, proxy_country: nil)
1010
+ post_json("/marketplace/flipkart-product", {
1011
+ product_id: product_id,
1012
+ url: url,
1013
+ proxy_country: proxy_country,
1014
+ })
1015
+ end
1016
+
1017
+ # Scrape Lazada product search.
1018
+ #
1019
+ # Searches a Lazada market (Singapore, Malaysia, Thailand, Philippines, Vietnam or Indonesia
1020
+ # - `country`) for `query` and returns `products`, each with `position`, `itemId`, `skuId`,
1021
+ # `name`, `price`, `priceText`, `originalPrice` (crossed-out price), `discountPercent`,
1022
+ # `rating`, `reviewCount`, `sold` (units sold, as Lazada rounds it) and `soldText`, `brand`,
1023
+ # `seller` / `sellerId`, `location` (ships from), `inStock`, `lazMall`, `freeShipping`,
1024
+ # `sponsored`, `image` and `url`. Also `total` (results Lazada lets you page through),
1025
+ # `matches` (all matches it reports), `currency` and `nextPage`. `sort` orders the results,
1026
+ # `min_price` / `max_price` filter by price, `page` picks the first page and `max_results`
1027
+ # collects across pages (40 a page). One unit per Lazada page read.
1028
+ #
1029
+ # @param query [String] What to search for, e.g. 'iphone case' or 'air fryer'.
1030
+ # @param country [String] Lazada market: 'sg' (lazada.sg), 'my' (lazada.com.my), 'th'
1031
+ # (lazada.co.th), 'ph' (lazada.com.ph), 'vn' (lazada.vn), 'id' (lazada.co.id). Default 'sg'.
1032
+ # Prices are in that market's currency.
1033
+ # @param sort [String] Result order: relevance, price_asc, price_desc. API default:
1034
+ # relevance.
1035
+ # @param page [Integer] First Lazada results page to read (1-102). API default: 1.
1036
+ # @param min_price [Float] Lowest price, in the market's currency.
1037
+ # @param max_price [Float] Highest price, in the market's currency.
1038
+ # @param max_results [Integer] Products to collect across pages, 1-400. A page holds 40
1039
+ # products; each page is one unit. API default: 40.
1040
+ # Calls POST /marketplace/lazada-search.
1041
+ def lazada_search(query, country: nil, sort: nil, page: nil, min_price: nil, max_price: nil, max_results: nil)
1042
+ post_json("/marketplace/lazada-search", {
1043
+ query: query,
1044
+ country: country,
1045
+ sort: sort,
1046
+ page: page,
1047
+ min_price: min_price,
1048
+ max_price: max_price,
1049
+ max_results: max_results,
1050
+ })
1051
+ end
1052
+
1053
+ # Scrape a Lazada product.
1054
+ #
1055
+ # Returns one Lazada product by `url`, or by `item_id` (+ optional `sku_id`) and `country`:
1056
+ # `itemId`, `skuId`, `title`, `brand`, `description`, `highlights`, `categories`, `price`,
1057
+ # `priceText`, `originalPrice`, `discountPercent`, `currency`, `rating`, `reviewCount`,
1058
+ # `ratingBreakdown` (1-5 stars), `inStock`, `maxQuantity`, `images`, `specifications`,
1059
+ # `variantDimensions` (e.g. colour and storage with their values), `variants` (every SKU
1060
+ # with its attributes, price, image, availability and link), `seller` (id, shop id, name,
1061
+ # positive rating, store highlights, link), `delivery` (option and estimate), `warranties`
1062
+ # and `url`. One unit per call.
1063
+ #
1064
+ # @param item_id [String] Lazada item id (the number after '-i' in a product link, e.g.
1065
+ # 13907853520).
1066
+ # @param url [String] A Lazada product link, e.g. https://www.lazada.sg/products/apple-
1067
+ # iphone-18-pro-max-i13907853520-s124925162667.html. Used instead of `item_id` / `country`.
1068
+ # @param sku_id [String] Optional SKU id (the number after '-s') to select one variant.
1069
+ # @param country [String] Lazada market: 'sg' (lazada.sg), 'my' (lazada.com.my), 'th'
1070
+ # (lazada.co.th), 'ph' (lazada.com.ph), 'vn' (lazada.vn), 'id' (lazada.co.id). Default 'sg'.
1071
+ # Prices are in that market's currency.
1072
+ # Calls POST /marketplace/lazada-product.
1073
+ def lazada_product(item_id = nil, url: nil, sku_id: nil, country: nil)
1074
+ post_json("/marketplace/lazada-product", {
1075
+ item_id: item_id,
1076
+ url: url,
1077
+ sku_id: sku_id,
1078
+ country: country,
1079
+ })
1080
+ end
1081
+
1082
+ # Scrape Allegro search.
1083
+ #
1084
+ # Searches Allegro (Poland) for `query` and returns `products` (offers), each with
1085
+ # `position`, `offerId`, `productId`, `title`, `price`, `currency` (PLN),
1086
+ # `priceWithDelivery`, `deliveryFrom`, `freeDelivery`, `smart` (Allegro Smart!),
1087
+ # `deliveryLabel`, `rating`, `ratingCount`, `recentBuyers` (bought in the last 30 days),
1088
+ # `offersOfProduct`, `quantity`, `sponsored`, `promoted`, `superSeller`, `seller` (id,
1089
+ # login, company, positive feedback %), `parameters`, `image`, `images` and `url`. Also
1090
+ # `total` (all matches), `available` (offers Allegro lets you page through), `lastPage`,
1091
+ # `category` and `nextPage`. Filter with `min_price` / `max_price`, `condition`,
1092
+ # `shipping_from`, `delivery_time`, `offer_type`, `store_city` and `store_region`; `sort`
1093
+ # orders the results; `page` and `max_results` page through them. One unit per Allegro page
1094
+ # read.
1095
+ #
1096
+ # @param query [String] What to search for, e.g. 'iphone 15' or 'lego'.
1097
+ # @param sort [String] Result order: relevance, price_asc, price_desc,
1098
+ # price_with_delivery_asc, price_with_delivery_desc, popularity, ending_soon, newest,
1099
+ # rating. API default: relevance.
1100
+ # @param page [Integer] First Allegro results page to read (1-100). API default: 1.
1101
+ # @param min_price [Float] Lowest price in zloty (PLN).
1102
+ # @param max_price [Float] Highest price in zloty (PLN).
1103
+ # @param condition [String] Item condition: new, like_new, refurbished, used, damaged.
1104
+ # @param shipping_from [String] Where the item ships from: pl, cz, sk, hu, cn, eu,
1105
+ # allegro_warehouse.
1106
+ # @param delivery_time [String] Delivery speed: one_day, two_days, three_days (one_day =
1107
+ # tomorrow).
1108
+ # @param offer_type [String] Offer type: buy_now, auction (buy_now = fixed price).
1109
+ # @param store_city [String] The seller's town, e.g. 'Kraków'.
1110
+ # @param store_region [String] The seller's voivodeship, e.g. 'mazowieckie' or 'slaskie' (or
1111
+ # its number 1-16).
1112
+ # @param max_results [Integer] Offers to collect across pages, 1-300. A page holds about 60
1113
+ # offers; each page is one unit. API default: 60.
1114
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'DE',
1115
+ # 'CZ'). Leave blank to let the API pick (recommended: Allegro refuses some countries'
1116
+ # exits).
1117
+ # Calls POST /marketplace/allegro-search.
1118
+ def allegro_search(query, sort: nil, page: nil, min_price: nil, max_price: nil, condition: nil, shipping_from: nil, delivery_time: nil, offer_type: nil, store_city: nil, store_region: nil, max_results: nil, proxy_country: nil)
1119
+ post_json("/marketplace/allegro-search", {
1120
+ query: query,
1121
+ sort: sort,
1122
+ page: page,
1123
+ min_price: min_price,
1124
+ max_price: max_price,
1125
+ condition: condition,
1126
+ shipping_from: shipping_from,
1127
+ delivery_time: delivery_time,
1128
+ offer_type: offer_type,
1129
+ store_city: store_city,
1130
+ store_region: store_region,
1131
+ max_results: max_results,
1132
+ proxy_country: proxy_country,
1133
+ })
1134
+ end
1135
+
1136
+ # Scrape an Allegro offer.
1137
+ #
1138
+ # Returns one Allegro (Poland) offer by `offer_id` or `url`: `offerId`, `productId`,
1139
+ # `title`, `productName`, `brand`, `gtin`, `price`, `originalPrice`, `currency`,
1140
+ # `sellingMode` (buy_now / auction), `available` (stock), `condition`, `rating`,
1141
+ # `ratingCount`, `ratingBreakdown` (1-5 stars), `recentBuyers`, `category`, `breadcrumbs`,
1142
+ # `parameters` (group / name / value), `description`, `images`, `variants`, `seller` (id,
1143
+ # login, Super Seller, feedback, company name and tax id), `location`, `smart`, `delivery`,
1144
+ # `shippingMethods` (name, price, Smart! price), `returnDays`, `warranty`, `endsAt`,
1145
+ # `reviews` and `url`. One unit per call.
1146
+ #
1147
+ # @param offer_id [String] Allegro offer number, e.g. 16680923539 (the number at the end of
1148
+ # an /oferta/ link or its offerId).
1149
+ # @param url [String] An Allegro offer or product link, e.g.
1150
+ # https://allegro.pl/oferta/16680923539 or
1151
+ # https://allegro.pl/produkt/...?offerId=16680923539. Used instead of `offer_id`.
1152
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'DE',
1153
+ # 'CZ'). Leave blank to let the API pick (recommended: Allegro refuses some countries'
1154
+ # exits).
1155
+ # Calls POST /marketplace/allegro-product.
1156
+ def allegro_product(offer_id = nil, url: nil, proxy_country: nil)
1157
+ post_json("/marketplace/allegro-product", {
1158
+ offer_id: offer_id,
1159
+ url: url,
1160
+ proxy_country: proxy_country,
1161
+ })
1162
+ end
1163
+
1164
+ # Scrape Falabella product search (Chile, Colombia, Peru).
1165
+ #
1166
+ # Searches a Falabella store (`country`: cl, co or pe) for `query` and returns the products
1167
+ # as JSON: `productId`, `skuId`, `title`, `brand`, `url`, `price` (and `priceMax` for a
1168
+ # price range), `cmrPrice` (price with the Falabella CMR card), `originalPrice` (crossed-out
1169
+ # list price), `discountPercent`, `currency`, `rating`, `reviewCount`, `sellerId` /
1170
+ # `sellerName`, `sponsored`, `bestSeller`, `freeShipping`, `deliveryLabels`, `badges`,
1171
+ # `installmentsWithoutInterest`, `image` / `images`, `topSpecifications` and `variants`
1172
+ # (colour / size options). Sort with `sort`, start at `page`, and collect up to
1173
+ # `max_results` across pages (48 per page, each page billed as one request, see
1174
+ # `pagesFetched`). `url` takes a Falabella search link instead of `query`.
1175
+ #
1176
+ # @param query [String] What to search, e.g. 'zapatillas nike', 'televisor 55' or
1177
+ # 'notebook'.
1178
+ # @param country [String] Falabella store: 'cl' (www.falabella.com, prices in CLP), 'co'
1179
+ # (www.falabella.com.co, prices in COP), 'pe' (www.falabella.com.pe, prices in PEN). Default
1180
+ # 'cl'.
1181
+ # @param sort [String] Order: relevance, price_low, price_high, rating, brand. API default:
1182
+ # relevance.
1183
+ # @param page [Integer] 1-based results page to start from (1-200). API default: 1.
1184
+ # @param max_results [Integer] Products to collect across pages (1-480); Falabella serves 48
1185
+ # per page and each page fetched is billed as one request. API default: 48.
1186
+ # @param url [String] Instead of `query`: a Falabella search link, e.g.
1187
+ # https://www.falabella.com/falabella-cl/search?Ntt=zapatillas (its store, sort and page are
1188
+ # used).
1189
+ # Calls POST /marketplace/falabella-search.
1190
+ def falabella_search(query = nil, country: nil, sort: nil, page: nil, max_results: nil, url: nil)
1191
+ post_json("/marketplace/falabella-search", {
1192
+ query: query,
1193
+ country: country,
1194
+ sort: sort,
1195
+ page: page,
1196
+ max_results: max_results,
1197
+ url: url,
1198
+ })
1199
+ end
1200
+
1201
+ # Scrape one Falabella product's full detail.
1202
+ #
1203
+ # Fetches one Falabella product (by `product_id` + `country`, or `url`) and returns it as
1204
+ # JSON: `productId`, `skuId`, `url`, `title`, `brand`, `description` / `longDescription`,
1205
+ # `price` / `priceMax`, `cmrPrice`, `originalPrice`, `discountPercent`, `currency`,
1206
+ # `available`, `stockUnits` (only when Falabella shows a low-stock count), `rating`,
1207
+ # `reviewCount`, `sellerId` / `sellerName`, `freeShipping`, `deliveryLabels`, `badges`,
1208
+ # `installmentsWithoutInterest`, `breadcrumbs`, `topSpecifications`, `specifications` (name
1209
+ # / value), `images`, `videos`, `returnPolicy` and `variants` (every colour / size SKU with
1210
+ # its own price, availability, delivery options and images). The top-level price fields are
1211
+ # those of `sku` (default the main variant). Companion to `/marketplace/falabella-search`.
1212
+ #
1213
+ # @param product_id [String] Falabella product id, e.g. '17776888' or 'prod11930024' (the
1214
+ # part after /product/ in a product link). Needs `country` unless it is 'cl'.
1215
+ # @param url [String] Instead of `product_id`: a Falabella product link, e.g.
1216
+ # https://www.falabella.com/falabella-cl/product/17776888/iphone-duo-256gb-night-
1217
+ # sky/17776887 (its store and SKU are used).
1218
+ # @param sku [String] Variant (SKU) whose price and availability fill the top-level fields;
1219
+ # default the product's main variant. Every variant is listed in `variants` either way.
1220
+ # @param country [String] With `product_id`: Falabella store: 'cl' (www.falabella.com,
1221
+ # prices in CLP), 'co' (www.falabella.com.co, prices in COP), 'pe' (www.falabella.com.pe,
1222
+ # prices in PEN). Default 'cl'.
1223
+ # Calls POST /marketplace/falabella-product.
1224
+ def falabella_product(product_id: nil, url: nil, sku: nil, country: nil)
1225
+ post_json("/marketplace/falabella-product", {
1226
+ product_id: product_id,
1227
+ url: url,
1228
+ sku: sku,
1229
+ country: country,
1230
+ })
1231
+ end
1232
+
1233
+ # Scrape Magazine Luiza (Magalu) product search.
1234
+ #
1235
+ # Searches Magazine Luiza (magazineluiza.com.br, Brazil) for `query` and returns the
1236
+ # products as JSON: `productId`, `offerId`, `title`, `brand`, `url`, `price`, `bestPrice`
1237
+ # with `bestPriceMethod` (usually the Pix price), `originalPrice` (list price),
1238
+ # `discountPercent`, `currency` (BRL), `installments` (count, amount, interest-free),
1239
+ # `rating`, `reviewCount`, `sellerId` / `sellerName`, `available`, `sponsored`,
1240
+ # `magaluIndica`, `category` / `subcategory`, `badges`, `image` and `variations`. Sort with
1241
+ # `sort`, start at `page`, and collect up to `max_results` across pages (40 per page, each
1242
+ # page billed as one request, see `pagesFetched`). `url` takes a Magalu search link instead
1243
+ # of `query`.
1244
+ #
1245
+ # @param query [String] What to search, e.g. 'geladeira', 'smart tv 55' or 'air fryer'.
1246
+ # @param sort [String] Order: relevance, price_low, price_high, best_selling, rating,
1247
+ # newest, fastest_delivery, price_with_shipping_low. API default: relevance.
1248
+ # @param page [Integer] 1-based results page to start from (1-25). API default: 1.
1249
+ # @param max_results [Integer] Products to collect across pages (1-200); Magalu serves 40
1250
+ # per page and each page fetched is billed as one request. API default: 40.
1251
+ # @param url [String] Instead of `query`: a Magazine Luiza search link, e.g.
1252
+ # https://www.magazineluiza.com.br/busca/geladeira/ (its sort and page are used).
1253
+ # Calls POST /marketplace/magazineluiza-search.
1254
+ def magazineluiza_search(query = nil, sort: nil, page: nil, max_results: nil, url: nil)
1255
+ post_json("/marketplace/magazineluiza-search", {
1256
+ query: query,
1257
+ sort: sort,
1258
+ page: page,
1259
+ max_results: max_results,
1260
+ url: url,
1261
+ })
1262
+ end
1263
+
1264
+ # Scrape one Magazine Luiza (Magalu) product's full detail.
1265
+ #
1266
+ # Fetches one Magazine Luiza product (by `product_id` or `url`) and returns it as JSON:
1267
+ # `productId`, `offerId`, `url`, `title`, `brand`, `reference`, `description`,
1268
+ # `keySellingPoints`, `price`, `bestPrice` / `bestPriceMethod`, `originalPrice`,
1269
+ # `discountPercent`, `currency` (BRL), `installments`, `available`, `rating`, `reviewCount`,
1270
+ # `commentCount`, `ratingHistogram`, `ratingAspects`, `reviews` (first page: rating, text,
1271
+ # author, date, photos), `seller` (id, name, legal name, city / state, seller since, score,
1272
+ # total sales, answer and delivery rate), `coupon`, `badges`, `breadcrumbs`,
1273
+ # `specifications` (group / name / value), `variations` (colour, size, capacity... with the
1274
+ # product id of each option), `images`, `videos` and package `dimensions`. Companion to
1275
+ # `/marketplace/magazineluiza-search`.
1276
+ #
1277
+ # @param product_id [String] Magalu product id, e.g. '240354400' or 'jc60hbaa6d' (the part
1278
+ # after /p/ in a product link).
1279
+ # @param url [String] Instead of `product_id`: a Magazine Luiza product link, e.g.
1280
+ # https://www.magazineluiza.com.br/notebook-acer-aspire-go/p/240354400/in/nota/.
1281
+ # Calls POST /marketplace/magazineluiza-product.
1282
+ def magazineluiza_product(product_id: nil, url: nil)
1283
+ post_json("/marketplace/magazineluiza-product", {
1284
+ product_id: product_id,
1285
+ url: url,
1286
+ })
1287
+ end
1288
+
780
1289
  # Scrape Vinted product search.
781
1290
  #
782
1291
  # Searches Vinted for `query` and returns the listings as JSON: `title`, `price` and
@@ -1300,6 +1809,228 @@ module ScrapeUnblocker
1300
1809
  })
1301
1810
  end
1302
1811
 
1812
+ # Search Kroger and its banner stores (Ralphs, Fred Meyer, King Soopers, ...).
1813
+ #
1814
+ # Products matching `query` on Kroger or one of its banner sites (`banner`: kroger, ralphs,
1815
+ # fredmeyer, kingsoopers, frysfood, smithsfoodanddrug, qfc, dillons, bakersplus, citymarket,
1816
+ # gerbes, foodfourless, harristeeter, marianos, metromarket, picknsave, jaycfoods, payless,
1817
+ # foodsco), priced for one store. Returns `products` in the site's order with `productId`
1818
+ # (Kroger's 13-digit id), `upc`, `gtin14`, `name`, `brand`, `size`, `url`, `image`, `price`
1819
+ # (what the shopper pays now), `regularPrice`, `salePrice`, `onSale`, `savings`, `promotion`
1820
+ # (`type` sale / price_drop / multibuy / must_buy / bogo, `text`, multibuy `quantity` and
1821
+ # `quantityPrice`, `expiresAt`), `unitPrice` (`price`, `unit`, `text`), `soldBy` (unit /
1822
+ # weight; weighed items carry Kroger's per-item estimate), `available`, `stockLevel` (HIGH /
1823
+ # LOW / TEMPORARILY_OUT_OF_STOCK), `maxOrderQuantity`, `fulfillment` (`pickup`, `delivery`,
1824
+ # `inStore`, `ship`: whether the store offers the product that way), `aisle` (`number`,
1825
+ # `description`, `side`, `bay`, `shelf`), `rating`, `reviewCount`, `categories`,
1826
+ # `snapEligible` and `offers` (extra deals such as 'Save $2 each when you buy 3'), plus
1827
+ # `position` and `sponsored`. `totalResults` is the site's total match count; one call
1828
+ # returns the first page (up to 24 products). `brand` narrows the search to one or more
1829
+ # brands. `store` names the store the prices are for: `storeId`, `name`, `banner`,
1830
+ # `address`, `latitude`, `longitude`, `fulfillmentMethods`, `hours`, `pricesFor` (pickup or
1831
+ # inStore) and `selectedBy` (store_id / zip / default); with `zip`, `nearbyStores` lists the
1832
+ # banner's other stores near it. Prices are in USD and depend on the store. One billed unit
1833
+ # per call.
1834
+ #
1835
+ # @param query [String] What to search for, e.g. 'milk', 'coca cola', 'gluten free bread'.
1836
+ # @param banner [String] Which Kroger-family store site to use: 'kroger' (Kroger), 'ralphs'
1837
+ # (Ralphs), 'fredmeyer' (Fred Meyer), 'kingsoopers' (King Soopers), 'frysfood' (Fry's Food),
1838
+ # 'smithsfoodanddrug' (Smith's), 'qfc' (QFC), 'dillons' (Dillons), 'bakersplus' (Baker's),
1839
+ # 'citymarket' (City Market), 'gerbes' (Gerbes), 'foodfourless' (Food 4 Less),
1840
+ # 'harristeeter' (Harris Teeter), 'marianos' (Mariano's), 'metromarket' (Metro Market),
1841
+ # 'picknsave' (Pick 'n Save), 'jaycfoods' (Jay C Food Stores), 'payless' (Pay Less),
1842
+ # 'foodsco' (FoodsCo). Host names (ralphs.com) and common spellings (food4less, smiths,
1843
+ # frys) work too. API default: kroger.
1844
+ # @param brand [String] Only products of these brands, e.g. 'Kroger' or 'Kroger|Simple
1845
+ # Truth' (separate several with | or ,).
1846
+ # @param store_id [String] Kroger store id (8 digits, e.g. '01400929'): prices, stock and
1847
+ # aisles are that store's. The id is shown as `store.storeId` in every answer and in
1848
+ # `nearbyStores` when you search by `zip`.
1849
+ # @param zip [String] US ZIP code: prices for the banner's nearest store to it that takes
1850
+ # online orders (the nearest store at all when none does). The answer lists the nearby
1851
+ # stores in `nearbyStores`. Leave both `store_id` and `zip` blank for the banner's default
1852
+ # store.
1853
+ # @param max_results [Integer] Products to return, 1-24 (the first page of results). API
1854
+ # default: 24.
1855
+ # Calls POST /groceries/kroger-search.
1856
+ def kroger_search(query, banner: nil, brand: nil, store_id: nil, zip: nil, max_results: nil)
1857
+ post_json("/groceries/kroger-search", {
1858
+ query: query,
1859
+ banner: banner,
1860
+ brand: brand,
1861
+ store_id: store_id,
1862
+ zip: zip,
1863
+ max_results: max_results,
1864
+ })
1865
+ end
1866
+
1867
+ # Kroger product details (Kroger, Ralphs, Fred Meyer, King Soopers, ...).
1868
+ #
1869
+ # One product from Kroger or one of its banner sites, by Kroger product id, UPC / GTIN
1870
+ # barcode or product link, priced for one store. `product` has `productId` (Kroger's
1871
+ # 13-digit id), `upc`, `gtin14`, `name`, `brand`, `size`, `url`, `image`, `price` (what the
1872
+ # shopper pays now), `regularPrice`, `salePrice`, `onSale`, `savings`, `promotion` (`type`
1873
+ # sale / price_drop / multibuy / must_buy / bogo, `text`, multibuy `quantity` and
1874
+ # `quantityPrice`, `expiresAt`), `unitPrice` (`price`, `unit`, `text`), `soldBy` (unit /
1875
+ # weight; weighed items carry Kroger's per-item estimate), `available`, `stockLevel` (HIGH /
1876
+ # LOW / TEMPORARILY_OUT_OF_STOCK), `maxOrderQuantity`, `fulfillment` (`pickup`, `delivery`,
1877
+ # `inStore`, `ship`: whether the store offers the product that way), `aisle` (`number`,
1878
+ # `description`, `side`, `bay`, `shelf`), `rating`, `reviewCount`, `categories`,
1879
+ # `snapEligible` and `offers` (extra deals such as 'Save $2 each when you buy 3'), and the
1880
+ # product page details: `description`, `highlights`, `images` (every angle, large),
1881
+ # `ingredients`, `allergens`, `nutrition` (`servingSize`, `servingsPerContainer`,
1882
+ # `nutrients` with amount, unit and % daily value), `dietary`, `claims`, `organic`,
1883
+ # `glutenFree`, `kosher`, `nonGmo`, `taxonomy` (category paths), `countryOfOrigin`,
1884
+ # `storage`, `dimensions`, `weight`, `ratingBreakdown`, `alcohol` and `ageRestricted`.
1885
+ # `store` names the store the prices are for: `storeId`, `name`, `banner`, `address`,
1886
+ # `latitude`, `longitude`, `fulfillmentMethods`, `hours`, `pricesFor` (pickup or inStore)
1887
+ # and `selectedBy` (store_id / zip / default). One billed unit per call.
1888
+ #
1889
+ # @param product [String] The product: Kroger's 13-digit product id ('0001111041700'), a UPC
1890
+ # or GTIN barcode ('011110417005'), or a product link from any banner
1891
+ # (https://www.kroger.com/p/kroger-2-reduced-fat-milk/0001111041700). A link picks its
1892
+ # banner by itself.
1893
+ # @param banner [String] Which Kroger-family store site to use: 'kroger' (Kroger), 'ralphs'
1894
+ # (Ralphs), 'fredmeyer' (Fred Meyer), 'kingsoopers' (King Soopers), 'frysfood' (Fry's Food),
1895
+ # 'smithsfoodanddrug' (Smith's), 'qfc' (QFC), 'dillons' (Dillons), 'bakersplus' (Baker's),
1896
+ # 'citymarket' (City Market), 'gerbes' (Gerbes), 'foodfourless' (Food 4 Less),
1897
+ # 'harristeeter' (Harris Teeter), 'marianos' (Mariano's), 'metromarket' (Metro Market),
1898
+ # 'picknsave' (Pick 'n Save), 'jaycfoods' (Jay C Food Stores), 'payless' (Pay Less),
1899
+ # 'foodsco' (FoodsCo). Host names (ralphs.com) and common spellings (food4less, smiths,
1900
+ # frys) work too. Default 'kroger'.
1901
+ # @param store_id [String] Kroger store id (8 digits, e.g. '01400929'): prices, stock and
1902
+ # aisles are that store's. The id is shown as `store.storeId` in every answer and in
1903
+ # `nearbyStores` when you search by `zip`.
1904
+ # @param zip [String] US ZIP code: prices for the banner's nearest store to it that takes
1905
+ # online orders (the nearest store at all when none does). The answer lists the nearby
1906
+ # stores in `nearbyStores`. Leave both `store_id` and `zip` blank for the banner's default
1907
+ # store.
1908
+ # Calls POST /groceries/kroger-product.
1909
+ def kroger_product(product, banner: nil, store_id: nil, zip: nil)
1910
+ post_json("/groceries/kroger-product", {
1911
+ product: product,
1912
+ banner: banner,
1913
+ store_id: store_id,
1914
+ zip: zip,
1915
+ })
1916
+ end
1917
+
1918
+ # Scrape Instacart product search.
1919
+ #
1920
+ # Searches one retailer's Instacart catalogue (Costco, Aldi, Publix, Safeway, Kroger, CVS,
1921
+ # Sprouts, ... named by `retailer`) for `query` and returns `products` in Instacart's order,
1922
+ # priced at the store Instacart assigns to `zip`: `id` (item id), `productId`, `name`,
1923
+ # `brand`, `size`, `packageSize`, `price`, `priceText`, `wasPrice`, `onSale`, `unitPrice`
1924
+ # (`price`, `per`, `text`), `soldBy` (each / weight), `estimatedWeight`, `promotions`
1925
+ # (`text`, `type`), `available`, `stockLevel`, `rating`, `reviewCount`, `category`,
1926
+ # `storeBrand`, `dietary`, `aisle`, `image` and `url`, plus `store` (the shop, its location
1927
+ # id and the ZIP code used) and `totalResults`. Prices are Instacart's prices in USD, which
1928
+ # can differ from the retailer's in-store prices. Up to `max_results` products, billed one
1929
+ # unit per 30.
1930
+ #
1931
+ # @param query [String] What to search for, e.g. 'milk', 'greek yogurt', 'paper towels'.
1932
+ # @param retailer [String] The retailer's Instacart slug: the part after /store/ in an
1933
+ # Instacart link, e.g. 'costco', 'aldi', 'publix', 'safeway', 'kroger', 'cvs', 'sprouts',
1934
+ # 'wegmans', 'sams-club'. An Instacart store link is accepted too.
1935
+ # @param zip [String] 5-digit US ZIP code of the delivery area. Instacart prices and stock
1936
+ # belong to the store serving it. Default 94105 (San Francisco).
1937
+ # @param max_results [Integer] Products to return, 1-300, in Instacart's order. Billed one
1938
+ # unit per 30. API default: 30.
1939
+ # Calls POST /groceries/instacart-search.
1940
+ def instacart_search(query, retailer: nil, zip: nil, max_results: nil)
1941
+ post_json("/groceries/instacart-search", {
1942
+ query: query,
1943
+ retailer: retailer,
1944
+ zip: zip,
1945
+ max_results: max_results,
1946
+ })
1947
+ end
1948
+
1949
+ # Scrape an Instacart product.
1950
+ #
1951
+ # One product of a retailer on Instacart, priced at the store Instacart assigns to `zip`:
1952
+ # the search fields (`id`, `productId`, `name`, `brand`, `size`, `packageSize`, `price`,
1953
+ # `wasPrice`, `onSale`, `unitPrice`, `soldBy`, `promotions`, `available`, `stockLevel`,
1954
+ # `rating`, `reviewCount`, `category`, `dietary`, `aisle`, `image`, `url`) plus
1955
+ # `description`, `ingredients`, `directions`, `warnings`, the product page `sections` and
1956
+ # the `images` gallery, and `store`. `product` is an Instacart product id, an item id from a
1957
+ # search result or a product link. One unit per call.
1958
+ #
1959
+ # @param product [String] An Instacart product id ('16902650'), an item id from a search
1960
+ # result ('items_4142-16902650') or a product link
1961
+ # (https://www.instacart.com/products/16902650-...).
1962
+ # @param retailer [String] The retailer's Instacart slug: the part after /store/ in an
1963
+ # Instacart link, e.g. 'costco', 'aldi', 'publix', 'safeway', 'kroger', 'cvs', 'sprouts',
1964
+ # 'wegmans', 'sams-club'. An Instacart store link is accepted too. Optional when `product`
1965
+ # is a link carrying `retailerSlug`.
1966
+ # @param zip [String] 5-digit US ZIP code of the delivery area. Instacart prices and stock
1967
+ # belong to the store serving it. Default 94105 (San Francisco).
1968
+ # Calls POST /groceries/instacart-product.
1969
+ def instacart_product(product, retailer: nil, zip: nil)
1970
+ post_json("/groceries/instacart-product", {
1971
+ product: product,
1972
+ retailer: retailer,
1973
+ zip: zip,
1974
+ })
1975
+ end
1976
+
1977
+ # Scrape Publix grocery search.
1978
+ #
1979
+ # Searches publix.com (US) for `query` at one store and returns `products`: `productId`,
1980
+ # `itemCode`, `title`, `brand`, `size`, `url`, `image`, `price`, `priceText`, `unit` (e.g.
1981
+ # 'lb' for items sold by weight), `wasPrice`, `currency`, `onSale`, `promotion` (`type` such
1982
+ # as BOGO, `message`, `conditions`, `validThrough`, `savings`), `hasCoupon`, `soldByWeight`,
1983
+ # `aisle`, `category`, `categoryPath` and `storeId`, plus `store` (the store the prices are
1984
+ # for), `totalResults`, `totalPages` and `nextPage`. Publix publishes a price online only
1985
+ # while an item is on promotion, so `price` is null for the rest. A keyword Publix sends to
1986
+ # a category returns that category, named in `redirectedTo`; one it sends to a page that
1987
+ # lists no products (the weekly ad, cake ordering, store locations) answers with no products
1988
+ # and `sentElsewhere` true.
1989
+ #
1990
+ # @param query [String] What to search for, e.g. 'milk', 'cheerios' or 'chicken breast'.
1991
+ # @param store_id [String] Publix store number whose prices and promotions to return, e.g.
1992
+ # '1614'. Give this or `zip`; with neither, a store in Lakeland, FL is used. The store used
1993
+ # is named in `store`.
1994
+ # @param zip [String] US ZIP code; the nearest Publix store is used, e.g. '33801'.
1995
+ # @param page [Integer] 1-based results page to start from (48 products per page). API
1996
+ # default: 1.
1997
+ # @param max_results [Integer] Products to collect from `page` on, 1-480. Each page fetched
1998
+ # is billed one unit. API default: 48.
1999
+ # Calls POST /groceries/publix-search.
2000
+ def publix_search(query, store_id: nil, zip: nil, page: nil, max_results: nil)
2001
+ post_json("/groceries/publix-search", {
2002
+ query: query,
2003
+ store_id: store_id,
2004
+ zip: zip,
2005
+ page: page,
2006
+ max_results: max_results,
2007
+ })
2008
+ end
2009
+
2010
+ # Scrape a Publix product.
2011
+ #
2012
+ # Returns one publix.com (US) product at one store as `product`: the search fields
2013
+ # (`productId`, `itemCode`, `title`, `brand`, `size`, `price`, `wasPrice`, `promotion`,
2014
+ # `aisle` and so on) plus `images`, `description`, `ingredients`, `directions`, `allergens`,
2015
+ # `available`, `categories`, `badges`, `couponCount`, `smartLabelUrl` and `relatedProducts`,
2016
+ # and `store`, the store the price is for. Publix publishes a price online only while an
2017
+ # item is on promotion.
2018
+ #
2019
+ # @param product [String] A Publix product id ('RIO-PCI-103438') or product link
2020
+ # (https://www.publix.com/pd/cheerios-cereal-large-size/RIO-PCI-103438).
2021
+ # @param store_id [String] Publix store number whose prices and promotions to return, e.g.
2022
+ # '1614'. Give this or `zip`; with neither, a store in Lakeland, FL is used. The store used
2023
+ # is named in `store`.
2024
+ # @param zip [String] US ZIP code; the nearest Publix store is used, e.g. '33801'.
2025
+ # Calls POST /groceries/publix-product.
2026
+ def publix_product(product, store_id: nil, zip: nil)
2027
+ post_json("/groceries/publix-product", {
2028
+ product: product,
2029
+ store_id: store_id,
2030
+ zip: zip,
2031
+ })
2032
+ end
2033
+
1303
2034
  # Smyths Toys UK product stock, price, details and listings.
1304
2035
  #
1305
2036
  # Smyths Toys UK (smythstoys.com) as JSON, in four modes. `mode=product` (the default when
@@ -2109,6 +2840,94 @@ module ScrapeUnblocker
2109
2840
  })
2110
2841
  end
2111
2842
 
2843
+ # Scrape a Google AI Mode answer.
2844
+ #
2845
+ # Asks Google AI Mode (the `udm=50` AI Mode tab, not the AI Overview on a results page) the
2846
+ # question `q` and returns its full answer as JSON: `answer.text` (plain text),
2847
+ # `answer.markdown` and `answer.blocks` (paragraphs, headings, lists, tables and code, in
2848
+ # order), `references` (the pages the answer cites: `title`, `url`, `domain`, `source`,
2849
+ # `snippet`), `links` (links written into the answer text) and `followUps` (the follow-up
2850
+ # questions Google closes the answer with, if any). Set `proxy_country` to ask from a market
2851
+ # - the Google market (`gl`) follows it - and `hl` for the answer's language. Answers differ
2852
+ # between identical requests, as they do for a person.
2853
+ #
2854
+ # @param q [String] The question to ask Google AI Mode, e.g. 'how to learn python fast'.
2855
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'US',
2856
+ # 'DE'). The Google market (`gl`) follows it unless `gl` is set.
2857
+ # @param gl [String] Optional Google market override (ISO-2, e.g. 'us', 'de'). Default:
2858
+ # `proxy_country`, else 'us'.
2859
+ # @param hl [String] Language Google answers in, e.g. 'en', 'de', 'pt-BR'. Default 'en'.
2860
+ # Calls POST /search/google-ai-mode.
2861
+ def google_ai_mode(q, proxy_country: nil, gl: nil, hl: nil)
2862
+ post_json("/search/google-ai-mode", {
2863
+ q: q,
2864
+ proxy_country: proxy_country,
2865
+ gl: gl,
2866
+ hl: hl,
2867
+ })
2868
+ end
2869
+
2870
+ # Search Google Lens with an image URL.
2871
+ #
2872
+ # Runs a Google Lens search for the image at `url` and returns its visual matches as JSON
2873
+ # (usually ~60). Every match carries `title`, `url` (the page the image is on), `domain`,
2874
+ # `source` (site name), `imageUrl` (the matching image), `thumbnailUrl`, `width`, `height`
2875
+ # and `position`; product matches add `price` and `currency` (and `product` details) when
2876
+ # Google has them, and `priceText`, `rating` and `reviews` as the tile shows them.
2877
+ # `relatedSearches` lists what Lens recognised in the image (e.g. 'Eiffel Tower', with its
2878
+ # Knowledge Graph id) and `aiOverview` is Google's own description of the image when it is
2879
+ # ready in time (else null). Shopping matches follow the country the request comes from -
2880
+ # set `proxy_country` for a market (a US request is served from a European exit, where
2881
+ # Google loads Lens matches; `proxyCountry` in the response says which).
2882
+ #
2883
+ # @param url [String] Public http(s) URL of the image to search with, e.g. a product photo.
2884
+ # Google fetches the image itself.
2885
+ # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'US',
2886
+ # 'DE'). Shopping matches and prices follow the country Google sees the request from.
2887
+ # @param gl [String] Optional Google market override (ISO-2). Default: `proxy_country`, else
2888
+ # 'us'.
2889
+ # @param hl [String] Google UI language, e.g. 'en', 'de'. Default 'en'.
2890
+ # @param max_results [Integer] Optional cap on the visual matches returned (1-100; default:
2891
+ # all, usually ~60).
2892
+ # Calls POST /images/google-lens.
2893
+ def google_lens(url, proxy_country: nil, gl: nil, hl: nil, max_results: nil)
2894
+ post_json("/images/google-lens", {
2895
+ url: url,
2896
+ proxy_country: proxy_country,
2897
+ gl: gl,
2898
+ hl: hl,
2899
+ max_results: max_results,
2900
+ })
2901
+ end
2902
+
2903
+ # Ask Google Gemini and get its answer and cited sources.
2904
+ #
2905
+ # Asks Google Gemini (gemini.google.com, signed out) one question and returns `answer`
2906
+ # (plain text) and `answerMarkdown`, `citations` (pages cited inline: `position`, `title`,
2907
+ # `url`, `domain`, `siteName`, `description`, `citedText` - the answer sentences that page
2908
+ # backs - and `firstCitedAt`, the character offset of the first of them), `sources` (pages
2909
+ # Gemini consulted when it searched the web: `position`, `title`, `url`, `domain`),
2910
+ # `webSearch` (whether it searched the web), `followUpPrompts` (`label`, `prompt`),
2911
+ # `richBlocks` (cards Gemini drew that have no text, e.g. a sports score or image strip,
2912
+ # left out of the answer), `model` (the model label the app shows), `location` and
2913
+ # `locationCountry` (where Gemini placed the visitor), `complete` and `conversationId`.
2914
+ # `country` asks from that country. Answers vary between identical calls, as they do for
2915
+ # people. One unit per call.
2916
+ #
2917
+ # @param prompt [String] The question to ask Gemini, as a user would type it, e.g. 'best CRM
2918
+ # for small business' (max 4000 characters). Each call starts a new conversation.
2919
+ # @param country [String] Two-letter country code to ask from, e.g. US, GB, DE. Gemini
2920
+ # answers for where it locates the visitor (local results, currency, language defaults);
2921
+ # `locationCountry` in the answer says where it placed this call. Leave blank for any
2922
+ # European location.
2923
+ # Calls POST /ai/gemini.
2924
+ def gemini(prompt, country: nil)
2925
+ post_json("/ai/gemini", {
2926
+ prompt: prompt,
2927
+ country: country,
2928
+ })
2929
+ end
2930
+
2112
2931
  # Search Bing.
2113
2932
  #
2114
2933
  # Searches Bing for `keyword` in a real browser and returns the results page as JSON:
@@ -3925,7 +4744,7 @@ module ScrapeUnblocker
3925
4744
  # @param video [String] Video id (dQw4w9WgXcQ) or any YouTube video URL.
3926
4745
  # @param lang [String] Preferred caption language code, e.g. 'en'. Omit for the
3927
4746
  # default/first track. A prefix like 'en' matches 'en-US'.
3928
- # @param format [String] Output shape: text, segments. 'segments' returns timestamps; 'text'
4747
+ # @param format [String] Output shape: segments, text. 'segments' returns timestamps; 'text'
3929
4748
  # returns only the joined plaintext. API default: segments.
3930
4749
  # @param proxy_country [String] Proxy country the request is sent from (ISO-2, e.g. 'US',
3931
4750
  # 'DE') for the watch page. Defaults to US.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ScrapeUnblocker
4
- VERSION = "0.14.1"
4
+ VERSION = "0.15.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scrapeunblocker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.1
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ScrapeUnblocker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-25 00:00:00.000000000 Z
11
+ date: 2026-10-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: JS-rendered pages that bypass Cloudflare, DataDome, PerimeterX and Akamai,
14
14
  plus Google SERP and 100+ ready-made site plugins (Amazon, Walmart, Zillow, Google