{"openapi":"3.0.3","info":{"title":"Factor Weave API","version":"1.0.0","description":"Quantitative factor data, vector similarity search, leak-free forward-return labels and derived market analytics (factor dispersion, regime, risk-cluster tags, 32-D embeddings) for ~12,000 US-listed tickers.\n\n**Auth:** all data endpoints require either a `Bearer <jwt>` (obtain via POST /auth/login) or an `X-API-Key: fw_live_\u2026` header (mint at /me/keys). Tier gating is enforced per endpoint \u2014 see the [Tiers docs](https://factorweave.com/#docs).\n\n**Honest framing:** the data is a research substrate, not a return-prediction service. Our own leak-free testing ([research note](https://factorweave.com/research.html)) shows factor similarity does not forecast returns; the supervised similarity method is a return-weighted projection, not an oracle.","contact":{"name":"Factor Weave support","email":"support@factorweave.com","url":"https://factorweave.com/"},"license":{"name":"Proprietary"}},"servers":[{"url":"https://factorweave.com/api","description":"Production"}],"tags":[{"name":"Public","description":"No-auth metadata, health, demo."},{"name":"Auth","description":"Register, log in, manage your profile and dev keys."},{"name":"Data","description":"Per-ticker factor rows and top-N rankings."},{"name":"Vector Search","description":"Top-K nearest setups by similarity (4 methods, tier-gated)."},{"name":"Labels","description":"Forward-return labels and binary targets \u2014 backtest fuel (PRO+)."},{"name":"Derived Analytics","description":"Whole-universe and per-ticker derivations \u2014 market context, report card, risk cluster, embeddings."},{"name":"Alerts","description":"Indicator-threshold rule storage."},{"name":"Billing","description":"Stripe checkout, portal, webhook."},{"name":"Usage","description":"Quota counters and recent activity."},{"name":"MCP","description":"Model Context Protocol \u2014 stateless streamable-HTTP."},{"name":"Admin","description":"Operator-only."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT obtained from POST /auth/login. Expires in 24h."},"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Long-lived dev key starting `fw_live_\u2026`. Mint via POST /me/keys."}},"schemas":{"Error":{"type":"object","properties":{"detail":{"type":"string","description":"Human-readable error message"}}},"TierGateError":{"type":"object","properties":{"detail":{"type":"string"},"your_tier":{"type":"string","enum":["FREE","HOBBY","PRO","QUANT"]},"required_tier":{"type":"string","enum":["HOBBY","PRO","QUANT"]}}},"FeatureRow":{"type":"object","description":"Per (ticker, date) factor row. ~28 fields.","additionalProperties":true,"properties":{"ticker":{"type":"string"},"date":{"type":"string","format":"date"},"rsi":{"type":"number","nullable":true},"mom":{"type":"number","nullable":true,"description":"Momentum"},"meanrev":{"type":"number","nullable":true},"rv_20":{"type":"number","nullable":true,"description":"20-day realized volatility"},"beta_spy":{"type":"number","nullable":true},"comp_score":{"type":"number","nullable":true},"q_comp_score":{"type":"integer","nullable":true,"description":"Cross-sectional percentile (0\u2013100)"}}},"NeighborRow":{"type":"object","properties":{"ticker":{"type":"string"},"date":{"type":"string","format":"date"},"rank":{"type":"integer"},"score":{"type":"number"},"features":{"$ref":"#/components/schemas/FeatureRow"},"labels":{"type":"object","additionalProperties":true}}},"LoginResponse":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"bearer"},"user_id":{"type":"integer"},"email":{"type":"string"},"subscription_tier":{"type":"string","enum":["FREE","HOBBY","PRO","QUANT"]},"expires_at":{"type":"string","format":"date-time"}}}},"parameters":{"Ticker":{"name":"ticker","in":"path","required":true,"schema":{"type":"string"},"example":"AAPL","description":"Uppercase US ticker symbol."}},"responses":{"BadRequest":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing or invalid authorization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TierForbidden":{"description":"Tier too low for this endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TierGateError"}}}},"NotFound":{"description":"Ticker / resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"QuotaExceeded":{"description":"Daily quota exhausted for this tier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/health":{"get":{"tags":["Public"],"summary":"Liveness check","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/status":{"get":{"tags":["Public"],"summary":"Detailed health + bundle freshness + degraded status","description":"Returns 503 when degraded (bundle stale, DB unreachable, etc.) \u2014 designed for uptime monitors.","responses":{"200":{"description":"Healthy"},"503":{"description":"Degraded \u2014 see body.checks for what failed"}}}},"/manifest":{"get":{"tags":["Public"],"summary":"Static bundle metadata","description":"latest_date, ticker_count, generated_at, file map. Use this to know when the data refreshed.","responses":{"200":{"description":"Manifest"}}}},"/config":{"get":{"tags":["Public"],"summary":"Runtime feature flags","description":"paid_tiers_enabled and Stripe publishable key. Read by the frontend at boot.","responses":{"200":{"description":"Config"}}}},"/demo/{ticker}":{"get":{"tags":["Public"],"summary":"No-auth taste \u2014 one of the sample tickers","parameters":[{"$ref":"#/components/parameters/Ticker"}],"responses":{"200":{"description":"Curated factor subset + 8 cosine analogues"},"403":{"description":"Ticker is not in the demo sample (free account unlocks all 10,000+)"}}}},"/demo/tickers":{"get":{"tags":["Public"],"summary":"The demo-sample ticker list (8 names)","responses":{"200":{"description":"Ticker list"}}}},"/auth/register":{"post":{"tags":["Auth"],"summary":"Create account (free tier)","description":"Always creates a FREE account regardless of submitted subscription_tier \u2014 paid upgrades happen via Stripe Checkout (POST /billing/create-checkout).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8}}}}}},"responses":{"200":{"description":"Account created"},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/auth/login":{"post":{"tags":["Auth"],"summary":"Exchange email + password for a JWT","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}}}}}},"responses":{"200":{"description":"Token + profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"description":"Rate-limited (too many failed attempts)"}}}},"/auth/me":{"get":{"tags":["Auth"],"summary":"Current user profile + tier + daily usage","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"responses":{"200":{"description":"Profile"}}}},"/me/keys":{"get":{"tags":["Auth"],"summary":"List your long-lived dev API keys","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"responses":{"200":{"description":"Key list (prefixes only \u2014 values shown once at creation)"}}},"post":{"tags":["Auth"],"summary":"Mint a new dev API key","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","example":"local-dev"},"ttl_days":{"type":"integer","minimum":1,"maximum":3650,"example":365}}}}}},"responses":{"201":{"description":"Key created (full value returned ONCE)"}}}},"/me/keys/{id}":{"delete":{"tags":["Auth"],"summary":"Revoke a dev API key","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"204":{"description":"Revoked"}}}},"/features/{ticker}":{"get":{"tags":["Data"],"summary":"Factor row for a ticker","description":"No date params \u2192 latest single row. ?date= \u2192 that exact date. ?start_date= & ?end_date= \u2192 range. History covers the last 252 trading days.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/Ticker"},{"name":"date","in":"query","schema":{"type":"string","format":"date"}},{"name":"start_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"end_date","in":"query","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Feature row(s)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FeatureRow"}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/top":{"get":{"tags":["Data"],"summary":"Top-N tickers by a factor","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"name":"factor","in":"query","required":true,"schema":{"type":"string"},"description":"mom, meanrev, comp_score, rsi, z_52w, beta_spy, gap_pct, \u2026"},{"name":"n","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"direction","in":"query","schema":{"type":"string","enum":["asc","desc"],"default":"desc"}}],"responses":{"200":{"description":"Ranked rows"}}}},"/csv/features":{"get":{"tags":["Data"],"summary":"CSV export of a ticker's features","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"name":"ticker","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"CSV","content":{"text/csv":{"schema":{"type":"string"}}}}}}},"/vector-search/similar/{ticker}":{"get":{"tags":["Vector Search"],"summary":"Top-K nearest setups by similarity","description":"Tier-gated: cosine (FREE+), dtw (HOBBY+), label_aware (PRO+), supervised (QUANT). Each neighbor row carries its factor row + forward-return labels pre-joined.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/Ticker"},{"name":"method","in":"query","schema":{"type":"string","enum":["cosine","dtw","label_aware","supervised"],"default":"cosine"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"min_lookback_days","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Require neighbor_date \u2265 min_lookback_days before query_date. Use 30 to filter same-day co-moving ETFs."}],"responses":{"200":{"description":"Neighbors","content":{"application/json":{"schema":{"type":"object","properties":{"query_ticker":{"type":"string"},"query_date":{"type":"string","format":"date"},"neighbors":{"type":"array","items":{"$ref":"#/components/schemas/NeighborRow"}}}}}}},"403":{"$ref":"#/components/responses/TierForbidden"}}}},"/labels/{ticker}":{"get":{"tags":["Labels"],"summary":"Leak-free forward-return labels (PRO+)","description":"fwd_ret_1d/5d/20d + binary targets (target_1d_up1, target_5d_up3, target_20d_up10). Last 252 trading days.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/Ticker"}],"responses":{"200":{"description":"Labels"},"403":{"$ref":"#/components/responses/TierForbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/market-context":{"get":{"tags":["Derived Analytics"],"summary":"Whole-universe factor dispersion, breadth, regime","description":"FREE = today's reading only; HOBBY+ = full 252-day history. ?latest=1 trims to today for any tier.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"name":"latest","in":"query","schema":{"type":"integer","enum":[0,1]}}],"responses":{"200":{"description":"Market context payload"}}}},"/report-card/{ticker}":{"get":{"tags":["Derived Analytics"],"summary":"Per-ticker digest \u2014 ranks, regime, risk cluster, unusualness (HOBBY+)","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/Ticker"}],"responses":{"200":{"description":"Report card"},"403":{"$ref":"#/components/responses/TierForbidden"}}}},"/risk-cluster/{ticker}":{"get":{"tags":["Derived Analytics"],"summary":"Volatility regime a ticker's factor analogues landed in (PRO+)","description":"calm / normal / stressed \u2014 derived from neighbours' realized forward vol. A risk-coherence signal, not a vol forecast.","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/Ticker"}],"responses":{"200":{"description":"Risk-cluster tag"},"403":{"$ref":"#/components/responses/TierForbidden"}}}},"/embedding/{ticker}":{"get":{"tags":["Derived Analytics"],"summary":"Raw 32-D regime-aware embedding vector (QUANT)","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"$ref":"#/components/parameters/Ticker"}],"responses":{"200":{"description":"32-D vector","content":{"application/json":{"schema":{"type":"object","properties":{"ticker":{"type":"string"},"as_of":{"type":"string","format":"date"},"dim":{"type":"integer","example":32},"embedding":{"type":"array","items":{"type":"number"}}}}}}},"403":{"$ref":"#/components/responses/TierForbidden"}}}},"/alerts/rules":{"get":{"tags":["Alerts"],"summary":"List your alert rules","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"responses":{"200":{"description":"Rules"}}},"post":{"tags":["Alerts"],"summary":"Create an alert rule","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","indicator","condition","value"],"properties":{"name":{"type":"string"},"indicator":{"type":"string","enum":["rsi","price","volume","momentum"]},"condition":{"type":"string","enum":[">","<","=","crosses_above","crosses_below"]},"value":{"type":"number"},"description":{"type":"string"}}}}}},"responses":{"201":{"description":"Created"}}}},"/alerts/rules/{id}":{"delete":{"tags":["Alerts"],"summary":"Delete an alert rule","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"204":{"description":"Deleted"}}}},"/billing/create-checkout":{"post":{"tags":["Billing"],"summary":"Stripe Checkout URL for a tier upgrade","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tier"],"properties":{"tier":{"type":"string","enum":["HOBBY","PRO","QUANT"]},"billing_period":{"type":"string","enum":["monthly","annual"],"default":"monthly"}}}}}},"responses":{"200":{"description":"checkout_url"}}}},"/billing/create-portal":{"post":{"tags":["Billing"],"summary":"Stripe Billing Portal URL","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"responses":{"200":{"description":"portal_url"}}}},"/billing/webhook":{"post":{"tags":["Billing"],"summary":"Stripe webhook receiver","description":"Signature-verified inside the handler. Not for client use.","responses":{"200":{"description":"Received"},"400":{"description":"Invalid signature"}}}},"/usage":{"get":{"tags":["Usage"],"summary":"Today + month-to-date stats","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"responses":{"200":{"description":"Usage payload"}}}},"/recent-activity":{"get":{"tags":["Usage"],"summary":"Your last 50 API calls","security":[{"bearerAuth":[]},{"apiKeyAuth":[]}],"responses":{"200":{"description":"Activity log"}}}},"/mcp":{"post":{"tags":["MCP"],"summary":"Model Context Protocol \u2014 stateless streamable-HTTP","description":"JSON-RPC 2.0 over POST. Supports `initialize`, `tools/list`, `tools/call`. 12 tools available \u2014 see https://factorweave.com/mcp.html and the MCP docs panel.","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"JSON-RPC response"}}}}}}