Blog · 2026-10-17

Build a Congress Insider Trading Tracker Bot in Python

Build a Congress Insider Trading Tracker Bot in Python
A real alert from a test run: traded September 8, disclosed October 1, posted the next time the bot ran.

The Senate had a chance to ban congressional stock trading on September 30. It fell seven votes short.

So members of Congress can still trade, and a congress insider trading tracker still has a job. You can build your own in about 60 lines of Python. It polls the House's STOCK Act disclosures through an API, remembers what it has already seen, and posts each new trade to Slack or Discord. The data is public and free at the source. The work is parsing it, which an API handles for you.

Key takeaway: A congress insider trading tracker bot polls the House's STOCK Act disclosures, remembers which trades it has already seen, and posts new ones to Slack or Discord through a webhook. Key each trade on its disclosure, not its trade date, because reports arrive up to 45 days late (a median of 16 days on recent House filings). Polling every six hours costs about 240 API credits a month.

I run the data pipeline behind one of these feeds. This is the bot I'd build, with code I ran against the live API before publishing.

How does a congress insider trading tracker work?

A congress insider trading tracker watches the periodic transaction reports that members of Congress must file under the STOCK Act, then alerts you when a new trade appears. The House Clerk publishes each report as a PDF. A tracker parses those PDFs into rows (member, ticker, buy or sell, dollar range, dates), checks which rows are new, and sends them wherever you'll see them.

Every tracker app on the market does those same four steps, and if you just want to watch by hand, how to track congressional stock trades covers the free sites.

The difference is who runs them. An app runs them for you and shows you the result in its own screen. A bot runs them for you and drops the result into the place you already look.

I prefer the bot. If your team already lives in Slack, nobody is going to open a separate app to check one thing.

The flow you're about to build looks like this.

StepWhat happensWho does it
1. FilingA member files a periodic transaction reportThe member
2. PublishingThe House Clerk posts the report as a PDFHouse Clerk
3. ParsingThe PDF becomes clean rows with a ticker and amountEdgrapi's /v1/congress
4. AlertingNew rows get posted to Slack or DiscordYour bot
How a congress trading tracker works
Apps and bots run the same four steps. Only the last one, where the alert lands, is yours to choose.

Where does congressional stock trading data come from?

Congressional stock trading data comes from the periodic transaction reports members file under the STOCK Act. House reports are published by the Clerk of the House at disclosures-clerk.house.gov, and Senate reports go to the Senate's eFD system. They are not on SEC EDGAR, even though some tutorials say so. Each report is a PDF, so the raw data needs parsing before code can use it.

That EDGAR mix-up is worth calling out, because the top Python tutorial for this exact project makes it. If you go looking for congressional trades in the SEC's API, you'll find corporate insiders, not lawmakers.

The House side is the cleaner of the two. The Clerk publishes a yearly index of filings plus each report as a typed PDF, with no login.

The Senate side is harder to reach from a server. Edgrapi's feed covers the House only today, and I'd rather say that plainly than have you find out from a missing alert.

How fast are congressional stock trades disclosed?

Congressional stock trades are disclosed up to 45 days after they happen. The House Committee on Ethics says a periodic transaction report is due by the earlier of 30 days after the member learns of the trade or 45 days after the trade itself, for transactions over $1,000. On Edgrapi's feed, the last 100 House trades disclosed through October 15, 2026 took a median of 16 days to appear.

That lag drives two choices later on: how the bot spots new trades, and how often it needs to check.

The rule comes straight from the House Committee on Ethics in its January 2023 PTR memo. The same memo says late reports get a 30-day grace period before fees kick in.

I pulled the numbers from our own feed on October 17, 2026. Those 100 trades came from 12 members across 86 tickers, 56 buys and 41 sells.

Disclosure lag (last 100 House trades)Days
Fastest0
Median16
Average17.1
Slowest33
Over the 45-day limitNone

Some members do file late. GetSignal's late-filing analysis counted 431 of 9,203 House transactions filed after 45 days between October 2025 and October 2026, or 4.7%.

When people call a tracker "real-time," they mean real-time to the disclosure. The trade itself happened weeks earlier.

Disclosure lag for the last 100 House trades
Every one of the last 100 House trades showed up within 33 days. The median was 16.

How do you build a congress trading tracker bot in Python?

Build the bot in four parts: fetch the latest trades from an API, skip the ones you've already seen, filter to the tickers or members you care about, and post the rest to a Slack or Discord webhook. The script below does all four in about 60 lines of standard-library Python, with no packages to install. Run it on a schedule and it sends only new trades.

You need three things first. An Edgrapi API key, a Python 3 install, and an incoming webhook URL from Slack or Discord.

Save this as congress_bot.py.

import json, os, pathlib, urllib.request

API = "https://api.edgrapi.com/v1/congress?limit=100"
SEEN = pathlib.Path("seen_trades.json")
TICKERS = {t.strip().upper() for t in os.environ.get("WATCH_TICKERS", "").split(",") if t.strip()}
MEMBERS = [m.strip().lower() for m in os.environ.get("WATCH_MEMBERS", "").split(",") if m.strip()]

def get(url, headers):
    req = urllib.request.Request(url, headers=headers)
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.loads(r.read())

def trade_key(t):
    # One report can list many trades, so key on the filing plus the trade.
    return "|".join([t["disclosure_url"], t["ticker"], t["action"],
                     t["transaction_date"], str(t["amount_low"])])

def wanted(t):
    if TICKERS and t["ticker"] not in TICKERS:
        return False
    if MEMBERS and not any(m in t["member"].lower() for m in MEMBERS):
        return False
    return True

def fmt(t):
    hi = f"${t['amount_high']:,}" if t["amount_high"] else "+"
    return (f"{t['member']} ({t['state_district']}) {t['action'].upper()} {t['ticker']} "
            f"${t['amount_low']:,}-{hi}, traded {t['transaction_date']}, "
            f"disclosed {t['disclosed_date']}\n{t['disclosure_url']}")

def post(text):
    if os.environ.get("SLACK_WEBHOOK_URL"):
        url, body = os.environ["SLACK_WEBHOOK_URL"], {"text": text}
    elif os.environ.get("DISCORD_WEBHOOK_URL"):
        url, body = os.environ["DISCORD_WEBHOOK_URL"], {"content": text[:2000]}
    else:
        print(text); return
    req = urllib.request.Request(url, data=json.dumps(body).encode(),
                                 headers={"Content-Type": "application/json"}, method="POST")
    urllib.request.urlopen(req, timeout=30).read()

def main():
    data = get(API, {"Authorization": "Bearer " + os.environ["EDGRAPI_KEY"]})
    seen = set(json.loads(SEEN.read_text())) if SEEN.exists() else None
    keys = [(trade_key(t), t) for t in data["trades"]]
    if seen is None:   # first run: remember everything, alert on nothing
        SEEN.write_text(json.dumps([k for k, _ in keys]))
        print(f"baseline saved: {len(keys)} trades"); return
    new = [t for k, t in keys if k not in seen and wanted(t)]
    for t in reversed(new):   # oldest first
        post(fmt(t))
    SEEN.write_text(json.dumps(sorted(seen | {k for k, _ in keys})))
    print(f"{len(new)} new trade(s) posted")

if __name__ == "__main__":
    main()

Run it once with no webhook set, and it saves a baseline instead of spamming you with 100 old trades.

$ EDGRAPI_KEY=edgr_... python congress_bot.py
baseline saved: 100 trades

From then on, each run posts only trades it hasn't seen. When I tested it on October 17, two new trades came through like this.

Cleo Fields (LA06) BUY AAPL $1,001-$15,000, traded 2026-09-08, disclosed 2026-10-01
https://disclosures-clerk.house.gov/public_disc/ptr-pdfs/2026/20035464.pdf

Every alert links to the original House PDF. Click it before you act on anything, because the PDF is the official record.

Why should the bot dedupe on the disclosure, not the trade date?

Dedupe on the disclosure itself, because trades show up weeks after they happen and one filing can list many trades. If your bot tracks "trades since my last run" by transaction date, it misses every trade that was made earlier but disclosed today, which is most of them. Keying on the filing link plus the trade's ticker, action, date, and amount catches each trade exactly once.

It's an easy bug to write, and the most popular tutorial for this project has it.

Think about what the lag does. A trade from September 8 gets disclosed on October 1. A bot that asks "what traded since yesterday?" on October 1 never sees it.

The fix is to stop asking about time at all. Ask "have I seen this exact trade before?" and keep the answer in a small file.

One more detail in the key. A single report can list the same ticker twice, say two buys on different days. Including the date and amount keeps those separate.

Dedupe on trade date versus on the disclosure
Asking about time misses late disclosures. Asking about identity does not.

How do you send congress trade alerts to Slack or Discord?

Send alerts to Slack or Discord with an incoming webhook, which is a private URL that accepts a JSON POST and turns it into a message. Slack's webhooks take a text field. Discord's take a content field capped at 2,000 characters. Create the webhook in your workspace or server settings, put the URL in an environment variable, and the bot posts there instead of printing.

Slack's incoming webhooks guide walks through creating one. You get a URL, and anything you POST to it lands in the channel you picked.

Discord's version lives in the channel's Integrations settings. Its webhook docs list the 2,000-character limit on content, which is why the script trims the text.

export SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
export WATCH_TICKERS=NVDA,MSFT,PG
python congress_bot.py

Keep the webhook URL private. Anyone who has it can post to your channel.

Filters are optional. Leave WATCH_TICKERS and WATCH_MEMBERS empty and you get every House trade. On the recent feed that was 100 trades disclosed over four weeks, about 25 a week.

Should you use a congress trading tracker app or build your own?

Use an app if you want charts and zero setup. Build your own if you want alerts inside the tools you already use, with filters you control. Apps and bots read the same House filings, so the data is identical. What differs is where results show up, how you filter them, and whether you can wire the feed into your own code or an AI agent.

I compared the main tracker apps in the best free congress stock trade trackers. They're good at browsing.

Browsing is a habit, and it's one I never keep up. An alert that lands in a channel I already read keeps working when I forget about it.

A bot also lets you do things apps don't. You can join congressional buys to your own watchlist, your portfolio, or other data like insider Form 4 buys from the same API.

Tracker appYour own bot
SetupNoneAbout 15 minutes
Where alerts landThe app or its emailSlack, Discord, anywhere with a webhook
FiltersThe app's optionsAny rule you can write
Data sourceHouse and Senate filingsHouse filings via Edgrapi (Senate not yet)
CostFree tier or subscriptionAPI credits, a few hundred a month

Which congressional trades are worth an alert?

Not every disclosure deserves a ping. Most House trades are small, often in the lowest $1,001 to $15,000 range, and many are routine portfolio moves. The trades worth an alert are usually the larger ranges, purchases rather than sales, and stocks you already follow. Start with a ticker watchlist, add a minimum amount if the channel gets noisy, and keep the PDF link on every message.

On our feed, 68 of the last 100 House trades sat in that lowest $1,001 to $15,000 band. Another 25 were $15,001 to $50,000, and only 7 were bigger.

A floor of $15,001 cuts the noise by about two thirds. You can add it with one line in wanted():

if t["amount_low"] < 15001:
    return False

Purchases tend to be more interesting than sales. A member can sell for a hundred reasons, like taxes or a house, but a buy is a choice to own something.

I'd skip anything clever, like inferring what a member "knows" from their committee. The disclosure tells you what was traded and roughly how much. It doesn't tell you why.

Amount ranges of recent House trades
Two thirds of recent House trades sat in the smallest reporting band.

How often should a congress trading tracker check for new trades?

Check a few times a day, not every minute. Because members have up to 45 days to disclose and new reports arrive in batches, polling every few hours catches every trade without wasting calls. Each call to Edgrapi's congress feed costs 2 credits, so four checks a day cost about 240 credits a month, which fits easily inside the $10 Starter plan.

I'd run it every six hours with cron.

0 */6 * * * cd /path/to/bot && EDGRAPI_KEY=edgr_... python congress_bot.py

GitHub Actions works too if you don't have a server. Just persist seen_trades.json between runs, or the bot will think every run is its first.

Faster polling doesn't buy you anything here. The trade is already weeks old by the time the Clerk posts it, so checking every minute just burns credits on empty responses.

Polling cadence versus monthly credit cost
Four checks a day catch every trade. Checking every minute costs 360 times as much and catches nothing extra.

Can an AI agent track congressional trades instead?

Yes. Instead of a script, you can give an AI assistant like Claude a tool that reads the same congressional trading data, then ask it questions in plain English. Edgrapi's MCP server exposes a get_congress tool, so an agent can answer "any House purchases of NVDA this month?" from live disclosures. It's better for questions, while the bot is better for steady alerts.

They pair well. The bot pings the channel when something new lands, and the agent handles the follow-up, like "which other House members bought this stock in the last six months?"

Setup is one config block in Claude or Cursor. I covered it in the government data MCP server guide, and the raw endpoint is documented in the congress trading API guide.

Is a congressional stock trading ban coming?

Not yet. The House passed the Stop Insider Trading Act, H.R. 7008, by 232 to 198 on July 22, 2026, but on September 30 the Senate failed to invoke cloture on the motion to proceed, 53 to 47, short of the 60 votes needed. Until Congress passes a ban, members can keep trading individual stocks and disclosing them under the STOCK Act.

Both votes are on the bill's official record at congress.gov.

If a ban does pass, read the final text before you turn your bot off. What members have to sell, and by when, will decide how long new disclosures keep showing up.

I'll update this post if the Senate takes it up again.

Ship the bot today and check the PDF behind every alert

Set up the bot in three steps: get an API key, create a Slack or Discord webhook, and schedule congress_bot.py to run every six hours. Run it once to save a baseline, then let it post only new trades. Treat every alert as a pointer to the House PDF, not as advice, since the trade is weeks old. Get a free Edgrapi key and you can have it posting today.

Frequently asked questions

How do I get alerts when a member of Congress buys a stock?

Use a tracker that watches STOCK Act disclosures and alerts on new ones. You can use an app, or build a small bot that polls a congressional trading API, skips trades it has already seen, and posts new ones to Slack or Discord through a webhook. Filter by ticker or member if you only care about a few names.

How do I build my own congress trading tracker?

Fetch recent trades from a congressional trading API, store a key for each trade you've seen, filter to the tickers or members you want, and post new trades to a Slack or Discord webhook. Run the script on a schedule, every few hours is enough. The Python version in this guide is about 60 lines with no extra packages.

Where does congressional stock trading data come from?

It comes from periodic transaction reports filed under the STOCK Act. The House Clerk publishes House reports at disclosures-clerk.house.gov, and Senate reports go to the Senate eFD system. They are not on SEC EDGAR. Each report is a PDF, so trackers parse them into rows with the member, ticker, amount range, and dates.

How fast are congressional stock trades disclosed?

A report is due by the earlier of 30 days after the member learns of a trade or 45 days after the trade, for trades over $1,000, per the House Committee on Ethics. In practice, the last 100 House trades on Edgrapi's feed through October 15, 2026 took a median of 16 days to be disclosed.

Can I get congressional trades in Slack or Discord?

Yes. Create an incoming webhook in Slack or Discord, then have a script post each new trade to it as JSON. Slack expects a text field and Discord expects content, limited to 2,000 characters. The bot in this guide supports both, chosen by which webhook URL you set.

Did the congressional stock trading ban pass?

No. The House passed H.R. 7008, the Stop Insider Trading Act, 232 to 198 on July 22, 2026. On September 30, 2026, the Senate failed to invoke cloture on the motion to proceed, 53 to 47, short of the 60 votes needed. Members can still trade individual stocks and must disclose them.

Get a free API key