Data sources
Mapvest never claims a ticker without a source. This doc lists every provider we use and what they’re for.
Primary
| Provider | Purpose | Key |
|---|---|---|
| OpenRouter | Multimodal LLM (image → brand) + comparable-judge step. Prefer openai/gpt-5.6-terra, fall back to anthropic/claude-opus-4.8 then x-ai/grok-4.6. | OPENROUTER_API_KEY (Doppler mapvest/dev) |
| Gemini (direct) | Fallback multimodal if OpenRouter degrades. | GEMINI_API_KEY |
| Exa | Open-web search for ticker discovery, parent-company lookup, ETF constituent lookup. | EXA_API_KEY |
| Google Places | Nearby POI enumeration for the map view (primary). Multi-type queries (restaurant, cafe, store, bank, …) merged + de-noised (no hospitals/doctors/parks). | GOOGLE_MAPS_API_KEY (Doppler; billed GCP project steady-force-468319-u7) |
Secondary / free-tier
| Provider | Purpose | Notes |
|---|---|---|
| OpenStreetMap Overpass | Nearby POI fallback when Google Places is denied/unavailable. Mirrors are raced in parallel; prefer overpass.openstreetmap.fr. | No key. Cite as OSM/Overpass. |
| Photon (Komoot) | Last-resort nearby brand search if every Overpass mirror fails. | No key. Shortlist of common public brands only. |
| SEC EDGAR | Parent-company resolution, subsidiary lookup, 10-K brand mentions. | No key, please rate-limit. |
Yahoo Finance (via yfinance server-side) | Realtime quote for a resolved ticker, plus daily history for the native Overview price chart (GET /v1/quote-history). | Best-effort, do not display live price without a disclaimer. Never invent closes. |
| ETF.com / Nasdaq holdings | ETF constituent + weight lookup. | Scraped via Exa (respect robots). |
| Wikidata | Brand → parent company mapping for the seed table. | Public SPARQL. |
Seed brand→ticker table
packages/finance/data/brands.json ships with a hand-vetted seed of ~500 common consumer brands mapped to {ticker, exchange, parent}. Missing brands hit the runtime resolver (Exa + LLM) and get written back to the cache table.
Seed structure:
{
"hershey's": { "ticker": "HSY", "exchange": "NYSE", "parent": "The Hershey Company" },
"mcdonald's": { "ticker": "MCD", "exchange": "NYSE", "parent": "McDonald's Corp" },
...
}
Add-only. Never edit an entry to silently change a ticker — file an issue with a source URL.
Private → public comparable
The comparable resolver ranks candidates by:
- Same sector (GICS / SIC).
- Same sub-industry.
- Similar geographic exposure.
- Similar revenue tier if data available.
- ETF exposure > 3% of AUM.
Each candidate carries a score in [0, 1] and a reasoning string. Never return a candidate without at least one Exa source.
Related sibling repos
Callout — sibling repos are link-outs, not dependencies (v0.1). Mapvest does not vendor code from these projects and does not import their modules. The API exposes each sibling behind a stable
/v1/*scaffold that today returns alinkOutURL and anote, and in v0.2 will proxy to a deployed instance of the sibling. Seedocs/SYSTEM_DESIGN.mdD10 for the boundary decision.
| Repo | What it will provide in v0.2 | v0.1 surface | Ownership |
|---|---|---|---|
option_derivation | Options-chain derivation — implied vol surface, greeks, expected-move bands for a given ticker. Consumed by the iOS detail sheet when a public ticker is resolved. | GET /v1/options?ticker=XYZ returns { linkOut, note } and the iOS detail sheet renders an "Options →" badge that opens the linkOut in expo-web-browser. | jawauntb (sibling repo, separate service) |
The Underlying Analyzer Reboot | Auction + depth charts, analysis snapshots, Anthropic briefs / memos, watchlist cockpit/alerts, SEC packs. Live at underlying-terminal-production.up.railway.app. | Thin Mapvest proxies (period 1mo not 1m): GET /v1/chart/:type?ticker=&period= (eager auction 1mo on ticker open; other types lazy), GET /v1/analysis/:ticker (snapshot), POST /v1/memo (full brief), POST /v1/cockpit / POST /v1/alerts (Saved, cap 10), GET /v1/memo/sec/:ticker. Comparables stay Exa+agent — Underlying has no comps API. | jawauntb (sibling repo, separate service) |
| Derivation Research Console | Multi-tool LLM research briefs (idea-chats). Tools (Yahoo, Exa, SEC, options research, etc.) run server-side. Factory / Experiments / Jobs UI stays operator-only — not exposed in Mapvest. Call the Railway origin with Cloudflare front-door host attestation (x-research-console-forwarded-host + RESEARCH_CONSOLE_SERVICE_TOKEN_READ / _MUTATE); bare Railway hosts return 403 REQUEST_HOST_INVALID. When Derivation returns a machine error (MODEL_BUDGET_EXHAUSTED), Mapvest falls back to OpenRouter (x-ai/grok-4.6 → openai/gpt-5.6-luna → anthropic/claude-opus-4.8) for a tools-free brief and never shows the raw code. Fixing Derivation’s model budget is still a sibling-service change. | DERIVATION_URL (Railway). Mapvest: POST /v1/agent/chat, POST /v1/agent/stream (SSE with 3s ping keepalives so Railway Hikari / iOS URLSession do not idle-close during Derivation or OpenRouter waits), GET /v1/agent/threads, GET /v1/agent/threads/:id. Product IA: agent overview on ticker detail, Research tab + Research… sheet, Saved → Briefs. Broker orders permanently off. | jawauntb (sibling service) |
| Nearby geo cache (Postgres) | Places tiles from Google/Overpass/Photon keyed by geohash6 + radius (12h TTL). Brand→ticker joins cached 7d so Exa is not re-hit for the same brand. | nearby_cache, brand_ticker_cache via POSTGRES_URL. First visit pays cascade cost; repeats serve from DB then resolve tickers. | Mapvest API |
Both siblings are expected to run as their own Railway (or equivalent) services. Mapvest is only responsible for:
- Publishing a stable request shape (
ticker,brand,hintSector). - Rendering whatever the sibling returns behind a "not investment advice" disclaimer.
- Never claiming the sibling's output as its own; the source in
sources[]must name the sibling repo/service sodocs/DATA_SOURCES.mdstays honest.
Ticker honesty
Comparables pipeline for private brands / IP:
- Parallel Exa searches (competitors, parent ticker, comps).
- Heuristic extract of exchange-cited symbols (
$MCD,NYSE: MCD). - OpenRouter agent (
openai/gpt-5.6-terra, thenanthropic/claude-opus-4.8, thenx-ai/grok-4.6) judges the evidence and picks ≤3 real listed tickers with reasoning + source URL.
Random ALLCAPS tokens in titles (e.g. NYP, MOUNT, MSHS for nonprofits) are rejected — see packages/finance/src/tickerSymbol.ts.
What we do NOT use
- Paid market-data feeds (Polygon paid tier, IEX Cloud paid tier) — not until unit economics justify.
- Any user-content scraper. If we need training data, it comes from provider APIs, not scraping.
- Fabricated tickers from abbreviations or GuideStar / 401k plan names.
Billing (not market data)
Stripe Checkout / Customer Portal charges Mapvest Pro ($19.99/mo) on the web. iOS charges the same plan through StoreKit 2 (mapvest_pro_monthly); the API verifies Apple's signed transaction (POST /v1/billing/apple) and sets users.plan = subscribed. This is not a market-data source and is never cited on a ticker card. Play Billing remains deferred with v0.2.