A practical guide to building insider trading alerts using SEC Form 4 data.
- 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
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.
Sign up at earningsfeed.com and generate an API key from your dashboard. The free tier (15 req/min) is enough for this tutorial.
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.
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 |
- 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.
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
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")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();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.
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()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:,}")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:,}")| 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-Remainingheader to avoid hitting limits
Full documentation: earningsfeed.com/api/docs
Key endpoints used in this tutorial:
GET /api/v1/insider/transactions- List insider transactionsGET /api/v1/companies/{cik}- Get company detailsGET /api/v1/companies/search- Find company by ticker/name
- 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/holdingsendpoint to see what hedge funds are buying
Built with the Earnings Feed API. Questions? support@earningsfeed.com