Real-time grocery stock checker across Indian quick-commerce platforms
Built and maintained by @Harsh-Gopal
Cart Radar is a web app that lets you paste any product link from a supported Indian grocery delivery platform and instantly see which stores near you have it in stock — including stores that aren't your default delivery zone.
Instead of just checking your nearest store, Cart Radar performs a hex-grid sweep of the surrounding area, probing multiple delivery zones to find every store that has the product.
| Platform | Stock Check | Area Sweep | Notes |
|---|---|---|---|
| Zepto | ✅ | ✅ | Hex-grid sweep across 5–30 km |
| Swiggy Instamart | ✅ | ✅ | Multi-zone sweep |
| BigBasket | ✅ | ✅ | Cookie-based location spoofing |
| Blinkit | ✅ | ✅ | Playwright-based |
| BB Now | ✅ | ❌ | Express delivery only |
| Tata Neu | 🚧 | 🚧 | Planned |
| Amazon Fresh | 🚧 | 🚧 | Planned |
| Flipkart Minutes | 🚧 | 🚧 | Planned |
- Python 3.11+
- Node.js 20+
- uv (Python package manager)
git clone https://github.com/Harsh-Gopal/CartRadar.git
cd CartRadar/cart-radarcd backend
uv sync
DEV_MODE=true uv run uvicorn app.main:app --port 8001 --reloadcd frontend
npm install
npm run devNavigate to http://localhost:5173
-
Set your location — Type an area name, locality, or pincode in the location search box. Use the GPS button to auto-detect your current location.
-
Paste a product link — Copy any product URL from Zepto, Swiggy, BigBasket, Blinkit, or BB Now and paste it in the product link box. The app auto-detects the platform.
-
Set search radius — Adjust the radius slider (5 km to 30 km) based on how far you're willing to look.
-
Check availability — Click "Check Availability". Results stream in real-time showing which stores have the product and at what price.
-
View on map — Switch to Map view to see all store pins with availability colour-coded (green = in stock, orange = out of stock).
cart-radar/
├── backend/
│ └── app/
│ ├── main.py # FastAPI entry point, routing, auth
│ ├── search.py # SSE orchestration, hex-grid sweep
│ ├── store_cache.py # SQLite cache for discovered stores
│ ├── grid.py # Haversine distance + hex-grid generator
│ ├── links.py # URL/product-ID parser (all platforms)
│ ├── ratelimit.py # Token bucket rate limiter
│ ├── config.py # Environment-based configuration
│ └── platforms/
│ ├── base.py # PlatformClient ABC
│ ├── zepto.py # Zepto client
│ ├── swiggy.py # Swiggy Instamart client
│ ├── bigbasket.py # BigBasket client
│ ├── blinkit.py # Blinkit (Playwright) client
│ └── bbnow.py # BB Now client
└── frontend/
└── src/
├── App.tsx # Main application component
├── hooks/
│ └── use-search.ts # SSE event stream hook
├── components/ # UI components
└── lib/
└── api.ts # Backend API client
- SSE Streaming — Results stream in real-time via Server-Sent Events. Users see stores appear one by one as the sweep progresses, instead of waiting for all results.
- Hex-Grid Sweep — Store discovery uses a hexagonally-packed grid to minimize gaps and overlap while covering a circular area efficiently.
- SQLite Store Cache — Discovered stores and probed coordinates are cached locally (90-day TTL) to speed up repeat searches.
- Unified Platform Interface — All platforms implement the same
PlatformClientABC (resolve_store+product_at_store), making it trivial to add new platforms. - Token Bucket Rate Limiting — Per-IP rate limits with daily caps prevent abuse without requiring authentication.
Set these environment variables to configure the backend:
| Variable | Default | Description |
|---|---|---|
DEV_MODE |
false |
Disables auth token requirement |
APP_TOKEN |
— | Required auth token (when DEV_MODE=false) |
MAX_RADIUS_KM |
50.0 |
Maximum search radius |
SWEEP_SPACING_KM |
2.0 |
Spacing between hex-grid probe points |
MAX_CONCURRENT |
5 |
Max concurrent platform requests |
RATE_LIMIT_RPM |
10 |
Max requests per minute per IP |
RATE_LIMIT_DAILY |
200 |
Max requests per day per IP |
ENABLED_PLATFORMS |
all | Comma-separated list of enabled platforms |
DB_PATH |
stores.sqlite3 |
Path to the SQLite store cache |
- No credentials stored — The app never stores passwords or API keys.
- HMAC token auth — Optional auth token uses
hmac.compare_digest(timing-safe). - Input validation — All API inputs validated via Pydantic models.
- Rate limiting — Token bucket per IP + daily caps prevent abuse.
- XSS safe — All geocode queries are URL-encoded before passing to Nominatim. All user input is treated as plain text, never rendered as HTML.
See docs/BUGS.md for the full bug tracker and docs/AUDIT_REPORT.md for the comprehensive audit report.
- Geocode 500 error —
httpximport was missing inmain.py, causing HTTP 500 on all Nominatim geocoding fallback paths. Fixed. - Zepto WAF bypass — Zepto blocks automated requests (HTTP 202). Fallback to
SAMPLE_STORE_IDfor product preview. Store sweep still works.
- Code-split JS bundle (currently 580KB — Leaflet is the main contributor)
- CORS origins via environment variable (currently hardcoded to localhost)
- Blinkit native API (replace Playwright for better sweep performance)
- Tata Neu / Flipkart Minutes / Amazon Fresh integration
- Automated test suite (pytest for backend, Vitest for frontend)
- Open Graph / SEO meta tags
Contributions are welcome! Please:
- Fork the repo
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Run the build:
cd frontend && npm run build - Open a Pull Request
MIT License — see LICENSE for details.
Made with ❤️ by @Harsh-Gopal | GitHub