Skip to content

Instantly share code, notes, and snippets.

@nrempel
Created December 15, 2025 01:08
Show Gist options
  • Select an option

  • Save nrempel/a563290f6ed75670043776f6f8d9bbb5 to your computer and use it in GitHub Desktop.

Select an option

Save nrempel/a563290f6ed75670043776f6f8d9bbb5 to your computer and use it in GitHub Desktop.
Track insider trading with the Earnings Feed SEC API - Python & JavaScript examples

How to Track Insider Trading with the Earnings Feed API

A practical guide to building insider trading alerts using SEC Form 4 data.

What You'll Learn

  • How to fetch insider transactions from the SEC
  • Filter for significant purchases vs. routine sales
  • Build a simple alerting system
  • Understand what the data actually means

Background: Why Insider Trading Data Matters

Corporate insiders—CEOs, CFOs, directors, and 10% owners—must report their stock transactions to the SEC within 2 business days via Form 4. This is public information, and historically, insider buying correlates with future stock performance better than almost any other signal.

The key insight: insiders sell for many reasons (diversification, taxes, planned sales), but they typically only buy when they believe the stock is undervalued.

Getting Started

1. Get an API Key

Sign up at earningsfeed.com and generate an API key from your dashboard. The free tier (15 req/min) is enough for this tutorial.

2. Your First Request

curl -s "https://earningsfeed.com/api/v1/insider/transactions?limit=5" \
  -H "Authorization: Bearer YOUR_API_KEY" | jq .

This returns the 5 most recent insider transactions across all companies.

Understanding the Data

Each transaction includes:

Field Description
personName Name of the insider
officerTitle Their role (CEO, CFO, Director, etc.)
ticker Stock symbol
companyName Company name
transactionCode Type of transaction (P=Purchase, S=Sale, etc.)
shares Number of shares (string)
pricePerShare Price per share (string)
transactionValue Total dollar value (string)
sharesAfter Insider's holdings after this transaction (string)
transactionDate When the trade occurred

Transaction Codes That Matter

  • P - Open market purchase (the bullish signal)
  • S - Open market sale
  • A - Award/grant (stock compensation)
  • M - Exercise of derivative (options exercise)
  • F - Tax payment (shares sold to cover taxes)

For detecting "skin in the game" buying, focus on code P.

Practical Examples

Find Large Insider Purchases

Filter for open market purchases over $100k:

import requests

API_KEY = "your_api_key_here"

response = requests.get(
    "https://earningsfeed.com/api/v1/insider/transactions",
    headers={"Authorization": f"Bearer {API_KEY}"},
    params={
        "direction": "buy",      # Only acquisitions
        "codes": "P",            # Open market purchases only
        "minValue": 100000,      # $100k minimum
        "limit": 25
    }
)

data = response.json()

for txn in data["items"]:
    value = int(txn['transactionValue'])
    print(f"{txn['transactionDate']} | {txn['ticker']:5} | "
          f"{txn['personName']:30} | ${value:>12,}")

Sample output:

2024-12-10 | NVDA  | Jensen Huang                   |    $5,234,000
2024-12-09 | JPM   | Jamie Dimon                    |   $12,500,000
2024-12-08 | AMZN  | Andrew Jassy                   |    $2,100,000

Track a Specific Company

Monitor insider activity at Apple:

response = requests.get(
    "https://earningsfeed.com/api/v1/insider/transactions",
    headers={"Authorization": f"Bearer {API_KEY}"},
    params={
        "ticker": "AAPL",
        "limit": 50
    }
)

for txn in response.json()["items"]:
    direction = "BUY" if txn["acquiredDisposed"] == "A" else "SELL"
    shares = float(txn['shares'])
    print(f"{txn['transactionDate']} | {direction:4} | "
          f"{txn['personName']:25} | {shares:>10,.0f} shares")

JavaScript/Node.js Version

const API_KEY = 'your_api_key_here';

async function getInsiderBuys() {
  const params = new URLSearchParams({
    direction: 'buy',
    codes: 'P',
    minValue: '100000',
    limit: '25'
  });

  const response = await fetch(
    `https://earningsfeed.com/api/v1/insider/transactions?${params}`,
    {
      headers: { 'Authorization': `Bearer ${API_KEY}` }
    }
  );

  const { items } = await response.json();

  for (const txn of items) {
    const value = parseInt(txn.transactionValue, 10);
    console.log(`${txn.ticker}: ${txn.personName} bought $${value.toLocaleString()}`);
  }
}

getInsiderBuys();

Building an Alert System

Here's a simple script that checks for large purchases and sends alerts:

import requests
from datetime import datetime, timedelta

API_KEY = "your_api_key_here"
SLACK_WEBHOOK = "your_slack_webhook_url"  # optional

def check_insider_buys():
    """Check for significant insider purchases in the last 24 hours."""

    yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")

    response = requests.get(
        "https://earningsfeed.com/api/v1/insider/transactions",
        headers={"Authorization": f"Bearer {API_KEY}"},
        params={
            "direction": "buy",
            "codes": "P",
            "minValue": 500000,  # $500k+ purchases
            "startDate": yesterday,
            "limit": 100
        }
    )

    data = response.json()

    if not data["items"]:
        print("No significant insider purchases in the last 24 hours")
        return

    print(f"Found {len(data['items'])} significant insider purchases:\n")

    for txn in data["items"]:
        value = int(txn['transactionValue'])
        shares = float(txn['shares'])
        price = float(txn['pricePerShare'])
        shares_after = float(txn['sharesAfter'])
        alert = (
            f"${value:,} purchase\n"
            f"  Insider: {txn['personName']} ({txn.get('officerTitle', 'N/A')})\n"
            f"  Company: {txn['ticker']} - {txn['companyName']}\n"
            f"  Shares: {shares:,.0f} @ ${price:.2f}\n"
            f"  Holdings after: {shares_after:,.0f} shares\n"
        )
        print(alert)

        # Optional: Send to Slack
        # requests.post(SLACK_WEBHOOK, json={"text": alert})

if __name__ == "__main__":
    check_insider_buys()

Run this on a cron job (e.g., daily at market close) to catch significant insider buying.

Advanced Filters

Cluster Buying Detection

When multiple insiders at the same company buy within a short window, that's often more significant than a single purchase:

from collections import defaultdict
from datetime import datetime, timedelta

def find_cluster_buying():
    """Find companies with multiple insider purchases this week."""

    week_ago = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d")

    response = requests.get(
        "https://earningsfeed.com/api/v1/insider/transactions",
        headers={"Authorization": f"Bearer {API_KEY}"},
        params={
            "direction": "buy",
            "codes": "P",
            "startDate": week_ago,
            "limit": 100
        }
    )

    # Group by company
    company_buys = defaultdict(list)
    for txn in response.json()["items"]:
        company_buys[txn["ticker"]].append(txn)

    # Find clusters (2+ unique insiders buying)
    clusters = {
        ticker: txns
        for ticker, txns in company_buys.items()
        if len(set(t["personCik"] for t in txns)) >= 2
    }

    for ticker, txns in clusters.items():
        total_value = sum(int(t["transactionValue"]) for t in txns)
        unique_insiders = len(set(t["personName"] for t in txns))
        print(f"{ticker}: {unique_insiders} insiders bought ${total_value:,} total")

find_cluster_buying()

Filter by Insider Role

Sometimes you only care about C-suite executives:

response = requests.get(
    "https://earningsfeed.com/api/v1/insider/transactions",
    headers={"Authorization": f"Bearer {API_KEY}"},
    params={
        "direction": "buy",
        "codes": "P",
        "limit": 100
    }
)

# Filter for C-suite only
c_suite_titles = ["ceo", "cfo", "coo", "president", "chief"]

for txn in response.json()["items"]:
    title = (txn.get("officerTitle") or "").lower()
    if any(role in title for role in c_suite_titles):
        value = int(txn['transactionValue'])
        print(f"{txn['ticker']}: {txn['officerTitle']} {txn['personName']} "
              f"bought ${value:,}")

Pagination for Historical Analysis

For backtesting or research, you'll need to paginate through historical data:

def get_all_purchases_for_year(year):
    """Fetch all insider purchases for a given year."""

    all_transactions = []
    cursor = None

    while True:
        params = {
            "direction": "buy",
            "codes": "P",
            "startDate": f"{year}-01-01",
            "endDate": f"{year}-12-31",
            "limit": 100
        }
        if cursor:
            params["cursor"] = cursor

        response = requests.get(
            "https://earningsfeed.com/api/v1/insider/transactions",
            headers={"Authorization": f"Bearer {API_KEY}"},
            params=params
        )

        data = response.json()
        all_transactions.extend(data["items"])

        if not data["hasMore"]:
            break

        cursor = data["nextCursor"]
        print(f"Fetched {len(all_transactions)} transactions...")

    return all_transactions

# Example: analyze 2024 insider buying
purchases_2024 = get_all_purchases_for_year(2024)
print(f"\nTotal insider purchases in 2024: {len(purchases_2024)}")

total_value = sum(int(t["transactionValue"]) for t in purchases_2024)
print(f"Total value: ${total_value:,}")

Rate Limits and Best Practices

Tier Rate Limit Best For
Free 15 req/min Development, testing
Pro 60 req/min Production apps
Trader 300 req/min High-frequency polling

Tips:

  • Cache responses when possible
  • Use limit=100 (maximum) to reduce request count
  • Implement exponential backoff on rate limit errors (HTTP 429)
  • Check X-RateLimit-Remaining header to avoid hitting limits

API Reference

Full documentation: earningsfeed.com/api/docs

Key endpoints used in this tutorial:

  • GET /api/v1/insider/transactions - List insider transactions
  • GET /api/v1/companies/{cik} - Get company details
  • GET /api/v1/companies/search - Find company by ticker/name

What's Next?

  • Combine with price data: Correlate insider buying with subsequent stock performance
  • Add technical filters: Only alert when insiders buy during price dips
  • Build a dashboard: Visualize insider activity across your watchlist
  • Track institutional holdings: Use the /api/v1/institutional/holdings endpoint to see what hedge funds are buying

Built with the Earnings Feed API. Questions? support@earningsfeed.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment