From be3d17037419febf1a79e313b99daf010a9a3c1d Mon Sep 17 00:00:00 2001 From: kasparasizi1 <132673909+kasparasizi1@users.noreply.github.com> Date: Tue, 30 Jun 2026 00:21:44 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20add=20google=20shopping=20offers=20(bar?= =?UTF-8?q?code=20=E2=86=92=20seller=20prices)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add client.google.shopping.offers({ barcode, gl?, hl? }) calling GET /v1/google/shopping/offers — resolves a product by barcode (GTIN-8/UPC-A/EAN-13/GTIN-14) and returns its multi-seller Google Shopping offers. New ShoppingOffersParams type, exported from the google and root barrels. Docs, README, CHANGELOG, and a unit test mirror the existing shopping methods. Version 0.15.2 -> 0.15.3. --- CHANGELOG.md | 6 ++++++ README.md | 2 +- docs/google.md | 17 ++++++++++++++++- package-lock.json | 4 ++-- package.json | 2 +- src/google/index.ts | 1 + src/google/shopping.ts | 12 ++++++++++++ src/google/types.ts | 29 +++++++++++++++++------------ src/index.ts | 1 + tests/google.test.ts | 19 ++++++++++++++++--- 10 files changed, 73 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01cef44..ab182a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.15.3] - 2026-06-30 + +### Added + +- Google Shopping barcode offers: `client.google.shopping.offers({ barcode, gl?, hl? })` calls `GET /v1/google/shopping/offers`, resolving a product by barcode (GTIN-8/UPC-A/EAN-13/GTIN-14) and returning its multi-seller Google Shopping offers. New `ShoppingOffersParams` type. Costs 14 credits; returns 422 for an invalid/checksum-failing barcode and 404 when the barcode can't be resolved. + ## [0.13.1] - 2026-06-13 ### Added diff --git a/README.md b/README.md index e417eaf..ac63bb9 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ The official Node.js/TypeScript client library for the [ScrapeBadger](https://sc - **Resilient Retries** - Exponential backoff with colored console warnings - **Typed Exceptions** - Distinct error classes for every failure scenario - **37+ Twitter endpoints** - Tweets, users, lists, communities, trends, geo, real-time streams -- **19 Google product APIs** - Search (with optional deferred AI Overview follow-up), Maps, News, Hotels, Trends (incl. topic autocomplete), Jobs, Shopping (+ merchant URL enrichment), Patents, Scholar (search + profiles + author + author citation + cite formats), Images, Videos, Finance, AI Mode, Lens, **Local Pack**, **Shorts**, **Flights**, Products +- **19 Google product APIs** - Search (with optional deferred AI Overview follow-up), Maps, News, Hotels, Trends (incl. topic autocomplete), Jobs, Shopping (+ merchant URL enrichment, barcode offers), Patents, Scholar (search + profiles + author + author citation + cite formats), Images, Videos, Finance, AI Mode, Lens, **Local Pack**, **Shorts**, **Flights**, Products - **Vinted scraping** - Search items, item details, user profiles, brands, colors, markets - **Web scraping** - Anti-bot bypass, JS rendering, and AI data extraction diff --git a/docs/google.md b/docs/google.md index 70187ce..c1f475b 100644 --- a/docs/google.md +++ b/docs/google.md @@ -185,6 +185,18 @@ console.log("Merchant URL:", enriched.merchant_url); // Get detailed product information + seller list const detail = await client.google.shopping.product({ product_id: first.product_id }); + +// Resolve a product by barcode (GTIN-8/UPC-A/EAN-13/GTIN-14) and return +// its multi-seller Google Shopping offers. 422 for an invalid/checksum- +// failing barcode, 404 when the barcode can't be resolved. +const byBarcode = await client.google.shopping.offers({ + barcode: "0190198001751", + gl: "us", +}); +console.log(byBarcode.product_title, byBarcode.resolved_query); +for (const o of byBarcode.offers as any[]) { + console.log(`${o.source} — ${o.price.extracted}`); +} ``` ### Patents @@ -271,7 +283,7 @@ const product = await client.google.products.detail({ product_id: "1234567890" } | `client.google.hotels` | Hotels: search, details | | `client.google.trends` | Trends: interest, regions, related, trending | | `client.google.jobs` | Job listings search | -| `client.google.shopping` | Shopping: search, product, click enrichment | +| `client.google.shopping` | Shopping: search, product, click enrichment, barcode offers | | `client.google.patents` | Patents: search, detail | | `client.google.scholar` | Academic paper search | | `client.google.autocomplete` | Search suggestion lookup | @@ -305,6 +317,7 @@ const product = await client.google.products.detail({ product_id: "1234567890" } | `shopping` | `search(params)` | Product search | | `shopping` | `product(params)` | Product detail + sellers | | `shopping` | `click(params)` | Merchant URL enrichment | +| `shopping` | `offers(params)` | Barcode → multi-seller offers | | `patents` | `search(params)` | Patent search | | `patents` | `detail(params)` | Patent document | | `scholar` | `search(params)` | Academic papers | @@ -343,6 +356,7 @@ import type { ShoppingSearchParams, ShoppingProductParams, ShoppingClickParams, + ShoppingOffersParams, PatentsSearchParams, PatentsDetailParams, ScholarSearchParams, @@ -411,6 +425,7 @@ import type { | `search`, `images`, `videos`, `maps/search`, `shopping/search`, `jobs/search`, `scholar/search`, `patents/search`, `finance/quote`, `trends/*` (except trending) | **2** | | `maps/place`, `maps/reviews`, `patents/detail`, `ai-mode/search`, `lens/search`, `hotels/search`, `products/detail` | **3** | | `hotels/details`, `shopping/product` | **5** | +| `shopping/offers` | **14** | | `news/*`, `autocomplete`, `trends/trending`, `maps/photos`, `maps/posts`, `shopping/product/click` | **1** | | Failed requests | **0** | diff --git a/package-lock.json b/package-lock.json index 4b0c3db..c43af3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "scrapebadger", - "version": "0.7.0", + "version": "0.15.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "scrapebadger", - "version": "0.7.0", + "version": "0.15.3", "license": "MIT", "dependencies": { "ws": "^8.18.0" diff --git a/package.json b/package.json index 8ace86c..e957141 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scrapebadger", - "version": "0.15.2", + "version": "0.15.3", "description": "Official Node.js SDK for ScrapeBadger - Async web scraping APIs for Twitter, Google, Vinted, Reddit, and more", "type": "module", "main": "./dist/index.js", diff --git a/src/google/index.ts b/src/google/index.ts index 67b5b7f..3f2e650 100644 --- a/src/google/index.ts +++ b/src/google/index.ts @@ -59,6 +59,7 @@ export type { ScholarProfilesParams, ScholarSearchParams, ShoppingClickParams, + ShoppingOffersParams, ShoppingProductParams, ShoppingSearchParams, ShortsSearchParams, diff --git a/src/google/shopping.ts b/src/google/shopping.ts index 8a366b9..7956ad4 100644 --- a/src/google/shopping.ts +++ b/src/google/shopping.ts @@ -6,6 +6,7 @@ import type { BaseClient } from "../internal/client.js"; import type { GoogleResponse, ShoppingClickParams, + ShoppingOffersParams, ShoppingProductParams, ShoppingSearchParams, } from "./types.js"; @@ -58,4 +59,15 @@ export class ShoppingClient { params: { ...params }, }); } + + /** + * Resolve a product by `barcode` (GTIN-8/UPC-A/EAN-13/GTIN-14) and return + * its multi-seller Google Shopping offers. Throws on a 422 (invalid or + * checksum-failing barcode) or 404 (barcode not resolvable to a product). + */ + async offers(params: ShoppingOffersParams): Promise { + return this.client.request("/v1/google/shopping/offers", { + params: { ...params }, + }); + } } diff --git a/src/google/types.ts b/src/google/types.ts index ebf5bd0..5b849c5 100644 --- a/src/google/types.ts +++ b/src/google/types.ts @@ -120,13 +120,7 @@ export interface NewsSearchParams { * back to a plain `string` when you know the topic code out-of-band. */ export type NewsTopic = - | "WORLD" - | "BUSINESS" - | "TECHNOLOGY" - | "ENTERTAINMENT" - | "SPORTS" - | "SCIENCE" - | "HEALTH"; + "WORLD" | "BUSINESS" | "TECHNOLOGY" | "ENTERTAINMENT" | "SPORTS" | "SCIENCE" | "HEALTH"; export interface NewsTopicsParams { // Accept the canonical codes as a `NewsTopic` union, but also allow any @@ -233,11 +227,7 @@ export interface TrendsTrendingNowParams { } export type TrendsDataType = - | "TIMESERIES" - | "GEO_MAP" - | "GEO_MAP_0" - | "RELATED_TOPICS" - | "RELATED_QUERIES"; + "TIMESERIES" | "GEO_MAP" | "GEO_MAP_0" | "RELATED_TOPICS" | "RELATED_QUERIES"; export interface TrendsSearchParams { /** Search term(s). Comma-separated (max 5) for TIMESERIES / GEO_MAP. */ @@ -349,6 +339,21 @@ export interface ShoppingClickParams { product_id?: string; } +/** + * Params for `/v1/google/shopping/offers` — resolve a product by barcode + * (GTIN-8/UPC-A/EAN-13/GTIN-14) and return its multi-seller Google + * Shopping offers. Returns 422 for an invalid/checksum-failing barcode and + * 404 when the barcode can't be resolved to a product. + */ +export interface ShoppingOffersParams { + /** Product barcode: a GTIN-8, UPC-A, EAN-13, or GTIN-14. */ + barcode: string; + /** ISO-3166 alpha-2 country code (e.g. `us`). */ + gl?: string; + /** UI/results language (e.g. `en`). Defaults to `en`. */ + hl?: string; +} + // ===== Patents ===== /** diff --git a/src/index.ts b/src/index.ts index 587e4ba..48dc9a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -149,6 +149,7 @@ export type { ScholarProfilesParams, ScholarSearchParams, ShoppingClickParams, + ShoppingOffersParams, ShoppingProductParams, ShoppingSearchParams, ShortsSearchParams, diff --git a/tests/google.test.ts b/tests/google.test.ts index ce3c63e..d744d01 100644 --- a/tests/google.test.ts +++ b/tests/google.test.ts @@ -123,9 +123,7 @@ describe("MapsClient", () => { it("place throws when no identifier is supplied", async () => { const client = makeClient(); - await expect(client.google.maps.place({})).rejects.toThrow( - "place_id or data_id" - ); + await expect(client.google.maps.place({})).rejects.toThrow("place_id or data_id"); }); it("reviews forwards all params", async () => { @@ -318,6 +316,21 @@ describe("ShoppingClient", () => { expect(url.searchParams.get("product_id")).toBe("pid123"); expect(response.merchant_url).toBe("https://www.razer.com/x"); }); + + it("offers resolves a barcode via /shopping/offers", async () => { + mockFetch({ barcode: "0190198001751", offers: [] }); + const client = makeClient(); + const response = await client.google.shopping.offers({ + barcode: "0190198001751", + gl: "us", + hl: "en", + }); + const url = capturedUrl(); + expect(url.pathname).toBe("/v1/google/shopping/offers"); + expect(url.searchParams.get("barcode")).toBe("0190198001751"); + expect(url.searchParams.get("gl")).toBe("us"); + expect(response.barcode).toBe("0190198001751"); + }); }); // ---------------------------------------------------------------------------