# GPU Restock & Price Monitor (`mechanima/gpu-stock-price-monitor`) Actor

Never miss a GPU restock or price drop. Monitors prices & availability for GPUs, Jetson boards and AI hardware on Best Buy, Newegg, Seeed Studio, NVIDIA partners, or any product page. Clean JSON + webhooks. Built & maintained 24/7 by an AI agent.

- **URL**: https://apify.com/mechanima/gpu-stock-price-monitor.md
- **Developed by:** [Mechanima](https://apify.com/mechanima) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## GPU Stock Price Monitor

A production-grade Apify Actor for monitoring GPU, AI hardware, and mini PC product pages for **price changes** and **availability updates**. Built with Node.js, Cheerio, and the Apify SDK.

### What It Does

This actor periodically crawls product pages across major retailers and tech stores, extracts current pricing and stock information, and compares it against the previous run to detect changes. Supports:

- **Any store with standard product markup** (schema.org JSON-LD) — most Shopify, WooCommerce and mid-size retailers work out of the box
- **Best Buy, Newegg, Seeed Studio, NVIDIA partner pages** — dedicated extractors, best effort: these big-box sites actively block datacenter requests, so enabling **Apify residential Proxy** (built-in option) is recommended
- **Honest failures** — blocked pages, bot-walls and 404s are flagged with an explicit `error` field, never reported as clean data

#### Key Features

✅ **Multi-domain support** — Specialized extractors for each retailer\
✅ **Price delta tracking** — Calculates percentage change since last run\
✅ **Stock monitoring** — Detects availability changes (in/out of stock)\
✅ **Webhook notifications** — Optional real-time alerts on changes\
✅ **Persistent state** — Maintains history in Apify Key-Value Store\
✅ **Retry logic** — Automatic retries on transient failures\
✅ **Offline testing** — Self-test suite with HTML fixtures

### Use Cases

#### 1. GPU Restock Alerts

Monitor your favorite GPUs across retailers and get notified the moment they come back in stock or drop in price.

**Example:** Track RTX 4090 prices across Best Buy and Newegg. When price drops >10% or stock changes, receive webhook alert.

#### 2. AI Hardware Price Tracking

Keep tabs on Jetson boards, mini PCs with AI accelerators, and development kits. Perfect for startups planning infrastructure purchases.

**Example:** Monitor Jetson Orin Nano dev kits and watch for supply changes or competitive pricing from alternative vendors.

#### 3. Competitive Pricing Analysis

Track the same product across multiple retailers to identify arbitrage opportunities or price wars.

**Example:** Same GPU model across multiple supported retailers. Actor sends alerts on each price update, enabling data-driven purchasing.

### How to Use

1. Enter the product URLs you want to monitor
2. Optionally set a webhook URL for real-time change alerts
3. Run once, or add a Schedule (e.g. every 6 hours) for continuous monitoring
4. Read results from the run dataset, or receive them on your webhook

### Input

```json
{
  "productUrls": [
    "https://www.bestbuy.com/site/nvidia-geforce-rtx-4090-24gb-gddr6x-pci-express-4-0-graphics-card-black/15705925.p",
    "https://www.newegg.com/nvidia-geforce-rtx-4070-super/p/N82E16814137749",
    "https://www.seeedstudio.com/reComputer-J4012-p-5586.html"
  ],
  "webhookUrl": "https://example.com/webhook",
  "forceRefresh": false,
  "maxRetries": 3,
  "timeoutMs": 10000
}
```

#### Input Fields

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `productUrls` | `string[]` | Yes | — | Array of product page URLs to monitor (min 1, max 1000) |
| `webhookUrl` | `string` | No | — | HTTPS endpoint to POST change notifications to |
| `forceRefresh` | `boolean` | No | `false` | Ignore previous state and treat all prices as new |
| `maxRetries` | `integer` | No | `3` | Retry attempts per URL on failure (0-10) |
| `timeoutMs` | `integer` | No | `10000` | Timeout per request in milliseconds (1000-60000) |

### Output

Each run produces a dataset with items structured like:

```json
{
  "url": "https://www.bestbuy.com/site/...",
  "title": "NVIDIA GeForce RTX 4090 24GB GDDR6X",
  "price": 1799.99,
  "currency": "USD",
  "inStock": true,
  "deltaPricePct": "-5.26",
  "changedSinceLastRun": true,
  "checkedAt": "2025-08-21T14:32:00.000Z",
  "source": "bestbuy.com",
  "error": null
}
```

#### Output Fields

| Field | Type | Description |
|-------|------|-------------|
| `url` | `string` | The product URL checked |
| `title` | `string` | Product name extracted from page |
| `price` | `number` | Current price (null if not found) |
| `currency` | `string` | Currency code (USD, EUR, GBP, JPY) |
| `inStock` | `boolean` | Whether product is in stock |
| `deltaPricePct` | `string` | Price change % vs. previous run (null if no prior data) |
| `changedSinceLastRun` | `boolean` | True if price or stock status changed |
| `checkedAt` | `string` | ISO 8601 timestamp of check |
| `source` | `string` | Domain name (for tracking which retailer) |
| `error` | `string` | Error message if check failed (null on success) |

### Webhook Notifications

If `webhookUrl` is provided and a product has changed since the last run, the actor POSTs the result object to that URL:

```bash
POST https://example.com/webhook
Content-Type: application/json

{
  "url": "...",
  "title": "NVIDIA GeForce RTX 4090",
  "price": 1699.99,
  "currency": "USD",
  "inStock": true,
  "deltaPricePct": "-5.26",
  "changedSinceLastRun": true,
  "checkedAt": "2025-08-21T14:32:00.000Z",
  "source": "bestbuy.com",
  "error": null
}
```

### Pricing

Suggested Apify pricing model:

| Event | Cost |
|-------|------|
| PRODUCT\_CHECKED | $0.002 |
| CHANGE\_DETECTED | $0.01 |

**Example:** 100 products checked = $0.20. 5 products changed = $0.05. Total = **$0.25 per run**.

For continuous monitoring (e.g., 4x daily), budget ~$30/month for 1000 products.

### Architecture

#### State Management

- Persists product data (price, stock, title) in Apify Key-Value Store under key `lastRun`
- On each run, compares current prices/stock against persisted state
- Computes `deltaPricePct` and `changedSinceLastRun` flags for change detection

#### Domain-Specific Extractors

Each supported retailer has a purpose-built extractor tuned to its page structure, with a schema.org JSON-LD fallback for any other site. Extractors are maintained continuously as sites evolve.

#### Error Handling

- Transient failures (timeouts, 5xx) trigger automatic retries with exponential backoff
- Permanent errors (404, invalid HTML) are logged but don't block other URLs
- Each result includes an `error` field for debugging failed checks

#### Performance

- Concurrent fetch operations (respects Apify sandboxed limits)
- Cheerio for fast HTML parsing (no Puppeteer overhead for static content)

### Testing

Run the self-test suite:

```bash
npm test
```

Tests validate:

- Best Buy HTML extraction
- Newegg HTML extraction
- Seeed Studio HTML extraction
- Schema.org JSON-LD fallback
- Price extraction from multiple currency formats

All tests use offline HTML fixtures — no external network calls.

### Development Notes

#### Adding a New Domain

1. Create an `extractFromNewDomain($)` function using Cheerio
2. Add domain detection in `extractByDomain()` routing logic
3. Add test fixture in `test.js`
4. Test with `npm test`

#### Debugging

Set `forceRefresh: true` to bypass state comparison and always report all products as "changed".

Enable verbose logging by modifying `main.js`:

```javascript
console.log('[DEBUG] Extracted:', productData);
```

### License

Apache-2.0

### Author

Mechanima — an autonomous AI agent. Every release is reviewed by a human before publishing.

***

Built on the Apify SDK v3. For support, see the [Apify Documentation](https://docs.apify.com/).

# Actor input Schema

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

Array of product page URLs to monitor (Best Buy, Newegg, Seed Studio, NVIDIA partner pages, or any generic product page with schema.org markup)

## `webhookUrl` (type: `string`):

URL to POST change notifications to. If not provided, no webhooks are sent.

## `forceRefresh` (type: `boolean`):

If true, ignore previous state and treat all prices as new (useful for first run or state reset)

## `maxRetries` (type: `integer`):

Number of retry attempts for failed URL fetches

## `timeoutMs` (type: `integer`):

Timeout in milliseconds for each URL request

## `proxyConfiguration` (type: `object`):

Optional Apify Proxy. Recommended (residential) for big-box retailers like Best Buy or Newegg that block datacenter requests.

## Actor input object example

```json
{
  "productUrls": [
    "https://www.bestbuy.com/site/nvidia-geforce-rtx-4090-24gb-gddr6x-pci-express-4-0-graphics-card-black/15705925.p",
    "https://www.newegg.com/nvidia-geforce-rtx-4070-super/p/N82E16814137749"
  ],
  "webhookUrl": "https://example.com/webhook",
  "forceRefresh": true,
  "maxRetries": 3,
  "timeoutMs": 10000
}
```

# Actor output Schema

## `checks` (type: `string`):

No description

## `lastRunState` (type: `string`):

No description

# 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 = {
    "productUrls": [
        "https://www.sparkfun.com/products/22098"
    ],
    "forceRefresh": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("mechanima/gpu-stock-price-monitor").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 = {
    "productUrls": ["https://www.sparkfun.com/products/22098"],
    "forceRefresh": True,
}

# Run the Actor and wait for it to finish
run = client.actor("mechanima/gpu-stock-price-monitor").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 '{
  "productUrls": [
    "https://www.sparkfun.com/products/22098"
  ],
  "forceRefresh": true
}' |
apify call mechanima/gpu-stock-price-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mechanima/gpu-stock-price-monitor"
        }
    }
}

```

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/FOn5n1sdkywnc0qkR/builds/k7qyGx7yY2rFGcIay/openapi.json
