# Changelog of Bing Shopping Scraper (`searchapi/bing-shopping-scraper`) Actor

- **URL**: https://apify.com/searchapi/bing-shopping-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/searchapi/bing-shopping-scraper.md

## Changelog

### \[3.0.1] - 2026-08-29

#### Added

- Multiple-query search with fair interleaving and per-query limits.
- Stable `id`, `globalPosition`, `queryPosition`, selected filters, source, and extraction-method fields.
- A linked JSON-Schema output contract used by the Actor manifest.

#### Fixed

- Pagination and deduplication are now isolated per query in batch runs.
- The overall `maxItems` limit is respected after fair query allocation.
- Updated Apify, Crawlee, and Playwright and resolved all production dependency audit findings.

### \[2.0.1] - 2026-06-13

#### Fixed

- **Price extraction**: stopped using the broad `.resp-one-line.br-max-width` selector (which captured a wrapper containing both current and original price, e.g. `$138.99$159.99`). Now prefers the dedicated `.br-price` element and falls back to the first price-like text node in `.resp-one-line.br-max-width`, with a defensive regex strip to drop any trailing price. Result: 9/30 (~30%) of records with concatenated prices now return a single clean price.

#### Verified

- End-to-end local run with `query="wireless headphones"`, `maxItems=30` produced **30/30 records** in ~4s with zero failed requests.
- 100% fill rate on: `title`, `link`, `url`, `domain`, `productId`, `price`, `priceNumeric`, `currency`, `brand`, `seller`, `searchQuery`, `searchUrl`, `hostPageUrl`, `hostPageDomain`, `displayUrl`.
- 93% (28/30) on `thumbnail` — Bing lazy-loads images on scroll.
- `rating` and `reviewCount` are 0/30 because Bing Shopping no longer surfaces star ratings in the organic listing layout for this market/query.
- `originalPrice` and `originalPriceNumeric` are 0/30 — Bing shopping only surfaces the original price in product detail pages, not in the search grid.

### \[2.0.0] - 2026-06-12

#### Added

- New dataset fields: `type`, `resultType`, `searchQuery`, `searchUrl`, `country`, `language`, `scrapedAt`, `displayUrl`, `productId`, `link`, `url`, `domain`, `priceNumeric`, `originalPriceNumeric`, `discountPercent`, `sellerUrl`, `sellerRating`, `ratingCount`, `shippingCost`, `inStock`, `availability`, `condition`, `brand`, `category`, `isAd`, `badges`, `delivery`, `paymentOptions`, `warranty`, `favicon`, `searchMetadata`
- `priceNumeric` / `originalPriceNumeric` — parsed numeric price fields
- `discountPercent` — auto-computed from price + original price (e.g. 38)
- `brand` — extracted heuristically from product title (known brand list)
- `condition` — `new` / `used` / `refurbished` (when visible)
- `inStock` — boolean availability flag
- `sellerUrl` / `sellerRating` — explicit seller metadata
- `searchMetadata` object containing engine, market, country, language, page, query, resultType, scrapedAt
- `category` — optional category field
- `badges` — array of "Best Seller", "New", etc.
- `delivery` — delivery info text
- `paymentOptions` — array of payment methods
- `warranty` — warranty text
- New input fields: `mode` (`search` | `urls`), `startUrls`, `headless`, `market`
- New view "Full Details" showing every field
- New input mode `urls` to scrape arbitrary Bing Shopping URLs via `startUrls`
- Uses shared `_canonical/normalize.js` (`normalizeShoppingRecord`) for cross-actor consistency

#### Changed

- **Breaking** — `position` is now a global counter across pages (not per-page)
- `query` is kept as a backward-compat alias of `searchQuery`
- `url` is kept as a backward-compat alias of `productUrl`
- `merchant` is kept as a backward-compat alias of `seller`
- `seller` field now uses the seller name (e.g. `LT Online Store`, `Ubuy India`)
- Auto-pagination via `crawler.addRequests` from inside the page handler
- Multi-region support via `market` parameter (default `en-US`)

#### Fixed

- Robust price parsing for ₹, $, €, £, ¥, ¢, ₩, ₽, ฿ (handles thousand separators)
- Robust discount % parsing from "Save 38%", "-38%", "38% off"
- Robust brand extraction via known brand list (Sony, Apple, Samsung, Bose, ...)
- Skips "Shorts" / filter cards that don't have `.br-item` class
- Deduplication by `data-offerid` (Bing product ID)

### \[1.0.0] - 2025-07-01

#### Added

- Initial release of the actor
- Full README documentation with usage examples and field descriptions
- Input schema with validation for all parameters
- Dataset output schema defining all output fields
- Structured source layout: `src/main.js`, `src/routes/handlers.js`, `src/routes.js`, `src/errors/`

#### Changed

- Dockerfile updated: all `COPY` commands use `--chown=myuser` to prevent EACCES build errors
- Removed `postinstall: npx crawlee install-playwright-browsers` (browsers pre-installed in base image)
- `playwright` kept in `dependencies` (not devDependencies) for correct runtime import resolution

#### Fixed

- Build reliability: resolved `EACCES: permission denied, open '/home/myuser/package-lock.json'` on Apify Cloud (exit code 243)
