# Best Buy Price Drop & Stock Tracker (`dataspryva/best-buy-price-drop-and-stock-tracker`) Actor

Search Best Buy by keyword or product URL to get current and regular price, savings, rating, review count, brand, model and stock status for each item in one structured record.

- **URL**: https://apify.com/dataspryva/best-buy-price-drop-and-stock-tracker.md
- **Developed by:** [Dataspryva](https://apify.com/dataspryva) (community)
- **Categories:** E-commerce, Automation, Lead generation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Best Buy Price Drop & Stock Tracker

Watch Best Buy prices and availability without opening a browser tab for every product. Give this actor a few keywords, a category link, product links or SKU numbers, and it returns one structured row per product: what the item is, what it costs now, what it used to cost, whether it is in stock, how shoppers rated it, and where it sits in the result grid.

The actor runs without a Best Buy account, API key, cookie or proxy setup. It reads the public Best Buy storefront and its public price endpoint, then writes clean product records to the Apify dataset.

### Four ways to build a product list

| Mode | Field | Best for |
|---|---|---|
| Keyword search | `searchQueries` | Exploring a category, brand or product idea |
| Category or search URL | `categoryUrls` | Reusing a Best Buy browse/search link you already trust |
| Product URL | `productUrls` | Watching exact items you found on the site |
| SKU | `skuIds` | Feeding a restock or price watchlist from another system |

All four modes can be combined in a single run. Results are de-duplicated by SKU, filtered, sorted and capped by `maxResults`.

### What you can do with it

- **Price-drop watchlist** — run the same `skuIds` or `productUrls` on a schedule and compare `currentPrice` between datasets. A lower value is a drop; `regularPrice` tells you the reference price.
- **Restock monitor** — keep `inStockOnly` off and watch `availability` flip from `OutOfStock` to `InStock`.
- **Category deal scan** — pass a category URL with `sortBy: "Best-Discount"` or `condition: "open-box"` to surface markdowns and open-box units.
- **Competitive snapshot** — collect brand, model, rating and review count for every product in a search grid.
- **Rank tracking** — `position`, `searchPage` and `isSponsored` preserve the grid order Best Buy returned, so you can compare placement across runs.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `searchQueries` | array | – | One or more keyword searches (the input form prefills `["sony headphones"]`). Each is paginated until the global result cap is reached. |
| `categoryUrls` | array | `[]` | Best Buy category, browse or search-result links. `cp` pagination, sort and condition filters are added automatically. |
| `productUrls` | array | `[]` | Exact product links in new `/product/.../sku/...` or legacy `/site/.../<sku>.p?skuId=...` form. |
| `skuIds` | array | `[]` | Best Buy SKUs (6–9 digits). Product links are accepted here too. |
| `maxResults` | integer | `24` | Total records across all modes, 1–1000. |
| `maxPages` | integer | `50` | Page guard per keyword or category link, 1–100. |
| `sortBy` | select | `Best-Match` | One of Best-Match, Best-Selling, Best-Discount, Price-Low-To-High, Price-High-To-Low, Customer-Rating, New-Arrivals. |
| `condition` | select | `all` | `all`, `new`, `open-box` or `refurbished`. |
| `minPrice` | integer | `0` | Minimum current price in dollars; `0` disables the bound. |
| `maxPrice` | integer | `0` | Maximum current price in dollars; `0` disables the bound. |
| `inStockOnly` | boolean | `false` | Drop products Best Buy marks sold out, out of stock or unavailable. |
| `includeDetails` | boolean | `false` | Open each product page to add description, model, GTIN/UPC and seller where Best Buy publishes them. Slower. |

Every field in the input form is used by the actor. Older saved tasks that still send `keyword`, `searchQuery`, `categoryUrl`, `skus`, `maxItems`, `sort`, `skipOutOfStock` or `openBoxOnly` keep working: those legacy keys are mapped onto the current fields in code.

#### Example: exact-product watchlist

```json
{
  "skuIds": ["6505727", "6620467", "6689192"],
  "inStockOnly": false,
  "maxResults": 10
}
```

#### Example: open-box deals in a category

```json
{
  "categoryUrls": [
    "https://www.bestbuy.com/site/headphones/wireless-headphones/pcmcat331200050015.c?id=pcmcat331200050015"
  ],
  "condition": "open-box",
  "sortBy": "Price-Low-To-High",
  "maxResults": 60
}
```

#### Example: keyword search with a price window

```json
{
  "searchQueries": ["sony headphones", "noise cancelling earbuds"],
  "minPrice": 100,
  "maxPrice": 400,
  "inStockOnly": true,
  "sortBy": "Customer-Rating",
  "maxResults": 50
}
```

#### Example: one exact link

```json
{
  "productUrls": [
    "https://www.bestbuy.com/product/sony-wh1000xm5-wireless-noise-canceling-over-the-ear-headphones-black/J7XSRH5CXG/sku/6505727"
  ],
  "includeDetails": true
}
```

### Output fields

| Field | Type | Meaning |
|---|---|---|
| `sku` | string | Best Buy SKU. |
| `url` | string | Canonical product page URL. |
| `bsin` | string | Best Buy canonical product-family identifier, when the URL exposes it. |
| `name` | string | Product title. |
| `description` | string | Product description, from the optional detail pass. |
| `brand` | string | Manufacturer brand. |
| `model` | string | Model number or MPN, from the optional detail pass. |
| `gtin13` | string | GTIN/UPC, from the optional detail pass. |
| `image`, `imageUrl` | string | Primary product image (same value under both keys). |
| `currentPrice` | number | Price shown to a shopper now. |
| `regularPrice` | number | Non-sale reference price. |
| `salePrice` | number | Current price when it is below the regular price. |
| `savings` | number | Regular price minus current price. |
| `savingsPercent` | number | Savings as a percentage of the regular price. |
| `onSale` | boolean | True when the current price is below the regular price. |
| `currency`, `currencySymbol` | string | ISO currency code and display symbol. |
| `availability` | string | Normalized stock state, e.g. `InStock`, `OutOfStock`, `PreOrder`, `BackOrder`, `LimitedAvailability`. |
| `availabilityText` | string | Best Buy's own button text, e.g. `Add to Cart` or `Sold Out`. |
| `buttonState` | string | Raw Best Buy button state. |
| `priceType` | string | Best Buy price event type, e.g. `onSale` or `clearance`. |
| `condition` | string | `new`, `open-box` or `refurbished` when known. |
| `productType` | string | Best Buy product type, e.g. `hardgood`. |
| `department`, `productClass`, `subclass` | string | Best Buy taxonomy labels. |
| `categoryPath` | array | Department → class → subclass path. |
| `rating` | number | Average customer rating. |
| `reviewCount` | integer | Number of customer reviews. |
| `seller` | string | Seller name, from the optional detail pass. |
| `isSponsored` | boolean | True when the result card is labelled Sponsored. |
| `dealBadge` | string | Best Buy promotional badge, e.g. `Trending Deal` or `Holiday Deal`. |
| `position` | integer | 1-based position in this run's output after sorting. |
| `searchPage` | integer | Result page where a search/category product was found. |
| `searchTotalResults` | integer | Best Buy's reported result count for that query or category. |
| `searchQuery` | string | Keyword or category URL that produced the record. |
| `source` | string | `search`, `category`, `productUrl` or `sku`. |
| `scrapedAt` | string | ISO-8601 UTC collection timestamp. |

Fields Best Buy does not publish for a product are omitted rather than sent as `null` or an empty string. Missing price, rating or detail fields therefore mean “not available on this page”, not zero.

### How it works

1. Keyword and category modes load Best Buy's result grid in a real headless browser, scroll it to hydrate lazy cards, and read the rendered product cards. A direct-HTML fallback reads the server-rendered product data if the browser grid is empty.
2. Product URL and SKU modes skip search and go straight to Best Buy's public price endpoint, batching up to 20 SKUs per request.
3. Every mode merges the card data with the price/stock response, so price, savings and availability come from Best Buy's current price source rather than a cached grid tile. If that endpoint omits a SKU, the price shown on the hydrated result card fills the row.
4. Optional detail enrichment opens each product page and reads its structured product data for description, model, GTIN and seller.
5. Filters and sorting are applied once the merged records exist; duplicates are removed by SKU.

The actor adds `intl=nosplash` to every request. That tells Best Buy to serve the US storefront instead of its international country selector.

### Limitations and honest notes

- **Online availability only.** Best Buy's per-store, per-ZIP inventory needs a location-bound fulfillment session. This actor reports the national online stock state and does not claim store-level counts.
- **No stored price history.** The actor is stateless. Schedule it and keep each dataset, or write the rows to a spreadsheet, to build a price timeline.
- **Detail fields are best-effort and often unavailable.** Best Buy resets product-page requests from many datacenter IPs. In cloud runs `description`, `model`, `gtin13` and `seller` are therefore usually omitted; the price and stock row still ships, and `includeDetails` never blocks it.
- **SKUs with no usable data are skipped.** If Best Buy returns neither a name nor a price for a product (retired, region-locked, or a price-endpoint miss), that row is not emitted. `maxResults` is a cap, not a guarantee, so a run can return fewer rows. A run that finds no product at all returns one `bestbuy_error` row with `reason: "no_results"`.
- **Full specification sheets are not included.** The detail pass reads structured product data, not the interactive spec table.
- **Individual reviews are not included.** Use `rating` and `reviewCount` for reception; review text belongs to a dedicated reviews actor.
- **Marketplace offers are summarized, not enumerated.** The record follows the primary offer Best Buy displays.

### FAQ

**Do I need a Best Buy API key?** No. The actor reads public storefront and price data. It does not use the gated developer API.

**Do I need a proxy?** No. There is no proxy field and no proxy setup step. The actor uses a normal browser session and the public price endpoint.

**Why does a SKU sometimes have no `rating` or `image`?** Those values come from the search card. Direct SKU and product-URL lookups skip the grid, so the actor can fill price, stock, name, brand and taxonomy but may not see the card-only fields. Add a keyword search for the richest row.

**Why is `availability` different from what I see in a store?** The field tracks Best Buy's online button state. Store pickup and nearby inventory depend on a ZIP code and a different fulfillment call, which this actor does not make.

**Can I get more than 24 results?** Yes. Raise `maxResults` and, if needed, `maxPages`. Best Buy caps a search at roughly 200 results for many queries; category pages can expose more.

**What happens when a keyword finds nothing?** The dataset receives a single `bestbuy_error` row with `reason: "no_results"` and the inputs that produced it, so a failed run is visible instead of silently empty.

**How do I track a price drop?** Run the same `skuIds` or `productUrls` daily, keep the datasets, and compare `currentPrice` or `savingsPercent` between consecutive runs.

**Is this affiliated with Best Buy?** No. It reads publicly visible product information and is not endorsed by Best Buy.

# Actor input Schema

## `searchQueries` (type: `array`):

One or more Best Buy searches, for example `sony headphones` or `oled tv`. Each query is paginated until `maxResults` is reached.

## `categoryUrls` (type: `array`):

Best Buy category, browse or search-result links, e.g. `https://www.bestbuy.com/site/headphones/.../pcmcat....c?id=...`. Pagination, sorting and condition filters are applied automatically.

## `productUrls` (type: `array`):

Exact Best Buy product links in either the new `/product/.../sku/...` or legacy `/site/.../<sku>.p?skuId=...` format.

## `skuIds` (type: `array`):

Best Buy SKU numbers (6–9 digits). Product links are also accepted here.

## `maxResults` (type: `integer`):

Hard cap on the total number of product records emitted across every mode.

## `maxPages` (type: `integer`):

Pagination guard for each keyword or category link. Each page carries up to about 24 products.

## `sortBy` (type: `string`):

Best Buy's own sort order. Price, rating and discount orders are also enforced on the collected records.

## `condition` (type: `string`):

Keep only new, open-box or refurbished products. `All conditions` disables this filter.

## `minPrice` (type: `integer`):

Drop products below this current price. `0` means no minimum.

## `maxPrice` (type: `integer`):

Drop products above this current price. `0` means no maximum.

## `inStockOnly` (type: `boolean`):

Drop products that Best Buy marks sold out, out of stock or unavailable.

## `includeDetails` (type: `boolean`):

Open each product page for description, model number, GTIN/UPC and seller. Slower; unavailable fields are omitted honestly.

## Actor input object example

```json
{
  "searchQueries": [
    "sony headphones"
  ],
  "categoryUrls": [],
  "productUrls": [],
  "skuIds": [],
  "maxResults": 3,
  "maxPages": 50,
  "sortBy": "Best-Match",
  "condition": "all",
  "minPrice": 0,
  "maxPrice": 0,
  "inStockOnly": false,
  "includeDetails": false
}
```

# Actor output Schema

## `products` (type: `string`):

Dataset containing every scraped Best Buy product.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "sony headphones"
    ],
    "maxResults": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataspryva/best-buy-price-drop-and-stock-tracker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["sony headphones"],
    "maxResults": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("dataspryva/best-buy-price-drop-and-stock-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "sony headphones"
  ],
  "maxResults": 3
}' |
apify call dataspryva/best-buy-price-drop-and-stock-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataspryva/best-buy-price-drop-and-stock-tracker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/vTgN2nhL0hqk9nqMe/builds/Ed9cXcqgUuFh1rmod/openapi.json
