Skip to content

Congressional Trading Dataset

Access stock trading activity from both chambers of the US Congress. Track purchases and sales disclosed by House Representatives and Senators under the STOCK Act, collected directly from the official disclosure systems and updated daily.

The Congressional Trading dataset provides:

  • Politician Name: Name of the House or Senate member
  • Chamber: Whether the trade was filed by a House Representative or a Senator
  • Transaction Type: Purchase or Sale
  • Amount Range: Transaction size bracket, normalized to the statutory STOCK Act brackets — the string as originally filed is preserved in amountRaw, and rows whose amount could not be safely normalized are flagged via amountFlag
  • Beneficial Owner: Whose account traded (owner) — the member’s own (SELF), spouse (SP), dependent child (DC), or joint (JT)
  • Transaction Date: When the trade occurred
  • Dual Dating: Both the transaction date and the public disclosure date (disclosureDate) — the disclosure date marks when the trade became public, making it the correct point-in-time anchor for backtesting
  • Historical Data: 10+ years of history (filings since 2016), 100,000+ ticker-matched transactions
Chamber Source Update Frequency
House House Clerk financial disclosures (Periodic Transaction Reports) Daily
Senate Senate eFD system (Periodic Transaction Reports) Daily

Filings are collected directly from the official House and Senate disclosure systems — not resold third-party feeds — with the original filings archived for provenance. Coverage includes all 535 sitting members of both chambers, plus covered filings by former members, and spans US-listed stocks and ETFs with 10+ years of history (filings since 2016) available for backtesting.

Disclosure timing: the STOCK Act requires members of Congress to disclose trades within 45 days of the transaction. New filings appear in the dataset the day they are collected, but the reporting lag is inherent to the disclosure regime — factor it into any signal research. Members occasionally disclose trades late, so transaction dates older than the filing window do appear.

Every trade carries both dates, so you can measure that lag directly rather than assuming it: date is when the member traded, disclosureDate is when the filing became public. Any backtest should enter on disclosureDate — entering on date assumes knowledge that was not available at the time and will overstate returns. Disclosure dates were backfilled across the full history, so nulls are rare — but the field is still nullable, so guard for missing values in point-in-time work.

Each trade identifies whose account traded via the owner field:

Value Meaning
SELF The member’s own account
SP Spouse
DC Dependent child
JT Joint account
Account code A member-specific account identifier as filed
UNKNOWN Senate filing left the owner column blank

House filings that leave the owner column blank report SELF, per the House PTR-form instructions. This lets you separate trades a member made directly from spousal or family-account activity — a distinction that matters when weighting trades by how informed they are likely to be.

Congressional disclosures report amounts in ranges:

Range Minimum Maximum
$1,001 - $15,000 $1,001 $15,000
$15,001 - $50,000 $15,001 $50,000
$50,001 - $100,000 $50,001 $100,000
$100,001 - $250,000 $100,001 $250,000
$250,001 - $500,000 $250,001 $500,000
$500,001 - $1,000,000 $500,001 $1,000,000
$1,000,001 - $5,000,000 $1,000,001 $5,000,000
Over $5,000,000 $5,000,001 N/A

The amount field is normalized to these statutory brackets whenever the filed string is an unambiguous formatting variant of one; when a value is rewritten, the string as originally filed is preserved in amountRaw. Exact values (e.g., "$360.00") and open-ended filing categories (e.g., "Over $1,000,000") are kept as filed. A filing whose amount could not be read at all reports amount as "Unknown" with amountFlag set to review; a filing with two defensible readings is flagged ambiguous and kept as filed. Clean rows — the overwhelming majority — carry null in both fields.

House and Senate trades are served by separate endpoints with an identical schema:

from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
house_df = fb.house_trades.ticker("NVDA", as_dataframe=True)
senate_df = fb.senate_trades.ticker("NVDA", as_dataframe=True)
print(house_df)
print(senate_df)

For complete code examples in Python, JavaScript, C++, Rust, and cURL, see the API Reference.

Plot congressional trades on a price chart with the built-in SDK charts. You must supply your own price data:

from finbrain import FinBrainClient
import yfinance as yf
fb = FinBrainClient(api_key="YOUR_API_KEY")
# Provide your own price data
price_df = yf.download("NVDA", start="2024-01-01", end="2025-01-01")
# One-line interactive charts with buy/sell markers on price
fb.plot.house_trades("NVDA", price_df)
fb.plot.senate_trades("NVDA", price_df)
House Trades Chart
NVDA House Representative trades overlaid on price chart
Senate Trades Chart
NVDA Senate trades overlaid on price chart

Build alerts for significant congressional purchases across both chambers:

from finbrain import FinBrainClient
import pandas as pd
fb = FinBrainClient(api_key="YOUR_API_KEY")
LARGE_TRADES = [
"$500,001 - $1,000,000",
"$1,000,001 - $5,000,000",
"Over $5,000,000"
]
def get_congress_trades(symbol):
"""Combine House and Senate trades for a symbol"""
frames = []
for chamber, api in [("house", fb.house_trades), ("senate", fb.senate_trades)]:
try:
df = api.ticker(symbol, as_dataframe=True)
df["chamber"] = chamber
frames.append(df)
except Exception:
continue
return pd.concat(frames) if frames else pd.DataFrame()
def scan_large_congressional_trades(symbols):
"""Find stocks with large congressional purchases"""
results = []
for symbol in symbols:
df = get_congress_trades(symbol)
if df.empty:
continue
large_purchases = df[
(df["transactionType"] == "Purchase") &
(df["amount"].isin(LARGE_TRADES))
]
if not large_purchases.empty:
results.append({
"symbol": symbol,
"trades": large_purchases
})
return results
# Scan popular stocks
symbols = ["NVDA", "AAPL", "MSFT", "GOOGL", "AMZN", "META", "TSLA"]
alerts = scan_large_congressional_trades(symbols)
for alert in alerts:
print(f"\n{alert['symbol']}:")
for _, trade in alert['trades'].iterrows():
print(f" {trade['politician']} ({trade['chamber']}): {trade['amount']}")

Track trading activity of specific members of Congress:

from finbrain import FinBrainClient
import pandas as pd
fb = FinBrainClient(api_key="YOUR_API_KEY")
def get_politician_trades(politician_name, symbols):
"""Get all trades by a specific member across both chambers"""
all_trades = []
for symbol in symbols:
for api in [fb.house_trades, fb.senate_trades]:
try:
df = api.ticker(symbol, as_dataframe=True)
mask = df["politician"].str.lower().str.contains(politician_name.lower())
matched = df[mask].copy()
matched["symbol"] = symbol
all_trades.append(matched)
except Exception:
continue
return pd.concat(all_trades) if all_trades else pd.DataFrame()
# Track a specific politician's trades
trades = get_politician_trades(
"Pelosi",
["NVDA", "AAPL", "MSFT", "GOOGL", "AMZN", "CRM", "RBLX"]
)
for date, row in trades.iterrows():
print(f"{date}: {row['symbol']} - {row['transactionType']} {row['amount']}")

Find stocks where multiple members of Congress are buying:

from finbrain import FinBrainClient
import pandas as pd
fb = FinBrainClient(api_key="YOUR_API_KEY")
def find_cluster_buying(symbol, min_buyers=3):
"""Find if multiple members of Congress are buying a stock"""
frames = []
for api in [fb.house_trades, fb.senate_trades]:
try:
frames.append(api.ticker(symbol, as_dataframe=True))
except Exception:
continue
if not frames:
return None
df = pd.concat(frames)
purchases = df[df["transactionType"] == "Purchase"]
# Count unique members making purchases
buyers = purchases["politician"].unique()
if len(buyers) >= min_buyers:
return {
"symbol": symbol,
"unique_buyers": len(buyers),
"total_purchases": len(purchases),
"politicians": list(buyers)
}
return None
# Check multiple symbols
for symbol in ["NVDA", "AAPL", "MSFT", "GOOGL", "META"]:
result = find_cluster_buying(symbol)
if result:
print(f"{symbol}: {result['unique_buyers']} unique buyers")