{"openapi":"3.1.0","info":{"title":"CongressInvests API","description":"\n**Real-time congressional stock trade disclosures** from the U.S. Senate and House of Representatives, refreshed every 6 hours.\n\n## Quick start\n\n```bash\ncurl https://congressinvests.com/trades/NVDA\n```\n\n## Authentication\n\n| Tier | Header | Daily limit |\n|------|--------|-------------|\n| Free | *(none required)* | 100 req / day per IP |\n| Pro  | `X-Api-Key: YOUR_KEY` | 50,000 req / day |\n\nExceeding the free limit returns **HTTP 429 Too Many Requests**.\n\n## Data freshness\n\nEvery response includes three freshness fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `last_updated` | string (ISO 8601 UTC) | Timestamp of the last successful cache load |\n| `data_lag_minutes` | integer | Minutes elapsed since the last refresh |\n| `next_refresh_in_minutes` | integer | Minutes until the next scheduled refresh |\n\n## SDKs & examples\n\nOfficial Python and Node.js SDKs are available in the\n[congressinvests-api](https://github.com/Dianadone/congressinvests-api) repository.\n","contact":{"name":"CongressInvests Support","url":"https://congressinvests.com/#contact"},"version":"1.0.0"},"servers":[{"url":"https://congressinvests.com","description":"Production"}],"paths":{"/status":{"get":{"tags":["Meta"],"summary":"Health","description":"Liveness check plus cache status and data freshness metadata.\n\nReturns `status: \"ok\"` when the API is up. The `ready` field indicates\nwhether the trade cache has finished its initial load. Use `data_lag_minutes`\nand `next_refresh_in_minutes` to understand how fresh the data is.","operationId":"health_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"tags":["Meta"],"summary":"Health","description":"Liveness check plus cache status and data freshness metadata.\n\nReturns `status: \"ok\"` when the API is up. The `ready` field indicates\nwhether the trade cache has finished its initial load. Use `data_lag_minutes`\nand `next_refresh_in_minutes` to understand how fresh the data is.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/trades/recent":{"get":{"tags":["Trades"],"summary":"Get Recent Trades","description":"The most recently filed trades across all tickers, sorted by filing date descending.","operationId":"get_recent_trades_trades_recent_get","parameters":[{"name":"chamber","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by chamber: Senate or House","title":"Chamber"},"description":"Filter by chamber: Senate or House"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":365,"minimum":1},{"type":"null"}],"description":"Only include trades filed within the last N days","title":"Days"},"description":"Only include trades filed within the last N days"},{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/stories":{"get":{"tags":["Trades"],"summary":"Get Stories","description":"AI-generated one-sentence summaries of notable trade disclosures ($50k+ range floor).\n\nStrictly factual — states what the filing shows (who, what, amount, dates), never\nspeculates about motive. New stories are generated automatically as new trades are\ndiscovered during the regular refresh cycle. Browsers get an HTML feed page; pass\n`?format=json` to force JSON.","operationId":"get_stories_stories_get","parameters":[{"name":"ticker","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by ticker","title":"Ticker"},"description":"Filter by ticker"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format"}},{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/stories/{story_id}":{"get":{"tags":["Trades"],"summary":"Get Story","description":"A single trade story by id — the shareable permalink unit.\n\nBrowsers get an HTML page (with a per-story Open Graph share image);\npass `?format=json` to force JSON.","operationId":"get_story_stories__story_id__get","parameters":[{"name":"story_id","in":"path","required":true,"schema":{"type":"string","title":"Story Id"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format"}},{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/top-stocks":{"get":{"tags":["Trades"],"summary":"Top Stocks","description":"The stocks most bought and most sold by members of Congress, ranked by how\nmany members traded them, over trades disclosed in the last 30, 90, or 365\ndays. Browsers get an HTML page; pass `?format=json` to force JSON.","operationId":"top_stocks_top_stocks_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","description":"Window: 30, 90, or 365 days of disclosures","default":90,"title":"Days"},"description":"Window: 30, 90, or 365 days of disclosures"},{"name":"chamber","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by chamber: Senate or House","title":"Chamber"},"description":"Filter by chamber: Senate or House"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format"}},{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/trades/{ticker}":{"get":{"tags":["Trades"],"summary":"Get Trades","description":"All trades for a specific stock ticker, sorted by filing date descending.\n\n- Case-insensitive: `nvda` and `NVDA` both work.\n- Filter by `chamber=Senate` or `chamber=House`.\n- Paginate with `offset` and `limit` (max 500).\n- Covers the last 365 days of disclosures from both chambers.\n- Free tier: 100 requests/day per IP. Pro: 50,000/day with `X-Api-Key`.\n- Browsers (and crawlers) get a readable HTML page instead of JSON; pass\n  `?format=json` to force JSON, or set an `Accept: application/json` header.","operationId":"get_trades_trades__ticker__get","parameters":[{"name":"ticker","in":"path","required":true,"schema":{"type":"string","title":"Ticker"}},{"name":"chamber","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by chamber: Senate or House","title":"Chamber"},"description":"Filter by chamber: Senate or House"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Force the response format: 'json' or 'html'. By default browsers get an HTML page and API clients get JSON.","title":"Format"},"description":"Force the response format: 'json' or 'html'. By default browsers get an HTML page and API clients get JSON."},{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/trades":{"get":{"tags":["Trades"],"summary":"Get All Trades","description":"All cached trades across every ticker, sorted by transaction date descending.\n\nUse this for a global feed or bulk analysis. Default limit is 200; max is 1000.\nFilter by `chamber=Senate` or `chamber=House`. Paginate with `offset`.","operationId":"get_all_trades_trades_get","parameters":[{"name":"chamber","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by chamber: Senate or House","title":"Chamber"},"description":"Filter by chamber: Senate or House"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":200,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cache/status":{"get":{"tags":["Meta"],"summary":"Cache Status","description":"Cache metadata: readiness, age, ticker counts per chamber.","operationId":"cache_status_cache_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/cache/refresh":{"post":{"tags":["Admin"],"summary":"Cache Refresh","description":"Force a full cache reload. Requires a valid X-Api-Key header.","operationId":"cache_refresh_cache_refresh_post","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cache/refresh/incremental":{"post":{"tags":["Admin"],"summary":"Cache Refresh Incremental","description":"Force an incremental refresh (new trades since last scrape only).\n\nUnlike /cache/refresh (a full 365-day rescrape), this is the path that\nfires webhooks and generates trade stories for newly discovered trades —\nuseful to trigger those on demand instead of waiting out CACHE_STALE_AGE.\nRequires a valid X-Api-Key header.","operationId":"cache_refresh_incremental_cache_refresh_incremental_post","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/usage":{"get":{"tags":["Admin"],"summary":"Admin Usage","description":"Traffic summary for the last N days: total hits, per-day counts, top paths,\nbreakdown by API-key label, and status-code spread.\n\nOpened in a browser this serves an HTML dashboard (enter the key there);\notherwise it returns JSON and requires a valid `X-Api-Key` header.\n\nKey labels: `anon` (no key), `static` (env key), `invalid` (unrecognised key),\n`sub:<email>` (paying subscriber).","operationId":"admin_usage_admin_usage_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"description":"Look-back window in days","default":7,"title":"Days"},"description":"Look-back window in days"},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'json' or 'html'. Browsers get the dashboard; API clients get JSON.","title":"Format"},"description":"'json' or 'html'. Browsers get the dashboard; API clients get JSON."},{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/subscribers":{"get":{"tags":["Admin"],"summary":"Admin Subscribers","description":"One row per subscriber: plan status, whether an API key was issued, when\nthey signed up, and — from the usage log — how many API calls that key has\nmade and when it was last seen. Answers \"did they get access, are they\nusing it\". Admin only (env key header or dashboard cookie).\n\n`?reveal=1` adds the full `api_key` to each row (for hand-delivering a key\nto a subscriber who never received theirs).","operationId":"admin_subscribers_admin_subscribers_get","parameters":[{"name":"reveal","in":"query","required":false,"schema":{"type":"boolean","description":"Include the full api_key per row","default":false,"title":"Reveal"},"description":"Include the full api_key per row"},{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/contacts":{"get":{"tags":["Admin"],"summary":"Admin Contacts","description":"All contact-form submissions, newest first. Admin only (env key header\nor dashboard cookie).","operationId":"admin_contacts_admin_contacts_get","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/stripe/checkout":{"post":{"tags":["Meta"],"summary":"Stripe Checkout","description":"Create a Stripe Checkout session for the Pro plan. Returns a redirect URL.","operationId":"stripe_checkout_stripe_checkout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/stripe/portal":{"post":{"tags":["Meta"],"summary":"Stripe Portal","description":"Create a Stripe Billing Portal session for an existing Pro subscriber.\nPass the subscriber's API key in the X-Api-Key header.\nReturns a redirect URL to manage or cancel the subscription.","operationId":"stripe_portal_stripe_portal_post","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/stripe/webhook":{"post":{"tags":["Meta"],"summary":"Stripe Webhook","description":"Stripe webhook handler — processes subscription lifecycle events.","operationId":"stripe_webhook_stripe_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/contact":{"post":{"tags":["Meta"],"summary":"Contact","description":"Save a contact form submission to the SQLite database.","operationId":"contact_contact_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/subscribe":{"post":{"tags":["Webhooks"],"summary":"Webhook Subscribe","description":"Subscribe to trade alerts. Requires a valid Pro API key.\nAt least one of `ticker` or `politician` must be provided.\n`events` accepts: \"buy\", \"sell\", \"any\" (default: [\"any\"]).","operationId":"webhook_subscribe_webhooks_subscribe_post","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscribeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/unsubscribe/{sub_id}":{"delete":{"tags":["Webhooks"],"summary":"Webhook Unsubscribe","description":"Delete a webhook subscription by its ID. Must use the same API key used to create it.","operationId":"webhook_unsubscribe_webhooks_unsubscribe__sub_id__delete","parameters":[{"name":"sub_id","in":"path","required":true,"schema":{"type":"string","title":"Sub Id"}},{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/my-subscriptions":{"get":{"tags":["Webhooks"],"summary":"Webhook List","description":"List all webhook subscriptions for the authenticated API key.","operationId":"webhook_list_webhooks_my_subscriptions_get","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"type":"string","default":"","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/stories/subscribe":{"post":{"tags":["Trades"],"summary":"Stories Subscribe","description":"Subscribe an email address to trade-story notifications. Public, no\nAPI key required. Sends a digest email whenever new stories are\ngenerated (see stories._notify_new_stories); every email includes a\none-click unsubscribe link.","operationId":"stories_subscribe_stories_subscribe_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorySubscribeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/alerts/email/subscribe":{"post":{"tags":["Alerts"],"summary":"Alert Subscribe","description":"Free email alert for one stock *or* one member of Congress. Sends a\nconfirmation email first; alerts only start once the link is clicked.","operationId":"alert_subscribe_alerts_email_subscribe_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailAlertRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"ContactRequest":{"properties":{"name":{"type":"string","title":"Name"},"email":{"type":"string","format":"email","title":"Email"},"plan":{"type":"string","title":"Plan","default":""},"message":{"type":"string","title":"Message"}},"type":"object","required":["name","email","message"],"title":"ContactRequest"},"EmailAlertRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker"},"member":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Member"}},"type":"object","required":["email"],"title":"EmailAlertRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"StorySubscribeRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["email"],"title":"StorySubscribeRequest"},"SubscribeRequest":{"properties":{"webhook_url":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Webhook Url"},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker"},"politician":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Politician"},"events":{"items":{"type":"string"},"type":"array","title":"Events","default":["any"]}},"type":"object","required":["webhook_url"],"title":"SubscribeRequest"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"Trades","description":"Query congressional stock trade disclosures by ticker, date range, or chamber. Free tier: 100 req/day per IP, no key required."},{"name":"Webhooks","description":"**Pro tier only.** Subscribe to real-time trade alerts by ticker or politician. Requires a valid `X-Api-Key` header."},{"name":"Meta","description":"Health checks, cache status, and contact form."},{"name":"Admin","description":"Cache management and usage analytics. Require a valid `X-Api-Key` header."}]}