Every restaurant in Pakistan, turned into a queryable warehouse
Pakistan's richest restaurant dataset — menus, prices, cuisines, and locations — lives locked inside one app. This pipeline unlocks it: it discovers 29,000+ restaurants from Foodpanda's own sitemap, reverse-engineers the internal vendor API, and lands full menus in a normalized PostgreSQL warehouse ready for price intelligence and discovery products.
Arham Mirkar
DataLayer — Enterprise Data Infrastructure
The data exists — it just isn't yours
If you want to answer a simple question — what does biryani cost across Karachi, and who's cheapest?— there's no dataset to query. Menus, prices, ratings, and locations for tens of thousands of restaurants sit inside Foodpanda, visible one screen at a time and impossible to analyze in bulk.
Extracting it isn't a matter of a quick scrape. The vendor data is served by an undocumented internal API, gated on your delivery location, and sat behind bot protection. Doing it at national scale — reliably, over hours, without getting blocked — is the actual engineering problem.
Six stages, one national dataset
The pipeline is deliberately boring where it should be and clever only where it has to be. The cleverness is in how it gets clean data out; the rest is disciplined, resumable plumbing.
Discover
SitemapInstead of crawling listing pages city by city, the pipeline pulls Foodpanda's own adventure-map sitemap and extracts every restaurant URL — 29,115 unique vendor codes for all of Pakistan in a single pass.
Find the door
Access insightFoodpanda gates vendor data behind a delivery-range check tied to your coordinates. The key discovery: omitting latitude and longitude skips that gate entirely and returns the full vendor and menu payload — no cookies, no bot puzzle.
Fetch
Internal vendor APIEach code hits the internal v5 vendor endpoint with a nested include that returns the whole menu tree — categories, products, and price variations — in one request, GraphQL-style.
Normalize
StructureRaw JSON is decomposed into brands, cities, cuisines, vendors, menu sections, products, and price SKUs — with cuisines stored both as a queryable junction table and as JSONB for fidelity.
Load
PostgreSQLEverything upserts into an 11-table warehouse with ON CONFLICT DO UPDATE, so a re-run refreshes prices and availability instead of duplicating the country.
Endure
Resume + reconnectA checkpoint every 50 vendors, an auto-reconnecting database wrapper, and a run log mean a multi-hour national crawl can crash, resume, and finish without starting over.
The door nobody locked
Most people try to defeat bot protection head-on — harvesting cookies, solving challenges, rotating proxies. An early prototype here did exactly that with a headless browser. The production pipeline found a simpler path: the API only enforces its delivery-range check when you send coordinates. Leave them out, and it happily returns the full vendor and menu payload.
Held at ~0.85s between requests, with user-agent and session rotation every 50 calls and an automatic cooldown on any 403. Across the recorded national crawl, that discipline produced zero bot blocks.
A schema built for questions
A single JSON blob per restaurant is useless for analysis. The payload is decomposed into 11 related tables so you can ask real questions — chain density by city, cuisine pricing, menu-size distributions — with plain SQL.
Built to run for hours
Ingesting a whole country takes hours, and anything that runs for hours will be interrupted. The pipeline treats failure as normal.
Arham Mirkar
Founder & Data Engineer, DataLayer
“I spent a while fighting the bot protection the hard way before I noticed the API only cared about coordinates when I gave it some. That's the lesson I keep relearning in data work — the elegant path usually beats the brute-force one, if you're patient enough to look for it.”
Technology
| Layer | Technologies |
|---|---|
| Discovery | Foodpanda adventure-map sitemap, regex URL extraction → vendor code inventory |
| Access | Reverse-engineered internal v5 vendor API, coordinate-free bypass, browser-faithful headers |
| Evasion | Rotating Perseus session IDs, 5-UA pool, session rotation every 50 requests, 0.85s pacing, 403 cooldown + retries |
| Storage | PostgreSQL, 11 normalized tables, ON CONFLICT upserts, JSONB for cuisines / schedules / metadata |
| Resilience | Resume checkpoints, auto-reconnecting DB wrapper, sync_log run accounting, profile-only mode |
| Language | Python, requests, psycopg2 (no ORM) |
Need a dataset that doesn't exist yet?
If the data is out there in some messy form, it can be discovered, structured, and served. That's the whole job.