Home / Blog / Tutorial

How to Build a Live Congress Trading Alert Bot in 15 Minutes

Most retail investors find out about congressional stock trades days or weeks after the fact — scrolling through Twitter, or stumbling on a news article. By then, the market has already reacted.

There's a better way. The STOCK Act requires members of Congress to disclose trades within 45 days. Those disclosures are public the moment they're filed. With the CongressInvests API, you can get notified within hours of a new filing — often before most financial media covers it.

In this guide, we'll build a Python bot that watches specific tickers and fires a Slack alert the moment a new congressional trade is disclosed.

What you'll need

No API key required for the polling approach in steps 1–3. You'll only need a Pro key for the real-time webhook setup in step 4.

Step 1: Your first API call

No setup needed. Run this from your terminal and you'll get back every NVDA trade filed by Congress in the last year:

bash
curl https://congressinfor-production.up.railway.app/trades/NVDA

The response tells you the member's name, chamber, trade type, disclosed dollar range, and — importantly — the last_updated timestamp so you know exactly how fresh the data is:

json — abbreviated
{
  "ticker":           "NVDA",
  "total":            15,
  "last_updated":     "2026-05-31T10:04:17Z",
  "data_lag_minutes": 14,
  "trades": [
    {
      "member":     "John Boozman",
      "chamber":    "Senate",
      "ticker":     "NVDA",
      "trade_type": "buy",
      "amount":     "$1,001 - $15,000",
      "tx_date":    "2026-03-19",
      "disclosed":  "2026-04-14",
      "link":       "https://efdsearch.senate.gov/..."
    }
  ]
}

Step 2: Write the polling watcher

Install requests if you haven't:

bash
pip install requests

This script polls a list of tickers every hour, tracks which trades it's already seen, and prints an alert for anything new. It stays well within the free tier's 100 req/day limit even across four tickers.

python — watcher.py
import requests
import time

BASE    = "https://congressinfor-production.up.railway.app"
TICKERS = ["NVDA", "AAPL", "MSFT", "TSLA"]
seen    = set()

def check():
    for ticker in TICKERS:
        resp = requests.get(f"{BASE}/trades/{ticker}", timeout=10)
        if not resp.ok:
            continue
        for t in resp.json().get("trades", []):
            key = f"{t['member']}:{t['ticker']}:{t['tx_date']}"
            if key not in seen:
                seen.add(key)
                alert(t)

def alert(t):
    action = "BOUGHT" if t["trade_type"] == "buy" else "SOLD"
    print(f"🔔  {t['member']} ({t['chamber']}) {action} {t['ticker']} — {t['amount']}")
    print(f"    Filed: {t['disclosed']}  |  {t['link']}\n")

while True:
    check()
    print("Checked. Sleeping 1 hour…")
    time.sleep(3600)

Step 3: Add Slack notifications

First, create an Incoming Webhook in your Slack workspace. It takes about two minutes and gives you a URL like https://hooks.slack.com/services/T.../B.../....

Replace the alert() function with one that posts a formatted Slack message:

python
SLACK_WEBHOOK = "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"

def alert(t):
    action = "🟢 BOUGHT" if t["trade_type"] == "buy" else "🔴 SOLD"
    text = (
        f"*{action}* — {t['member']} ({t['chamber']})\n"
        f"*{t['ticker']}* · {t['amount']} · Filed {t['disclosed']}\n"
        f"<{t['link']}|View filing ↗>"
    )
    requests.post(SLACK_WEBHOOK, json={"text": text}, timeout=5)

Now when Nancy Pelosi files an NVDA trade, you'll get a Slack message within an hour of your next poll cycle.

Tip: Run this on a free-tier cloud VM (Railway, Render, or Fly.io) so it keeps running 24/7 without your laptop staying on.

Step 4: Go real-time with webhooks (Pro)

Polling every hour is fine, but the filing you care about most might land right after a poll. With a Pro API key, you can subscribe to push notifications that fire within minutes of a new filing being detected — no polling loop needed.

Subscribe to a ticker

bash
curl -X POST https://congressinfor-production.up.railway.app/webhooks/subscribe \
  -H "X-Api-Key: YOUR_PRO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://your-server.com/hook",
    "ticker":      "NVDA",
    "events":      ["any"]
  }'

You can also subscribe by politician name instead of ticker — handy if you want to follow a specific member across all their trades:

bash
curl -X POST https://congressinfor-production.up.railway.app/webhooks/subscribe \
  -H "X-Api-Key: YOUR_PRO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"webhook_url": "https://your-server.com/hook", "politician": "Nancy Pelosi"}'

Handle the webhook payload

Your server receives a POST with this payload on every new trade:

json
{
  "event":            "new_trade",
  "ticker":           "NVDA",
  "politician":       "Nancy Pelosi",
  "trade_type":       "Purchase",
  "amount":           "$1,000,001 - $5,000,000",
  "filing_date":      "2026-05-30",
  "transaction_date": "2026-05-15",
  "source":           "House",
  "api_url":          "https://congressinfor-production.up.railway.app/trades/NVDA"
}

A minimal Flask receiver that pipes it straight to Slack:

python — receiver.py
from flask import Flask, request, jsonify
import requests

app          = Flask(__name__)
SLACK_WEBHOOK = "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"

@app.route("/hook", methods=["POST"])
def hook():
    t      = request.get_json()
    action = "🟢 BOUGHT" if t["trade_type"] == "Purchase" else "🔴 SOLD"
    text   = (
        f"*{action}* — {t['politician']}\n"
        f"*{t['ticker']}* · {t['amount']} · Filed {t['filing_date']}\n"
        f"<{t['api_url']}|View all {t['ticker']} trades ↗>"
    )
    requests.post(SLACK_WEBHOOK, json={"text": text}, timeout=5)
    return jsonify({"ok": True})

if __name__ == "__main__":
    app.run(port=5000)
Testing locally? Use ngrok to expose your local server: ngrok http 5000. Then subscribe using the ngrok HTTPS URL as your webhook_url.

What's next

Ready to go real-time?

Pro tier unlocks webhook alerts, 50,000 requests/day, and priority cache refresh — starting at $29/month.

Get Pro access