A free India investing research web app for stocks and mutual funds — discover, filter, compare, and track without a paywalled screener.
Built on open, official data sources:
| Source | What it provides | How it's used |
|---|---|---|
| AMFI NAVAll.txt | Daily NAVs, scheme names, AMCs, SEBI categories for every MF scheme | Parsed on each refresh into the fund universe |
NSE index API (equity-stockIndices) |
Index constituents with price, change, volume, 52-week range | Fetched per configured index (NIFTY 50 / NEXT 50 / MIDCAP 50 by default) |
| mfapi.in (optional) | Free per-scheme NAV history | Computes 1Y/3Y/5Y returns during refresh |
| SEBI category rules | Riskometer-style classification | Fallback risk inference by scheme category |
No paid screener or subscription endpoint is used anywhere. Every source URL is an environment variable, so a moved endpoint is a config change, not a code change.
- Stock discovery — filter by sector, market-cap bucket, and index; sort by gainers, losers, volume, price, or P/E; paginated.
- Mutual fund discovery — filter by category, AMC, risk label, AUM floor, and minimum 1Y return; sort by returns or AUM.
- Unified search — one box searches symbols, company names, sectors, scheme names, AMCs, and sub-categories, ranked by match quality.
- Compare — 2 to 5 assets side by side (stocks and funds can be mixed; each group gets its own metric table). The best numeric value per row is highlighted. Comparisons are shareable URLs.
- Watchlist — save any asset with one tap (stored in
localStorage, shaped for a future account-backed API). Alerts are a visible phase-2 placeholder. - Plain-language metrics — hover/long-press any metric label for a simple explanation.
Browser ── pages (Next.js App Router, Tailwind)
│
▼
API routes (/api/stocks /funds /search /compare /meta /audits /refresh)
│
▼
store.ts — 3-tier data access
1. in-memory cache (5 min TTL, per instance)
2. Postgres (Supabase/Neon) when DATABASE_URL is set
3. bundled seed JSON (data/seed/) — zero-config fallback
▲
refresh.ts — scheduled ingestion (Vercel Cron → /api/refresh)
├─ sources/amfi.ts → transforms/amfi.ts (NAV dump → funds)
├─ sources/nse.ts → transforms/nse.ts (index payloads → stocks)
└─ sources/amfi.ts → computeReturns (NAV history → 1Y/3Y/5Y)
Design decisions:
- Transforms are pure functions (
src/lib/transforms/) with no I/O — they carry the test coverage and make the pipeline transparent and replayable. - Merging never destroys data. AMFI's dump has no AUM/returns; NSE payloads can be partial. Refreshes merge field-by-field, so a missing value never erases a known one.
- Every refresh is audited (
refresh_audittable,/api/audits) with source, timing, row counts, and error messages. Raw payloads are cached inraw_snapshots. - The API layer is the only data path the UI uses, so a future mobile app gets the same contract for free.
npm install
npm run dev # http://localhost:3000 — works immediately on seed data
npm test # vitest: data-transform and query testsNo environment variables are required for local development: without DATABASE_URL the
app serves the bundled example data in data/seed/ (illustrative sample values, not live
quotes — regenerate with node scripts/generate-seed.mjs).
Copy .env.example to .env.local and fill in what you need:
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
no | Postgres (Supabase/Neon). Enables durable storage + audit log. |
CRON_SECRET |
in prod | Bearer token protecting /api/refresh. Vercel Cron sends it automatically. |
AMFI_NAV_URL |
no | AMFI NAV dump (defaults to the official URL). |
NSE_INDEX_URL, NSE_HOME_URL |
no | NSE API endpoint template + cookie-handshake origin. |
NSE_INDICES |
no | Comma-separated indices to ingest. |
MF_HISTORY_URL |
no | NAV-history API template for returns enrichment ({CODE} placeholder). |
- Create a Postgres database (Supabase or Neon free tier is fine).
- Set
DATABASE_URL(use the pooled connection string on serverless). - Apply the schema:
DATABASE_URL=... npm run db:schema - Trigger a first refresh:
curl -H "Authorization: Bearer $CRON_SECRET" https://<app>/api/refresh?source=all
GET/POST /api/refresh?source=all|amfi|nse|returns (Bearer-token protected):
amfi— pulls the full NAV dump (~15k schemes) and merges NAVs/categories.nse— pulls each configured index; partial failures are recorded, not fatal.returns— enriches up to 50 growth-plan schemes per run with 1Y/3Y/5Y returns from the NAV-history API (rate-limit friendly; coverage accumulates across runs).- Every run writes an audit row; inspect via
GET /api/audits.
vercel.json schedules a weekday refresh at 12:30 UTC (6 pm IST, after AMFI publishes
daily NAVs).
Stock prices update near-live (default: at most 3 minutes old) while NSE is open (09:15–15:30 IST, Mon–Fri), without needing high-frequency crons:
- Server: any stock request during market hours checks data age; if stale, a
background refresh (Next.js
after()) pulls fresh NSE quotes once — with per-instance locking and a 90-second attempt backoff so traffic spikes can't stampede NSE. - Client: stock lists and detail pages re-poll every 60 seconds while the market is open and the tab is visible, and show a pulsing Live badge with the quote time (IST). When the market is closed, polling stops and the badge shows "Market closed".
- A 15-minute grace window after the close captures closing prices.
- Tune with
LIVE_REFRESH_SECONDS; list NSE holidays inNSE_HOLIDAYSto skip polling.
This is polling of NSE's public API, not exchange tick data — quotes can lag by a couple of minutes and NSE may throttle data-center IPs. When a refresh fails, the app keeps serving the last good data, the failure lands in the audit log, and the badge's timestamp makes the data age visible.
Known constraint: NSE's API sits behind a cookie handshake and rate-limits aggressively
from data-center IPs. The fetcher does the handshake and degrades gracefully (partial
data + audit entry); if it is blocked entirely, the app keeps serving the last good data
(DB or seed). For production-grade stock coverage, point NSE_INDEX_URL at your own
mirror/proxy of the official data.
- Push this repo to GitHub and import it in Vercel (framework auto-detected: Next.js).
- Add environment variables:
DATABASE_URL,CRON_SECRET(and any source overrides). - Deploy. The cron in
vercel.jsonis picked up automatically (Hobby plan allows daily crons; the default schedule works on all plans). - Apply the DB schema once from your machine:
DATABASE_URL=... npm run db:schema. - Hit
/api/refresh?source=allonce with the bearer token to populate real data.
| Endpoint | Query params | Notes |
|---|---|---|
GET /api/stocks |
q, sector, cap, index, sort, page, pageSize |
sort prefix - = descending |
GET /api/funds |
q, category, subCategory, amc, risk, minAum, maxAum, minReturn1y, sort, page, pageSize |
|
GET /api/search |
q, limit |
Unified, ranked across both types |
GET /api/compare |
ids=stock:RELIANCE,fund:120465 |
2–5 ids; unknown ids reported in missing |
GET /api/meta |
— | Distinct sectors/categories/AMCs for filter UIs |
GET /api/audits |
— | Recent refresh audit log |
GET /api/refresh |
source |
Bearer-token protected |
- Alerts — the watchlist already reserves a per-item
alertslot; a cron route can evaluate rules and notify. - Account-backed watchlists — the
localStorageshape (kind+id) imports directly into awatchliststable keyed by user. - Portfolio tracking — buy/sell transactions on top of the same normalized tables.
- More sources — additional indices via
NSE_INDICES; AMFI AUM downloads; SEBI statistics for validation.
Data may be delayed, incomplete, or (in seed mode) illustrative sample data. This is a research tool, not investment advice. Verify with official sources — AMFI, NSE, SEBI, and AMC pages — before making investment decisions.


