A 1×1 tracking pixel on Vercel + Upstash Redis.
Opens, clicks, campaigns, webhooks, and a live dashboard — fully serverless.
Most email/marketing tools bury open tracking behind a paywall and a SaaS account. This is the smallest useful version: drop a <img> tag in your email, get opens + clicks + per-campaign analytics in a dashboard you own — for the cost of a free Vercel + Upstash plan.
/api/track— returns a 1×1 transparent PNG, logs the open/api/click— logs the click, then 302-redirects (open-redirect-safe: onlyhttp/httpsallowed)- Campaign tracking —
?campaign=param on both endpoints - Live dashboard — totals, per-day chart, per-campaign breakdown, recent events with campaign filter
- Webhooks — set
WEBHOOK_URLto fan out every event in real time - O(1) analytics — counters maintained on write so
/api/statsdoesn't scan logs - Zero frontend deps — hand-rolled SVG chart, no charting library
git clone https://github.com/anujarkitekt/pixel-tracker-vercel.git
cd pixel-tracker-vercel
npm install
cp .env.local.example .env.local # fill in Upstash creds
npm run devOpen http://localhost:3000 for the dashboard.
Track an email open:
<img src="https://your-app.vercel.app/api/track?id=user-123&campaign=spring-sale" width="1" height="1" />Track a click (wraps the destination URL):
<a href="https://your-app.vercel.app/api/click?id=user-123&campaign=spring-sale&url=https%3A%2F%2Fexample.com%2Flanding">
Shop now
</a>Get aggregated stats:
curl https://your-app.vercel.app/api/stats
# { totals: { track, click }, byCampaign: [...], byDay: [...] }| Variable | Required | Purpose |
|---|---|---|
UPSTASH_REDIS_REST_URL |
yes | Upstash Redis REST URL |
UPSTASH_REDIS_REST_TOKEN |
yes | Upstash Redis REST token |
WEBHOOK_URL |
no | If set, every event is POSTed here (fire-and-forget, 2s timeout) |
DASHBOARD_USER |
no | Basic-Auth username for /, /api/logs, /api/stats (default admin) |
DASHBOARD_PASSWORD |
no | Basic-Auth password. If unset, auth is disabled. /api/track and /api/click always stay public. |
Client (email / web)
│
▼
/api/track ──┐
/api/click ──┼──▶ recordEvent ──▶ Upstash Redis (logs list + counters)
│ ├──▶ Webhook (optional)
│
▼
/api/stats, /api/logs ──▶ Dashboard (/)
Redis layout:
logs— capped list (newest 1000)stats:total:{track|click}— counterstats:campaign:<name>:{track|click}— counterstats:day:<YYYY-MM-DD>:{track|click}— countercampaigns,days— sets of known keys
- Apple Mail and Gmail proxy/preload images — opens may be inflated or anonymized
- Some clients block remote images by default (counts will under-report)
- Click tracking only redirects
http/httpsURLs (intentional, blocks open-redirect abuse)
- Per-recipient unique-open detection —
SADD unique:track <id>→ SCARD on read - CSV export —
/api/export.csv, "Download CSV" button on the dashboard (auth-gated by middleware) - Geo-IP enrichment — country flag + city, via Vercel
x-vercel-ip-*headers (no extra deps) - Per-campaign unique counts — Unique + Devices columns in the campaign table
- Hashed UA/IP fingerprint for device dedup — SHA-256 of
ip|userAgent, 16-hex truncated - TTL on per-event sets (
unique:*,device:*) so abandoned campaigns don't grow forever - Per-day unique/device split (data layout supports it; UI room is the constraint)
MIT © Anuj Singh