Blog
Build a Price Monitoring Agent in 10 Lines
Build an automated price monitoring agent that tracks Amazon and Walmart prices using Unbrowse's shadow API discovery. No scraping, no browser automation — just clean API calls discovered from real browsing traffic.
Build a Price Monitoring Agent in 10 Lines
Price monitoring is one of the most common automation tasks on the web. Teams build scrapers with Puppeteer, Playwright, or Selenium — then spend weeks maintaining them as sites change their HTML. CSS selectors break. Rate limits bite. CAPTCHAs appear.
There is a better way. Every e-commerce site already has internal APIs that power their product pages. Amazon, Walmart, Best Buy — they all fetch price data from structured JSON endpoints behind the scenes. Unbrowse discovers these shadow APIs automatically and lets your agent call them directly.
No browser. No selectors. No maintenance. Just structured data.
The Problem With Traditional Price Scrapers
A typical Playwright price scraper looks something like this:
// The old way: brittle, slow, expensive
const browser = await playwright.chromium.launch();
const page = await browser.newPage();
await page.goto('https://www.amazon.com/dp/B0EXAMPLE');
await page.waitForSelector('.a-price-whole');
const price = await page.$eval('.a-price-whole', el => el.textContent);
await browser.close();
This works until it doesn't. Amazon changes their class names regularly. The page takes 3-8 seconds to load. You need a full Chromium instance consuming 200+ MB of RAM. And if you run this at scale, you hit bot detection within minutes.
The Unbrowse Approach: Call the API Directly
When you visit an Amazon product page, the browser doesn't parse HTML to show you the price. It calls internal APIs. Unbrowse discovers these APIs by observing real browsing traffic, then lets your agent call them directly.
Here is the complete price monitoring agent:
import { Unbrowse } from "@unbrowse/sdk";
const unbrowse = new Unbrowse();
const products = [
{ name: "MacBook Air M3", url: "https://www.amazon.com/dp/B0CX23V2ZK" },
{ name: "Sony WH-1000XM5", url: "https://www.amazon.com/dp/B0BX2L8PBT" },
{ name: "iPad Air", url: "https://www.walmart.com/ip/12345678" },
];
for (const product of products) {
const result = await unbrowse.resolve({
intent: `get current price for ${product.name}`,
url: product.url,
});
console.log(`${product.name}: $${result.data?.price}`);
}
That is the entire agent. Ten lines of meaningful code. No browser launch, no selector maintenance, no bot detection dance.
How It Works Under the Hood
When Unbrowse sees a URL it hasn't encountered before, it does the following:
-
Checks the marketplace: Has any other agent already discovered APIs for this domain? If yes, use the cached route immediately. This takes about 50ms.
-
Discovers shadow APIs: If no cached route exists, Unbrowse opens a lightweight browser session powered by Kuri (a 464KB Zig-native CDP broker with ~3ms cold start), navigates to the page, and captures all network traffic. It identifies the API endpoints that return structured data — prices, availability, reviews, everything.
-
Reverse-engineers the contract: Unbrowse analyzes the discovered endpoints, extracts URL templates, query parameters, headers, and authentication patterns. It builds a reusable API contract.
-
Publishes to the marketplace: The learned route goes into the shared marketplace so the next agent that needs pricing from this domain gets it instantly.
-
Returns structured data: Your agent gets clean JSON with the price, currency, availability, and any other fields the shadow API exposes.
The first resolve for a new domain takes a few seconds. Every subsequent resolve — by you or any other agent — takes milliseconds.
Adding Price Alerts
Once you have reliable price data, adding alerts is trivial:
import { Unbrowse } from "@unbrowse/sdk";
const unbrowse = new Unbrowse();
const watchlist = [
{ name: "MacBook Air M3", url: "https://www.amazon.com/dp/B0CX23V2ZK", target: 999 },
{ name: "AirPods Pro", url: "https://www.amazon.com/dp/B0D1XD1ZV3", target: 189 },
];
async function checkPrices() {
for (const item of watchlist) {
const result = await unbrowse.resolve({
intent: `get current price for ${item.name}`,
url: item.url,
});
const currentPrice = result.data?.price;
if (currentPrice && currentPrice <= item.target) {
console.log(`ALERT: ${item.name} dropped to $${currentPrice} (target: $${item.target})`);
// Send notification via email, Slack, Telegram, etc.
}
}
}
// Run every hour
setInterval(checkPrices, 60 * 60 * 1000);
checkPrices();
Tracking Price History
For more sophisticated monitoring, store historical prices and detect trends:
import { Unbrowse } from "@unbrowse/sdk";
import { writeFileSync, readFileSync, existsSync } from "fs";
const unbrowse = new Unbrowse();
const HISTORY_FILE = "./price-history.json";
function loadHistory() {
if (existsSync(HISTORY_FILE)) {
return JSON.parse(readFileSync(HISTORY_FILE, "utf-8"));
}
return {};
}
function saveHistory(history) {
writeFileSync(HISTORY_FILE, JSON.stringify(history, null, 2));
}
async function trackPrice(url, name) {
const result = await unbrowse.resolve({
intent: `get current price and availability for ${name}`,
url,
});
const history = loadHistory();
if (!history[url]) history[url] = [];
history[url].push({
timestamp: new Date().toISOString(),
price: result.data?.price,
available: result.data?.inStock ?? true,
});
saveHistory(history);
return result.data;
}
Multi-Retailer Price Comparison
One of the most powerful patterns is comparing prices across retailers for the same product:
import { Unbrowse } from "@unbrowse/sdk";
const unbrowse = new Unbrowse();
const product = "Sony WH-1000XM5";
const retailers = [
{ name: "Amazon", url: "https://www.amazon.com/dp/B0BX2L8PBT" },
{ name: "Walmart", url: "https://www.walmart.com/ip/sony-wh-1000xm5" },
{ name: "Best Buy", url: "https://www.bestbuy.com/site/sony-wh-1000xm5/6505727.p" },
];
const prices = await Promise.all(
retailers.map(async (r) => {
const result = await unbrowse.resolve({
intent: `get current price for ${product}`,
url: r.url,
});
return { retailer: r.name, price: result.data?.price };
})
);
prices.sort((a, b) => (a.price || Infinity) - (b.price || Infinity));
console.log(`Best price for ${product}:`, prices[0]);
Because Unbrowse calls APIs directly instead of rendering pages, you can check all three retailers in parallel. The total time is roughly the time of the slowest single request — usually under 2 seconds for cached routes.
Performance Comparison
| Metric | Playwright Scraper | Unbrowse Agent |
|---|---|---|
| Time per price check | 3-8 seconds | 50-200ms (cached) |
| Memory usage | 200+ MB (Chromium) | ~15 MB |
| Maintenance | Weekly selector fixes | Zero — APIs are stable |
| Bot detection risk | High | None — uses real API patterns |
| Lines of code | 30-50 per retailer | 5-10 total |
Running on a Schedule
For production use, run your price monitor as a cron job or scheduled task. Since Unbrowse saves progress incrementally and cached routes persist, restarts are instant:
# crontab -e
0 * * * * cd /path/to/monitor && node price-agent.js >> prices.log 2>&1
Earning From Your Routes
Every shadow API that Unbrowse discovers gets published to the marketplace. When other agents use your discovered routes, you earn x402 micropayments. A price monitoring agent that covers major retailers is effectively mining valuable routes that other agents will reuse.
First movers get a 2x reward multiplier. If you are the first to index a retailer's pricing API, every subsequent agent that checks prices on that site generates earnings for you.
Getting Started
Install Unbrowse and start monitoring prices in under a minute:
git clone --single-branch --depth 1 https://github.com/unbrowse-ai/unbrowse.git ~/unbrowse
cd ~/unbrowse && ./setup --host off
Or install the SDK for programmatic use:
npm install @unbrowse/sdk
The first time you resolve a product URL, Unbrowse discovers the shadow API. Every time after that, it is a direct API call. Your price monitoring agent gets faster over time, not slower.
Stop maintaining scrapers. Start calling the APIs that were always there.