Public
Configurable Apple refurb stock tracker with multi-store alerts.
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Configurable Apple Certified Refurbished stock tracker. Polls multiple Apple storefronts, detects stock changes, and alerts via email + webhook.
cron.ts (every 15 min)
└─▸ For each store (IE, US, UK):
├─ Fetch refurb page
├─ Parse REFURB_GRID_BOOTSTRAP tiles
├─ Diff against last known state
├─ Store events in SQLite
└─ Dispatch alerts (email + webhook)
main.ts (HTTP API)
└─▸ /snapshot /history /cadence /dwell /batches /health
main.ts — HTTP API (Hono). Serves stock data and analytics.
cron.ts — Interval entrypoint. Polls stores, detects changes, alerts.
tracker-lib.mjs — Core logic: store configs, parsers, schema, alerts.
README.md — This file.
- Set the interval schedule — Open
cron.tsin the Val Town editor and set the Cron trigger. Recommended: every 15 minutes (free tier minimum). - Set env vars (optional):
API_KEY— Gates the HTTP API. If unset, the API is open.ALERT_WEBHOOK_URL— Webhook URL for Slack/Discord/custom. Only needed if you usechannels: ["webhook"]in alert rules.
- Customize alert rules — Edit
DEFAULT_ALERT_RULESintracker-lib.mjsto match the specs you care about.
Add a config object to STORE_CONFIGS in tracker-lib.mjs:
{
id: "de",
label: "Apple Germany",
region: "DE",
baseUrl: "https://www.apple.com",
refurbPath: "/de/shop/refurbished/mac",
currency: "EUR",
vatRate: 0.19,
}
No other code changes needed.
Rules are defined in DEFAULT_ALERT_RULES (in tracker-lib.mjs). Each rule has:
| Field | Description |
|---|---|
name | Human-readable name |
match | Filter object — see below |
alertOn | "appeared", "disappeared", "price_change", or "all" |
channels | ["email"], ["webhook"], or both |
| Field | Type | Example |
|---|---|---|
storeIds | string[] | ["ie"] — only IE store |
model | string | "MacBook Pro" — substring match |
minMemoryGb | number | 24 |
minStorageGb | number | 512 |
screenInches | number[] | [14, 15, 16] |
chip | string | "M4 Pro" — substring match |
maxPriceCents | number | 200000 — price ceiling |
partNumberPatterns | string[] | ["FW*"] — substring match |
An empty match: {} catches everything — useful as a catch-all.
// Any MacBook appearing → email.
{ name: "Any MacBook", match: {}, alertOn: "appeared", channels: ["email"] }
// 24GB+ MacBook Pro 14/15-inch → email + Slack.
{ name: "Pro 24GB+", match: { model: "MacBook Pro", minMemoryGb: 24, screenInches: [14, 15] },
alertOn: "appeared", channels: ["email", "webhook"] }
// Anything under €1,500 → webhook only.
{ name: "Budget picks", match: { maxPriceCents: 150000 }, alertOn: "appeared", channels: ["webhook"] }
All endpoints require ?key=<API_KEY> if the API_KEY env var is set.
| Endpoint | Description | Key params |
|---|---|---|
GET / | This README | — |
GET /snapshot | Current stock | store, model |
GET /history | Change events | store, part, kind, since, until, limit |
GET /cadence | Appearances by hour/day | store |
GET /dwell | Time-in-stock stats | store |
GET /batches | Batch arrivals (3+ items) | store, min |
GET /health | Run health per store | — |
| Variable | Required | Purpose |
|---|---|---|
API_KEY | No | Gates HTTP API |
ALERT_WEBHOOK_URL | No | Webhook URL for alert dispatch |