skill
skill-price-tracker
Open-source Agent Skill: track product page prices and stock, alert on drops/rises/sold-out, export CSV/JSON price curves.
Set up
Skills are installed by placing their folder where your agent loads skills.
The download comes from github.com/samjia12/skill-price-tracker itself, not from our server, so what you get is exactly what its author published.
Install instructions come from the README
README
synced Oct 23, 2026skill-price-tracker
Agent Skill that watches product page URLs, records price and stock snapshots, and alerts on drops, rises, and sold-out events. History can be exported as CSV or JSON price-curve data.
Use it from a CLI, from python -m price_tracker, or as a Grok / Codex-style skill via SKILL.md.
Features
- Fetch many product URLs with per-host rate limiting, retries, and a response size cap
- Extract price / currency / stock from JSON-LD (
Product/Offer), Open Graph meta tags, and optional per-site regex selectors - SQLite snapshot history with successful-vs-failed fetch tracking
- Alerts:
price_drop,price_rise,sold_out,back_in_stock - Drop / rise thresholds by percent and / or absolute amount
- Concurrent checks with stable URL order in the result
- Export price curves to CSV or JSON
- Zero runtime dependencies (Python 3.9+ standard library)
Install
git clone https://github.com/samjia12/skill-price-tracker.git
cd skill-price-tracker
python3 -m pip install -e ".[dev]" # optional; pytest is the only extra
You can skip the install and call python3 scripts/price_tracker.py from the repo root. That script puts src/ on sys.path.
Quick start
# 1) Write a default config
python3 scripts/price_tracker.py init-config -o tracker.json
# 2) Check URLs once (creates ./price_tracker.db)
python3 scripts/price_tracker.py check \
--url "https://shop.example/p/widget" \
--config tracker.json \
--format json
# 3) Poll on an interval (Ctrl+C to stop, or bound it)
python3 scripts/price_tracker.py watch -i examples/urls.txt --interval 3600 --max-runs 1
# 4) Export the curve
python3 scripts/price_tracker.py export --format json -o curve.json
Offline (no network) demos:
python3 examples/offline_check.py
python3 examples/threshold_alerts.py
python3 examples/export_price_curve.py
Usage examples
1. First check then a price drop
Input (from examples/offline_check.py, two local HTML fixtures):
https://shop.example/p/widget # JSON-LD Offer price 19.99 USD, InStock
https://shop.example/p/mug # og:price 12.50 EUR, instock
# second poll: widget HTML price rewritten to 14.99
Output (second run, abbreviated):
{
"snapshots": [
{
"url": "https://shop.example/p/widget",
"title": "Widget Pro",
"price": "14.99",
"currency": "USD",
"in_stock": true,
"error": null
}
],
"alerts": [
{
"kind": "price_drop",
"message": "Price dropped from 19.99 USD to 14.99 USD for Widget Pro",
"percent_change": -25.0125,
"amount_change": "-5.00"
}
],
"rejected": []
}
2. Thresholds filter noise
Input (python3 examples/threshold_alerts.py): previous price 100.00, new price 97.00 vs 80.00, then stock flips to sold out.
Output:
any-change config, -$3: price_drop
10% and $5 config, -$3: (none)
10% and $5 config, -$20: price_drop
sold out: sold_out
A 3% dip is ignored when drop_threshold_percent is 10 and drop_threshold_amount is 5.00. A $20 / 20% drop clears both gates. Stock changes are independent of price thresholds.
3. Export a four-point curve
Input (python3 examples/export_price_curve.py): four snapshots over four days — 29.99 in stock, 27.50 in stock, 27.50 sold out, 24.00 back in stock.
Output (CSV excerpt):
url,fetched_at,title,price,currency,in_stock,http_status,error
https://shop.example/p/widget,2026-08-20T12:00:00+00:00,Widget Pro,29.99,USD,true,,
https://shop.example/p/widget,2026-08-21T12:00:00+00:00,Widget Pro,27.50,USD,true,,
https://shop.example/p/widget,2026-08-22T12:00:00+00:00,Widget Pro,27.50,USD,false,,
https://shop.example/p/widget,2026-08-23T12:00:00+00:00,Widget Pro,24.00,USD,true,,
JSON export groups those rows into one product with a points[] array (t, price, in_stock) for charting.
4. Reject bad URLs, keep the rest
Input:
python3 scripts/price_tracker.py check \
--url "javascript:alert(1)" \
--url "https://shop.example/p/widget" \
--format json
Output: rejected contains the javascript URL with reason URL scheme is not allowed: javascript. The http(s) URL is still fetched. If every URL is invalid, the process exits with code 2 (EmptyInputError).
Configuration
python3 scripts/price_tracker.py init-config -o tracker.json writes every field. Common knobs:
| Field | Meaning | Default |
|---|---|---|
drop_threshold_percent | Minimum % drop to alert | 0 (any drop) |
drop_threshold_amount | Minimum absolute drop | null |
rise_threshold_percent / rise_threshold_amount | Same for rises | 0 / null |
poll_interval_seconds | watch sleep between runs | 3600 |
rate_limit_per_host | Requests per second per host | 1.0 |
max_concurrent | Worker threads | 4 |
max_retries | Extra attempts on 429 / 5xx / timeout | 3 |
selectors | Per-URL-pattern regexes that override JSON-LD / meta | [] |
db_path | SQLite file | price_tracker.db |
Selector example (see examples/config.json):
{
"url_pattern": "shop\\.example",
"price_regex": "data-sale=\"([0-9.]+)\"",
"stock_regex": "data-stock=\"([^\"]+)\""
}
url_pattern is a regular expression matched against the product URL. The first capturing group of each regex is the extracted value.
CLI
| Command | Purpose |
|---|---|
check | Fetch each URL once, store snapshots, print alerts |
watch | Repeat check every poll_interval_seconds |
export --format csv|json | Dump stored history (optional --url, --since, --until) |
history --url URL | Print the timeline for one product |
init-config -o FILE | Write a default JSON config |
Global-ish flags: --url (repeatable), --input-file / -i, --config, --db, --format.
Exit codes: 0 ok, 1 runtime / config / I/O error, 2 empty or invalid input, 130 interrupted.
How extraction works
- JSON-LD
Product/Offer/AggregateOffer(including@graph) product:price:amount,og:title,itemprop=price,data-*price*attributes- Matching
selectorsfrom config (these win when they produce a value)
Stock maps schema.org InStock / OutOfStock / SoldOut and common phrases (sold out, in stock). Pages with neither a price nor a stock signal are stored with error set and do not generate price/stock alerts.
FAQ
Does this work on JavaScript-only storefronts?
Only if the HTML still contains JSON-LD, Open Graph tags, or a regex-able string. There is no headless browser. Add a selectors rule when the markup is stable but not structured.
Will I get banned?
The default rate limit is one request per host per second, with 429 retries honoring Retry-After. Keep max_concurrent modest and obey each site's terms. This tool is for pages you are allowed to fetch.
Why is percent_change a float?
Amounts stay Decimal (serialized as strings). Percent is only used to compare against a threshold; JSON numbers are floats.
Can I point this at Amazon / Taobao / Shopify?
Shopify product pages often expose JSON-LD and work as-is. Amazon and Taobao frequently bot-check or omit offers in the raw HTML — expect error: could not extract price or stock from page unless you supply a selector and a permitted fetch path.
Where is the database?
db_path in config, or --db. It is a regular SQLite file; .gitignore already ignores *.db.
How do I run tests?
python3 -m pip install pytest && python3 -m pytest
Contributing
- Fork and clone; create a branch
feat/...orfix/... - Keep runtime deps at zero unless the feature cannot be done in stdlib
- Add or extend tests under
tests/(including a boundary case if you touch validation / I/O) - Run
python3 -m pytest— do not submit with failures - English for code comments and public docs; user-facing CLI messages stay English
- Open a PR with a short summary, test output, and any config/CLI flag changes
Please file issues with: the URL pattern (redact secrets), the HTML excerpt you expected to parse, config JSON, and the snapshot error string.
License
MIT — see LICENSE.