Congress stock trades API
Four read-only JSON endpoints over U.S. political and institutional disclosure: the list of members who have filed, one member's STOCK Act transactions, the tracked-portfolio list, and one portfolio in full. No key is needed to read, at 60 requests per hour per IP. Refreshed hourly, with a link to the original filing on every row.
Tracefour reproduces what was disclosed and when. It does not rank politicians, score their trades, or suggest anything about them. Not investment advice.
Two namespaces, one person
A person can exist twice in this API, at two different addresses, under two different slugs. The congress namespace holds individual disclosed transactions and uses the first-name-first slug. The tracker namespace holds an assembled portfolio view and uses a short curated slug. Nancy Pelosi is nancy-pelosi in one and pelosi in the other, and neither address answers for the other.
| Person | Portfolio view | Disclosed transactions |
|---|---|---|
| Nancy Pelosi | GET /v1/trackers/pelosi | GET /v1/congress/nancy-pelosi |
| Ro Khanna | GET /v1/trackers/khanna | GET /v1/congress/ro-khanna |
| Donald J. Trump | GET /v1/trackers/trump-portfolio | No member record. Not a member of Congress. |
| Warren Buffett | GET /v1/trackers/berkshire | No member record. 13F filer. |
# 404. "pelosi" is a tracker slug, not a member slug.
curl -s -o /dev/null -w '%{http_code}\n' "https://tracefour.com/v1/congress/pelosi"
# 404
# 200. The member namespace uses the first-name-first form.
curl -s -o /dev/null -w '%{http_code}\n' "https://tracefour.com/v1/congress/nancy-pelosi"
# 200
# 200. The tracker namespace uses the short form.
curl -s -o /dev/null -w '%{http_code}\n' "https://tracefour.com/v1/trackers/pelosi"
# 200GET /v1/catalog returns both slug lists in one call, which is the cheapest way to resolve a name without guessing.
Two things that surprise callers
Not every named politician is in here. A member appears once they have filed a covered transaction, and many never do. Probing twelve well-known names against a live dataset produced six 404s. That is the data being honest, not the API failing. Call GET /v1/congress and work from the list it returns.
The lag is the law, not the pipeline. The STOCK Act gives a member 30 days from becoming aware of a covered transaction, and no more than 45 days from the trade, to file it. Reports are ingested hourly once they are public, so a trade typically surfaces 30 to 45 days after it happened. Every row carries both transactionDate and disclosureDate, so the gap is always visible rather than smoothed away.
Endpoints
GET /v1/congress
Every member of Congress who has a disclosure on file, one row per person, with the slug the member endpoint expects.
Start here rather than guessing a slug. Members appear only once they have disclosed a covered transaction, so a sitting member with no reportable trades has no row and no page. Of twelve well-known names probed against a live dataset, six had no record at all.
One row per PERSON, not per spelling. The upstream feed has filed the same member under several name spellings, 34 of 125 people under 68 slugs when it was last measured, so the rows are folded on the member identifier before they are returned. Without that fold the same person would appear twice with two different trade histories.
No parameters.
curl
curl "https://tracefour.com/v1/congress"Python
import requests
response = requests.get("https://tracefour.com/v1/congress", timeout=30)
response.raise_for_status()
members = response.json()["data"]
by_slug = {member["slug"]: member for member in members}
print(len(members), "members with disclosures")
print(by_slug.get("nancy-pelosi"))JavaScript
const { data } = await fetch("https://tracefour.com/v1/congress").then((r) => r.json())
const senators = data.filter((member) => member.chamber === "senate")
console.log(`${data.length} members, ${senators.length} senators`)Response
Captured from the curl above, trimmed from 49 rows to 2. Rows are not alphabetical and the order is not part of the contract; key by slug.
{
"data": [
{
"slug": "nancy-pelosi",
"name": "Nancy Pelosi",
"chamber": "house",
"district": "CA11",
"url": "https://tracefour.com/congress/nancy-pelosi"
},
{
"slug": "mitch-mcconnell",
"name": "Mitch McConnell",
"chamber": "senate",
"district": "KY",
"url": "https://tracefour.com/congress/mitch-mcconnell"
}
],
"meta": {
"count": 49,
"fetchedAt": "2026-09-09T18:33:18.628Z",
"source": "sqlite",
"attribution": {
"text": "Underlying filings are public-domain government works. Tracefour compilation licensed CC BY 4.0: when you display it, link to the Tracefour page in meta.attribution.page, or the url on each record. Not investment advice.",
"site": "https://tracefour.com",
"page": "https://tracefour.com/congress"
},
"license": "https://creativecommons.org/licenses/by/4.0/",
"sourceLicense": "https://www.usa.gov/government-works"
}
}GET /v1/congress/{member}
One member: their identity row plus their disclosed transactions, newest first, up to 500 trades.
The slug is the first-name-first form from /v1/congress, for example nancy-pelosi. It is not the tracker slug. GET /v1/congress/pelosi returns 404.
Every amount is a bracket, because that is how the STOCK Act requires it to be disclosed. amountLabel is the bracket exactly as filed, amountMin and amountMax are its bounds, and amountMid is the arithmetic midpoint, present for sorting and sizing only. The disclosure is the bracket. Treat amountMid as a display convenience, not as a reported figure.
owner records who holds the asset, commonly Spouse or Joint, because a member must report a spouse’s covered transactions as well as their own. sourceLink is the original PDF.
An unknown slug returns 404 and a malformed slug returns 400.
| Parameter | Type | Accepts | Default |
|---|---|---|---|
member | string (path) | Kebab-case ASCII, 1 to 80 characters, from /v1/congress. | required |
curl
curl "https://tracefour.com/v1/congress/nancy-pelosi"Python
import requests
response = requests.get("https://tracefour.com/v1/congress/nancy-pelosi", timeout=30)
if response.status_code == 404:
raise SystemExit("No disclosures on file. List members with GET /v1/congress.")
response.raise_for_status()
payload = response.json()["data"]
for trade in payload["trades"][:5]:
print(trade["transactionDate"], trade["ticker"], trade["type"], trade["amountLabel"])JavaScript
const response = await fetch("https://tracefour.com/v1/congress/nancy-pelosi")
if (response.status === 404) throw new Error("No disclosures for that slug. See GET /v1/congress.")
const { data } = await response.json()
const purchases = data.trades.filter((trade) => trade.type === "Purchase")
console.log(data.member.memberName, purchases.length, "purchases")Response
Captured from the curl above, trimmed from 23 trades to 1. meta.count counts the member record, not the trades; use data.trades.length for those. meta trimmed to its first three keys.
{
"data": {
"member": {
"slug": "nancy-pelosi",
"memberName": "Nancy Pelosi",
"chamber": "house",
"district": "CA11",
"party": "D",
"firstSeen": "2026-06-24",
"lastSeen": "2026-06-24",
"lifetimeFilings": 2
},
"trades": [
{
"filingId": "nancy-pelosi|UBER|2026-05-29|Purchase|$500,001 - $1,000,000|Spouse",
"memberSlug": "nancy-pelosi",
"memberName": "Nancy Pelosi",
"chamber": "house",
"district": "CA11",
"party": "D",
"ticker": "UBER",
"assetDescription": "Uber Technologies Inc",
"assetType": "Stock Option",
"type": "Purchase",
"owner": "Spouse",
"transactionDate": "2026-05-29",
"disclosureDate": "2026-06-24",
"amountLabel": "$500,001 - $1,000,000",
"amountMin": 500001,
"amountMax": 1000000,
"amountMid": 750001,
"sourceLink": "https://disclosures-clerk.house.gov/public_disc/ptr-pdfs/2026/20034836.pdf",
"ingestedAt": "2026-06-24T18:58:56.037Z"
}
]
},
"meta": {
"count": 1,
"fetchedAt": "2026-09-09T18:23:30.741Z",
"source": "sqlite"
}
}GET /v1/trackers
Every tracked portfolio, with its slug, archetype and benchmark. Eleven at the time of writing, and this endpoint is the live list.
archetype tells you what the portfolio is built from and therefore what it can tell you. hedge_fund portfolios come from quarterly 13F-HR filings, congress portfolios from STOCK Act reports, and oge_278t from OGE Form 278-T periodic transaction reports.
The slug set is curated and closed, so an unknown tracker slug is always wrong rather than not yet ingested. Read the slug from this endpoint rather than deriving it from a name: Nancy Pelosi is pelosi, Donald Trump is trump-portfolio, Warren Buffett is berkshire.
No parameters.
curl
curl "https://tracefour.com/v1/trackers"Python
import requests
response = requests.get("https://tracefour.com/v1/trackers", timeout=30)
response.raise_for_status()
for tracker in response.json()["data"]:
print(f'{tracker["slug"]:<18} {tracker["archetype"]:<12} {tracker["name"]}')JavaScript
const { data } = await fetch("https://tracefour.com/v1/trackers").then((r) => r.json())
const congressTrackers = data.filter((tracker) => tracker.archetype === "congress")
console.log(congressTrackers.map((tracker) => tracker.slug))Response
Captured from the curl above, trimmed from 11 rows to 2. meta trimmed to its first three keys.
{
"data": [
{
"slug": "pelosi",
"name": "Nancy Pelosi",
"entityName": null,
"role": "U.S. Representative (CA-11) · Speaker Emerita",
"archetype": "congress",
"benchmarkTicker": "SPY",
"url": "https://tracefour.com/trackers/pelosi"
},
{
"slug": "trump-portfolio",
"name": "Donald J. Trump",
"entityName": null,
"role": "President · disclosed via OGE Form 278-T (trustee-managed accounts)",
"archetype": "oge_278t",
"benchmarkTicker": "SPY",
"url": "https://tracefour.com/trackers/trump-portfolio"
}
],
"meta": {
"count": 11,
"fetchedAt": "2026-09-09T18:33:18.661Z",
"source": "tracker-registry"
}
}GET /v1/trackers/{slug}
One tracked portfolio: identity, the latest disclosed snapshot with per-holding weights, a value history, a benchmark series, and the caveats that apply to that source.
The shape varies by archetype, and that is deliberate. A congress tracker carries congressActivity with bought, sold, net flow and trade count. An OGE tracker carries ogeTransactions, whose rows have valueLow, valueHigh and valueLabel and no midpoint field at all, because a 278-T discloses a range and inventing a point estimate inside it would report something the filer did not say. A 13F tracker carries quarter-over-quarter holding changes.
caveatKeys is the machine-readable list of what limits that portfolio: lag-30-45d for the STOCK Act reporting delay, range-bucketed for bracketed amounts, spouse-included where a spouse’s trades are reported, quarterly-batched and trustee-managed for the OGE source. Render them. They are the difference between a number and an honest number.
A 13F reports long U.S.-listed equity positions as of quarter end, disclosed up to 45 days later. It excludes shorts, bonds, cash and private holdings. The value series on a tracker page is a price-return reconstruction of disclosed positions, not the filer’s actual return, and it must not be presented as performance.
| Parameter | Type | Accepts | Default |
|---|---|---|---|
slug | string (path) | Lowercase alphanumeric with hyphens, 1 to 50 characters, from the closed set in /v1/trackers. | required |
curl
curl "https://tracefour.com/v1/trackers/pelosi"Python
import requests
response = requests.get("https://tracefour.com/v1/trackers/pelosi", timeout=30)
response.raise_for_status()
payload = response.json()["data"]
print(payload["identity"]["displayName"], payload["sourceBadge"])
for holding in payload["snapshot"]["holdings"][:5]:
print(holding["ticker"], round(holding["weight"] * 100, 1), "%")
print("caveats:", ", ".join(payload["caveatKeys"]))JavaScript
const { data } = await fetch("https://tracefour.com/v1/trackers/pelosi").then((r) => r.json())
const top = data.snapshot.holdings.slice(0, 5)
console.log(data.identity.displayName, data.snapshot.holdingsCount, "positions")
console.log(top.map((holding) => `${holding.ticker} ${(holding.weight * 100).toFixed(1)}%`))Response
Captured from the curl above and heavily trimmed: holdings cut from 15 to 1, and the valueHistory, valueSeriesUsd, benchmarkSeries, tickersWithPage, ogeTransactions and filingHistory keys dropped because each is a long array. Every key shown is verbatim. meta trimmed to its first three keys.
{
"data": {
"identity": {
"slug": "pelosi",
"displayName": "Nancy Pelosi",
"entityName": null,
"role": "U.S. Representative (CA-11) · Speaker Emerita",
"archetype": "congress",
"sourceId": "nancy-pelosi",
"benchmarkTicker": "SPY",
"sortOrder": 20
},
"sourceBadge": "U.S. HOUSE · PTR",
"snapshot": {
"periodEnd": "2026-06-24",
"filedAt": "2026-06-24",
"totalValueUsd": 66975023,
"holdingsCount": 15,
"holdings": [
{
"ticker": "AVGO",
"issuerName": "Broadcom Inc - exercised 200 calls → 20,000 sh",
"valueUsd": 18000002,
"netUsd": 18000002,
"weight": 0.2687569364477859,
"asOfDate": "2025-06-20",
"avgBuyPriceUsd": 234.86183338948717,
"currentPriceUsd": 416.04998779296875,
"returnPct": 77.14670016349787
}
]
},
"congressActivity": {
"bought": 38100015,
"sold": 28875008,
"netFlow": 9225007,
"tradeCount": 23
},
"caveatKeys": [
"lag-30-45d",
"range-bucketed",
"spouse-included",
"no-reason-disclosed",
"options-as-underlying",
"concentration-cap"
]
},
"meta": {
"count": 1,
"fetchedAt": "2026-09-09T18:23:44.307Z",
"source": "sqlite"
}
}License and attribution
Periodic transaction reports, 13F-HR filings and OGE Form 278-T reports are public-domain U.S. government works, and Tracefour claims nothing over them. The compilation of them, meaning the parsing, member folding, price attachment and portfolio reconstruction, is licensed CC BY 4.0. Its one condition is attribution with a link, which every response supplies in meta.attribution.page and in the url on each record.
Related
- SEC Form 4 API: corporate insider filings, clusters and streaks.
- European insider trading API: BaFin, FCA, Finansinspektionen and AFM filings.
- MCP server: get_congress_member and get_tracker as agent tools.
Frequently asked questions
Is there a free API for Congress stock trades?
Yes. GET https://tracefour.com/v1/congress lists every member with disclosures and GET /v1/congress/{member} returns that member’s STOCK Act transactions as JSON. No key and no sign-up are needed, at 60 requests per hour per IP address, or 600 per hour with a free key.
What is the difference between /v1/trackers/pelosi and /v1/congress/nancy-pelosi?
They are two different addresses for related things. /v1/congress/nancy-pelosi returns the individual STOCK Act transactions as disclosed, newest first. /v1/trackers/pelosi returns a portfolio view assembled from those disclosures: current disclosed positions, weights, a value history and a benchmark comparison. The slug differs between the namespaces, so pelosi is a 404 on the congress endpoint and nancy-pelosi is a 404 on the tracker endpoint.
How current are congressional stock trade disclosures?
The STOCK Act requires a covered transaction to be reported within 30 days of the member becoming aware of it and no later than 45 days after the trade. A trade therefore reaches the public record roughly 30 to 45 days after it happened, and sometimes later. Tracefour ingests new reports hourly, but it cannot shorten a lag that is set by statute. Each row carries transactionDate and disclosureDate so the gap is visible.
Why are congressional trade amounts shown as ranges?
Because that is how they are filed. A periodic transaction report discloses a bracket such as $500,001 to $1,000,000 rather than an exact figure, and assets below $1,000 are not reportable at all. The API returns amountLabel exactly as filed with amountMin and amountMax as its bounds. OGE Form 278-T rows carry valueLow and valueHigh with no midpoint, because the range is the disclosure.
Which politicians and funds have tracked portfolios?
Call GET /v1/trackers for the live list. It covers congressional filers, hedge fund and conglomerate 13F filers, and one OGE Form 278-T filer, each with a slug, an archetype naming the filing type it is built from, and a benchmark ticker. The set is curated and closed, so an unlisted slug returns 404 rather than empty data.
Does the tracker value history show a politician’s actual returns?
No. It is a price-return reconstruction of the positions that were disclosed, re-indexed to zero at the first filing in the selected window. It excludes trade timing between filings, position sizing, fees, dividends and cash, and 13F sources exclude shorts, bonds and private holdings entirely. It must not be described as an actual return or as performance.