Quickwatch tracks your Zomato delivery performance -- promised vs actual delivery times, worst restaurants, and trends.
Zomato promises you a delivery time, but there is no way to know if they are consistently late, which restaurants are the worst offenders, or whether things are getting better or worse over time. This tool captures your real delivery data by intercepting the Zomato app's API traffic, stores it in a local SQLite database, and presents it through an analytics dashboard. There are two approaches to intercept traffic:
- (a) Patched APK on a real phone (recommended) -- Use
apk-mitmto patch the Zomato APK to remove SSL certificate pinning, install it on your phone, and route traffic through mitmproxy on your Mac via WiFi proxy. No root, no Frida, no emulator required. - (b) Android emulator with Frida (alternative) -- Run Zomato on an Android emulator on your Mac and use Frida to bypass SSL pinning at runtime. Requires a rootable emulator image and more setup.
Either way, you keep ordering as usual -- the tracker silently records every promised ETA, every status update, and every actual delivery timestamp.
The dashboard is a dark-themed, responsive web interface with five sections:
-
Summary Cards -- Three cards at the top showing Total Orders, On Time percentage (color-coded green/yellow/red), and Average Delay in minutes. These update instantly when you change the time filter.
-
Delivery Trends -- A dual-axis line chart showing Average Delay (left axis, red) and On Time % (right axis, green) over time. Each point represents a day. Hover to see the order count for that day.
-
Worst Restaurants -- A horizontal bar chart ranking the top 10 worst restaurants by average delay. Bars are color-coded: green (under 5 min), yellow (5-15 min), red (over 15 min). Hover to see the order count.
-
Cost vs Delay -- A scatter plot correlating order cost with delivery delay. Red dots are late orders, green dots are early. The Pearson correlation coefficient is displayed above the chart with a plain-English interpretation (e.g., "no significant correlation" or "higher cost orders tend to be delayed more").
-
Recent Orders -- A paginated table listing every order with date, restaurant name, cost, promised time, actual delivery time, and delay (color-coded). 20 orders per page with Previous/Next navigation.
All five sections respond to the time filter pills in the header: 7 Days, 30 Days, 90 Days, or All Time.
| Traffic Capture | Request Inspection |
|---|---|
![]() |
![]() |
| mitmproxy intercepting Zomato API traffic in real time | Inspecting captured request payloads and responses |
At a high level, the system has these components working together. See HOW_IT_WORKS.md for the full technical deep dive.
With the patched APK approach (recommended):
- A real Android phone runs the Zomato app patched with
apk-mitmto remove SSL certificate pinning - mitmproxy sits on your Mac, intercepting all API traffic routed through the phone's WiFi proxy
- No Frida, no root, no emulator -- the patched APK handles SSL pinning removal at the APK level
- Captured data is parsed, normalized, and stored in a local SQLite database
- A Flask dashboard serves the analytics UI at
localhost:5000
With the emulator + Frida approach (alternative):
- An Android emulator runs the real Zomato app on your Mac
- mitmproxy sits between the emulator and Zomato's servers, intercepting all API traffic
- Frida injects code into the running Zomato app to bypass SSL certificate pinning, root detection, and Frida detection -- without this, mitmproxy cannot see the encrypted traffic
- Captured data is parsed, normalized, and stored in a local SQLite database
- A Flask dashboard serves the analytics UI at
localhost:5000
Everything runs on your Mac (and optionally your Android phone). You need the following:
- macOS (Intel or Apple Silicon)
- Python 3.10+ -- check with
python3 --version - Homebrew -- install from https://brew.sh if you do not have it
- Android platform tools -- for
adb(needed for both approaches) - Node.js -- required for
apk-mitm(patched APK approach) - apk-mitm -- install with
npm install -g apk-mitm(patched APK approach only) - Android Studio (or the Android SDK command-line tools) -- only needed for the emulator approach
- Frida -- only needed for the emulator approach (
pip install frida-tools)
This quick start uses the recommended patched APK approach with a real Android phone.
# 1. Clone the repo
git clone <repo-url>
cd quickwatch
# 2. Install Python dependencies
pip install -r requirements.txt
# 3. Install system tools
brew install mitmproxy
brew install android-platform-tools
npm install -g apk-mitm
# 4. Connect your Android phone via USB and enable USB debugging
# (Settings -> Developer Options -> USB Debugging)
# 5. Pull the Zomato APK from your phone
adb pull $(adb shell pm path com.application.zomato | head -1 | sed 's/package://') /tmp/zomato-original.apk
# 6. Patch the APK to remove SSL pinning
apk-mitm /tmp/zomato-original.apk -o /tmp/zomato-patched.apk
# 7. Install the patched APK on your phone
adb install /tmp/zomato-patched.apk
# If you get an error about split APKs, see the Detailed Setup Guide below
# 8. Set your phone's WiFi proxy to your Mac's IP on port 8080
# (Settings -> WiFi -> long-press your network -> Modify -> Advanced -> Proxy -> Manual)
# Set hostname to your Mac's local IP (find it with: ipconfig getifaddr en0)
# Set port to 8080
# 9. Start mitmproxy and install its CA certificate on your phone
# Run mitmproxy first, then visit http://mitm.it on your phone's browser
# and install the certificate for Android
# 10. Start tracking (first run enters discovery mode automatically)
python start.py --no-frida
# 11. Place an order through the Zomato app on your phone
# Track it until delivery, then press Ctrl+C
# 12. Generate endpoint config from the captured traffic
python start.py configure
# 13. Start normal tracking with the dashboard
python start.py --no-frida --with-dashboard
# 14. Open the dashboard
open http://localhost:5000This is the recommended approach. It uses a real Android phone with a patched version of the Zomato APK that has SSL certificate pinning removed. No root access, no Frida, and no emulator required. It is simpler to set up, more stable (no emulator crashes or Frida detection issues), and works with your actual phone that you already use for ordering.
# Install Homebrew (if not installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install mitmproxy
brew install mitmproxy
# Install Android platform tools (for adb)
brew install android-platform-tools
# Install apk-mitm (requires Node.js)
npm install -g apk-mitm
# Install Python dependencies
pip install -r requirements.txtVerify everything is installed:
mitmdump --version
adb version
apk-mitm --version
python3 --version- On your Android phone, go to Settings -> About Phone
- Tap Build Number seven times to enable Developer Options
- Go back to Settings -> Developer Options
- Enable USB Debugging
- Connect your phone to your Mac via USB
- On the phone, approve the "Allow USB debugging?" prompt
Verify the connection:
adb devices
# You should see your device listedFirst, check if Zomato is installed as a single APK or a split APK (most modern apps use split APKs):
adb shell pm path com.application.zomatoIf you see a single base.apk path:
# Pull the APK
adb pull $(adb shell pm path com.application.zomato | head -1 | sed 's/package://') /tmp/zomato-original.apk
# Patch it
apk-mitm /tmp/zomato-original.apk -o /tmp/zomato-patched.apk
# Install the patched APK (this will replace the original Zomato app)
adb install /tmp/zomato-patched.apkIf you see multiple paths (split APK):
# Create a directory for the APK parts
mkdir -p /tmp/zomato-split
# Pull all APK parts
adb shell pm path com.application.zomato | sed 's/package://' | while read -r apk; do
adb pull "$apk" /tmp/zomato-split/
done
# Patch the base APK (apk-mitm handles the SSL pinning config in the base)
apk-mitm /tmp/zomato-split/base.apk -o /tmp/zomato-split/base-patched.apk
# Install all parts together using install-multiple
# Replace base.apk with the patched version in the install command
adb install-multiple /tmp/zomato-split/base-patched.apk /tmp/zomato-split/split_config.*.apkAfter installation, open the patched Zomato app and log in with your account. Your existing account data, addresses, and payment methods will all work as normal.
Your phone needs to route its traffic through mitmproxy running on your Mac. Both devices must be on the same WiFi network.
- Find your Mac's local IP address:
ipconfig getifaddr en0
# e.g., 192.168.1.42- On your phone, go to Settings -> WiFi
- Long-press (or tap the gear icon on) your connected WiFi network
- Select Modify Network (or Advanced)
- Set Proxy to Manual
- Set Hostname to your Mac's IP (e.g.,
192.168.1.42) - Set Port to
8080 - Save
Important: Your phone's internet will stop working until mitmproxy is running on your Mac. This is expected -- the phone is trying to route through a proxy that does not exist yet.
mitmproxy needs its CA certificate trusted on your phone so it can decrypt HTTPS traffic. The patched APK already removes SSL pinning for Zomato's own certificates, but the system still needs to trust mitmproxy's certificate.
- Start mitmproxy on your Mac:
mitmproxy- On your phone, open the browser and navigate to http://mitm.it
- Tap the Android button to download the certificate
- Follow the prompts to install it (you may need to set a screen lock if you have not already)
- On Android 11+, you may also need to go to Settings -> Security -> Encryption & Credentials -> Install a certificate -> CA certificate and install it from there
You can stop mitmproxy after installing the certificate (Ctrl+C). The tracker will start its own mitmproxy instance.
python start.py --no-fridaThe --no-frida flag tells the tracker to skip all Frida-related setup (no Frida server, no injection). SSL pinning is already handled by the patched APK.
The tool will:
- Check that mitmproxy and adb are installed
- Start mitmproxy on port 8080
- Begin capturing all traffic from
*.zomato.com
Now place an order through the Zomato app on your phone and track it all the way through delivery. This captures the full lifecycle of API calls. When the order is delivered, press Ctrl+C to stop.
The captured traffic is saved to discovery_log.json.
python start.py configureThis analyzes the discovery log and identifies which API endpoints correspond to order placement, tracking, delivery completion, and order details. Review the output and see Step 6 under Method B for more details on this process.
python start.py --no-fridaIn this mode, the tracker only captures relevant API calls, extracts specific fields, and stores them in the SQLite database.
# Dashboard only
python start.py dashboard
# Or capture + dashboard together
python start.py --no-frida --with-dashboardOpen http://localhost:5000 in your browser.
Use this approach if you do not have a spare Android phone, or if you prefer to keep everything on your Mac. This method runs Zomato in an Android emulator and uses Frida to bypass SSL pinning at runtime.
# Install Homebrew (if not installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install mitmproxy
brew install mitmproxy
# Install Android platform tools (for adb)
brew install android-platform-tools
# Install Frida tools
pip install frida-tools
# Install Python dependencies
pip install -r requirements.txtVerify everything is installed:
mitmdump --version
frida --version
adb version
python3 --versionYou need a Google APIs system image (NOT Google Play). Google APIs images allow root access via adb root, which is required for Frida to inject into the Zomato app.
If you have Android Studio installed:
- Open Android Studio
- Go to Tools -> Device Manager
- Click "Create Device"
- Choose a Pixel device (e.g., Pixel 9)
- Select a system image labeled "Google APIs" (NOT "Google Play Store") -- the arm64 / API 35 variant is recommended
- Finish the wizard and start the emulator
If you prefer command-line only:
# List available system images
sdkmanager --list | grep "system-images"
# Install a Google APIs system image (NOT google_apis_playstore)
sdkmanager "system-images;android-35;google_apis;arm64-v8a"
# Create an AVD (Android Virtual Device)
avdmanager create avd -n zomato_tracker -k "system-images;android-35;google_apis;arm64-v8a" -d pixel_9
# Start the emulator
emulator -avd zomato_trackerFrida needs a server component running on the device. The frida-server version must match your host Frida version exactly.
# 1. Verify your Frida version
frida --version
# e.g., 17.9.1
# 2. Enable root access on the emulator
adb root
# 3. Download frida-server matching your version and architecture
# Replace VERSION with your frida --version output
cd /tmp
curl -sL "https://github.com/frida/frida/releases/download/VERSION/frida-server-VERSION-android-arm64.xz" -o frida-server.xz
xz -d frida-server.xz
# 4. Push frida-server to the emulator
adb push /tmp/frida-server /data/local/tmp/frida-server
adb shell chmod 755 /data/local/tmp/frida-server
# 5. Start frida-server on the emulator
adb shell "/data/local/tmp/frida-server -D &"
# 6. Verify Frida can see the device
frida-ps -UYou should see a list of running processes. If you get a connection error, make sure adb root succeeded and frida-server is running.
Note: You need to restart frida-server each time you reboot the emulator. Run steps 2 and 5 again after a reboot.
Since the Google APIs image does not include the Play Store, you need to sideload the Zomato APK.
- Download the Zomato APK from a trusted source like APKMirror -- choose the arm64-v8a variant
- Install it on the emulator:
adb install ~/Downloads/zomato.apk- Open Zomato on the emulator and log in with your account
- Set your delivery address
python start.pyThe tool will:
- Check that mitmproxy, Frida, and adb are installed
- Verify the emulator is running and Zomato is installed
- Automatically set the HTTP proxy on the emulator
- Start mitmproxy on port 8080
- Start Frida to bypass SSL pinning on the Zomato app
Now place an order through the Zomato app on the emulator and track it all the way through delivery. This captures the full lifecycle of API calls. When the order is delivered, press Ctrl+C to stop.
The captured traffic is saved to discovery_log.json.
python start.py configureThis analyzes the discovery log and identifies which API endpoints correspond to:
- Order placement -- when you place an order
- Order tracking -- live status and ETA updates
- Delivery completion -- when the order is delivered
- Order details -- order information and restaurant data
It uses a combination of URL pattern matching and response body analysis to classify endpoints with a confidence level (high, medium, low). The output is saved to endpoints_config.json.
Review the generated config. The tool prints a summary showing which endpoints it identified, what fields it found, and its confidence level. If something looks wrong, you can manually edit endpoints_config.json.
Once endpoints are configured, every subsequent run uses normal capture mode:
python start.pyIn this mode, the tracker only captures relevant API calls (orders, tracking, delivery), extracts specific fields, and stores them in the SQLite database (zomato_tracker.db). It computes delay_minutes automatically when an order is delivered: actual delivery time - promised ETA.
Launch the dashboard:
# Dashboard only
python start.py dashboard
# Or capture + dashboard together
python start.py --with-dashboardOpen http://localhost:5000 in your browser. Use the time filter buttons (7 Days, 30 Days, 90 Days, All Time) to adjust the view. All charts and the orders table update in real time.
python start.py Start capture (auto-detects discovery or normal mode)
python start.py --no-frida Start capture without Frida (for patched APK approach)
python start.py --discover Force discovery mode (re-capture all traffic)
python start.py configure Analyze discovery log and generate endpoint config
python start.py dashboard Launch the web dashboard only (no capture)
python start.py --with-dashboard Start capture and dashboard together
python start.py status Show system status (DB stats, config state, tool checks)
python start.py --port 9090 Use a custom proxy port (default: 8080)
python start.py --dashboard-port 3000 Use a custom dashboard port (default: 5000)
--no-frida -- Skip all Frida-related setup and injection. Use this when running the patched APK approach on a real phone. The tracker will start mitmproxy but will not attempt to launch Frida or attach to the Zomato process. SSL pinning bypass is handled by the patched APK instead.
python start.py status gives you a quick overview:
- Database path and order count
- On-time percentage and average delay
- Endpoint configuration state (how many endpoints, their confidence levels)
- Discovery log entry count
- Whether required tools are installed
- Whether an emulator is currently running
quickwatch/
start.py CLI entry point -- manages all subcommands
requirements.txt Python dependencies (mitmproxy, frida-tools, flask)
zomato_tracker.db SQLite database (created at runtime)
endpoints_config.json Endpoint config (generated by configure command)
discovery_log.json Raw API traffic log (generated in discovery mode)
capture/
addon.py mitmproxy addon -- intercepts and processes traffic
endpoints.py URL pattern matcher and field extractor
configure.py Discovery log analyzer and config generator
frida_bypass.js Frida script for SSL pinning, root, and Frida detection bypass
store/
db.py SQLite database layer (WAL mode, thread-safe)
models.py Data models (Order, Restaurant, TrackingEvent)
dashboard/
app.py Flask app factory
routes.py API routes for the dashboard
templates/
index.html Dashboard HTML template
static/
css/style.css Dashboard styles (dark theme, responsive)
js/charts.js Chart.js-powered visualizations and data loading
The dashboard exposes a JSON API that you can use to build custom visualizations or export data.
All endpoints accept an optional ?days=N query parameter to filter by the last N days. Use ?days=all or omit the parameter for all-time data.
| Endpoint | Description |
|---|---|
GET /api/summary |
Summary stats: total_orders, pct_on_time, avg_delay |
GET /api/orders?limit=50&offset=0 |
Paginated order list with restaurant names. Each order includes id, restaurant_name, order_total, promised_eta, actual_delivery, delay_minutes, status, items, created_at |
GET /api/restaurants |
Top 10 worst restaurants by average delay. Each entry: id, name, order_count, avg_delay |
GET /api/trends |
Daily aggregates: date, avg_delay, order_count, avg_late_delay, on_time_pct |
GET /api/correlation |
Scatter plot data: points array of {cost, delay}, correlation (Pearson r), count |
GET /api/order/<id>/tracking |
Tracking event timeline for a specific order: events array with timestamp, status, eta_minutes, rider_lat, rider_lng |
Phone internet stops working after setting proxy
- This is expected if mitmproxy is not running yet. The phone is trying to route all traffic through the proxy on your Mac. Start mitmproxy first (
mitmproxyorpython start.py --no-frida), then the phone's internet will work again. - Make sure your phone and Mac are on the same WiFi network.
- Double-check the proxy hostname matches your Mac's local IP (
ipconfig getifaddr en0).
apk-mitm fails
- Make sure Node.js is installed:
node --version. If not, install it withbrew install node. - Try using
npx apk-mitminstead of the globally installed version:npx apk-mitm /tmp/zomato-original.apk -o /tmp/zomato-patched.apk - If it fails with a Java error, make sure you have Java installed:
brew install openjdk - On Apple Silicon Macs, you may need to install Rosetta:
softwareupdate --install-rosetta
Split APK installation fails
- If
adb installgives an error like "INSTALL_FAILED_MISSING_SPLIT", the app uses split APKs. - Run
adb shell pm path com.application.zomatoto see all APK parts. - Pull all parts, patch the base APK, and use
adb install-multipleto install them together. See the split APK instructions under Method A, Step 3. - You may need to uninstall the existing Zomato app first:
adb uninstall com.application.zomato
mitmproxy not capturing traffic
- For the real phone approach: verify the WiFi proxy is set correctly on the phone and that mitmproxy is running on your Mac.
- For the emulator approach: make sure the emulator's HTTP proxy is set:
adb shell settings put global http_proxy 10.0.2.2:8080 - Verify mitmproxy is running: check the terminal for output.
- Install the mitmproxy CA certificate on the device: visit
mitm.itin the device's browser.
Frida can't attach to Zomato / "need Gadget" error (emulator approach only)
- You must use a Google APIs emulator image, NOT a Google Play image. Google Play images are production builds that do not allow root access.
- Run
adb rootto enable root -- if it says "adbd cannot run as root in production builds", you have the wrong image - Make sure
frida-serveris running on the emulator:adb shell ps -A | grep frida - If frida-server is not running, start it:
adb shell "/data/local/tmp/frida-server -D &" - Check the Frida version matches between your host and the server:
frida --version - Verify connectivity:
frida-ps -Ushould list running processes - If Frida crashes, it will auto-retry after 5 seconds
Zomato keeps crashing/restarting (emulator approach only)
- This is expected behavior. Zomato's anti-tampering detects Frida hooks and kills the process. The tracker auto-restarts Frida, and discovery data continues to accumulate across restarts.
SSL errors / no traffic visible
- The mitmproxy CA certificate may not be installed properly on the device
- Re-run the setup: navigate to
mitm.itin the device's browser and install the certificate - For the patched APK approach: make sure you are running the patched version of Zomato, not the original
- For the emulator approach: Android 7+ system CA certificates require a rooted device -- use
adb rooton Google APIs images
No order endpoints found during configure
- Make sure you placed AND tracked a full order during discovery mode (not just browsed the menu)
- Check
discovery_log.jsonhas entries:python start.py status - Try a longer discovery session -- place multiple orders if possible
Dashboard shows no data
- Check the database exists:
python start.py status - Make sure you have run at least one capture session in normal mode (not discovery mode)
- Check the time filter -- switch to "All Time" to see everything
Emulator won't start (emulator approach only)
- Check available disk space (emulators need several GB)
- Try a cold boot: in Android Studio Device Manager, click the dropdown next to your device and select "Cold Boot Now"
- Try deleting and recreating the AVD
App detects emulator (emulator approach only)
- Some Zomato app versions may refuse to run on an emulator
- The Frida script includes root detection bypass, but emulator detection is separate
- Try a different Android version or system image
- Consider switching to the patched APK approach on a real phone, which avoids this issue entirely
Port already in use
- The tracker automatically finds alternative ports if the default is taken
- You can also specify custom ports:
python start.py --port 9090 --dashboard-port 3000
Contributions are welcome. Some ideas:
- Support other food delivery apps -- Swiggy, Uber Eats, DoorDash. The architecture is generic: the discovery/configure pipeline can learn any app's API. You would need a new Frida script tuned to the app's specific SSL pinning implementation (or use the apk-mitm approach for a quicker start).
- Better endpoint detection -- The configure step uses heuristics. Machine learning or more sophisticated pattern matching could improve accuracy.
- Data export -- CSV/JSON export of order data for analysis in other tools.
- Notifications -- Alert when an order is running significantly late.
- Historical comparison -- Compare delivery performance across months or seasons.
To set up for development:
git clone <repo-url>
cd quickwatch
pip install -r requirements.txt
python start.py status # verify your setupThe codebase is intentionally simple: no build tools, no frontend framework, no ORM. Flask serves the dashboard, Chart.js renders charts, SQLite stores data. You can read and understand the entire system in an afternoon.
MIT

