| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A free, unofficial public API for fuel prices in Pakistan.
There's no official government/OGRA API for this, so this project scrapes public price pages from PSO, Shell Pakistan, and PakWheels, normalizes the results, and re-serves them as a small, rate-limited JSON API — plus one page showing current prices and live docs.
Live: fuel.trackmate.page · API base: https://fuel.trackmate.page/api
Fuel prices in Pakistan are set by OGRA on a biweekly cycle and published as plain web pages by PSO, Shell, and PakWheels — but there's no structured, public API for them. This project fills that gap: three small scrapers feed one normalized schema, so any app, script, or bot can pull current or historical prices with a single curl.
No install, no key — just call it:
curl https://fuel.trackmate.page/api/prices{
"count": 2,
"prices": [
{
"source": "pso",
"product": "petrol",
"price_pkr": 299.5,
"unit": "litre",
"city": null,
"effective_date": null,
"scraped_at": "2026-07-04T16:21:28.663Z"
},
{
"source": "shell",
"product": "hsd",
"price_pkr": 309.5,
"unit": "litre",
"city": null,
"effective_date": null,
"scraped_at": "2026-07-04T16:21:28.663Z"
}
]
}Base URL: https://fuel.trackmate.page/api. Public, no API key needed. The live docs page (/) has the same reference with copy-pasteable examples and a "Copy docs for AI" button if you want to hand this whole reference to an LLM/agent.
Latest known price for every source/product/city combination.
curl https://fuel.trackmate.page/api/prices| Status | Meaning |
|---|---|
| 200 | OK |
| 429 | rate_limited — see Rate limiting |
Latest prices from one source only. :source is one of pso, shell, pakwheels.
curl https://fuel.trackmate.page/api/prices/pso{
"count": 2,
"prices": [
{
"source": "pso",
"product": "petrol",
"price_pkr": 299.5,
"unit": "litre",
"city": null,
"effective_date": null,
"scraped_at": "2026-07-04T16:21:28.663Z"
},
{
"source": "pso",
"product": "octane_plus",
"price_pkr": 340,
"unit": "litre",
"city": "Karachi",
"effective_date": null,
"scraped_at": "2026-07-04T16:21:28.663Z"
}
]
}| Status | Meaning |
|---|---|
| 200 | OK |
| 400 | invalid_source — :source isn't one of pso, shell, pakwheels |
| 429 | rate_limited |
Historical snapshots. product and source are optional filters; days defaults to 30, max 365.
curl "https://fuel.trackmate.page/api/history?product=hsd&days=90"{
"count": 2,
"days": 1,
"prices": [
{
"source": "pso",
"product": "petrol",
"price_pkr": 299.5,
"unit": "litre",
"city": null,
"effective_date": null,
"scraped_at": "2026-07-04T16:18:48.887Z"
},
{
"source": "pakwheels",
"product": "petrol",
"price_pkr": 297.53,
"unit": "litre",
"city": null,
"effective_date": "04-July-2026",
"scraped_at": "2026-07-04T16:18:49.736Z"
}
]
}| Status | Meaning |
|---|---|
| 200 | OK |
| 400 | invalid_source |
| 429 | rate_limited |
Scraper status — when it last ran and which sources succeeded/failed. Useful for monitoring: if a source sits in failed_sources for more than a day or two, its page probably changed shape and the scraper needs a fix.
curl https://fuel.trackmate.page/api/health{
"status": "ok",
"last_scrape": {
"ran_at": "2026-07-04T16:21:37.862Z",
"ok_sources": ["shell", "pakwheels", "pso"],
"failed_sources": []
}
}| Status | Meaning |
|---|---|
| 200 | OK |
| 429 | rate_limited |
/api/* is rate-limited via an in-memory token bucket per IP: 30-request burst, refilling at 1 token/sec (~60 requests/minute sustained). A 429 response includes a Retry-After header (seconds) plus X-RateLimit-Limit and X-RateLimit-Remaining. Please cache responses client-side — prices only change a few times a month.
This lives in-process, so it resets on deploy and is per-dyno if this ever scales beyond one web dyno; fine for a single-dyno deployment.
POST /api/admin/refresh is a separate, non-public endpoint protected by an x-admin-secret header — used to force an immediate re-scrape right after a price hike is announced, instead of waiting for the next scheduled run.
src/
scrapers/
http.ts # shared fetch + text-extraction helpers
types.ts # normalized price schema
shell.ts # Shell price board scraper (reads their AEM model.json)
pakwheels.ts # PakWheels petroleum prices table scraper
pso.ts # PSO per-city Octane+ grid + national Premier/Diesel/LPG
index.ts # orchestrator — runs all 3, tolerates partial failure
db/
client.ts # Turso/libSQL client
schema.ts # table creation + queries
api/
routes.ts # /api/prices, /api/prices/:source, /api/history, /api/health, /api/admin/refresh
rateLimiter.ts # in-memory token bucket middleware
web/
index.html # the one public page (prices + docs)
server.ts # app entrypoint
scripts/
scrape-once.ts # entrypoint for Heroku Scheduler (or any cron)
Stack: Bun + TypeScript, Hono, Turso (hosted libSQL/SQLite). Scraping is plain fetch + regex/text-pattern matching — no headless browser, since all sources are either server-rendered or expose a JSON data endpoint.
bun install
cp .env.example .env # fill in TURSO_* if you have a Turso db, or leave unset for local.db
bun run dev # http://localhost:3000Run a scrape manually:
bun run scrapeIssues and PRs are welcome — especially:
Before opening a PR: run bun run scrape locally against the target source and make sure it still parses; these are text-pattern scrapers, so a page wording change is the most common way they break.
MIT — this is meant to be a public utility. PRs adding more sources or fixing a broken scraper are welcome.
| Back | FazBrowse Home | New Git URL |