Home / Tutorials / Ownership & fund analysis
Ownership & fund analysis

Build an insider buying screener with Form 4 data

Screen open market insider purchases (Form 4 code P) across the whole market with SecFeed's bulk insider data, then drill into one company.

Open market purchases by officers and directors (transaction code P on Form 4) are a widely studied data point because insiders buy with their own money. This tutorial screens every purchase filed over two months, finds companies where several insiders bought, then looks at one company's full history.

Step 1: load two months of insider transactions

For market wide screens, the bulk dataset is the efficient path: one call returns every parsed Form 3, 4 and 5 row filed in a month as gzipped JSON Lines.

import gzip
import json
import os
import requests

BASE = "https://secfeedapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"}

# Parsed Form 3/4/5 rows, one gzipped JSON Lines file per filing month
rows = []
for month in ["2026-09", "2026-10"]:
    r = requests.get(f"{BASE}/v1/bulk/download", params={"dataset": "insider-transactions", "month": month},
                     headers=HEADERS, timeout=300)
    r.raise_for_status()
    rows += [json.loads(line) for line in gzip.decompress(r.content).splitlines()]
print(len(rows), "rows filed in September and October 2026")

def role(owner):
    if owner.get("isOfficer"):
        return owner.get("officerTitle") or "Officer"
    if owner.get("isDirector"):
        return "Director"
    if owner.get("isTenPercentOwner"):
        return "10% owner"
    return owner.get("otherText") or "Other"

buys = [x for x in rows if x["code"] == "P" and x["kind"] == "transaction" and x["shares"] and x["price"]]
for x in buys:
    x["usd"] = x["shares"] * x["price"]
buys = [x for x in buys if x["usd"] >= 100_000 and x["price"] < 5000]  # a price above $5,000 is almost always a filer typo
print(len(buys), "open market purchases of $100,000 or more")

# cluster: issuers where two or more different insiders bought
by_issuer = {}
for x in buys:
    by_issuer.setdefault((x["ticker"], x["issuer_name"]), set()).add(x["owners"][0]["name"])
clusters = {k: v for k, v in by_issuer.items() if len(v) >= 2}
print(len(clusters), "issuers with purchases by 2+ insiders")
print()
for (ticker, name), insiders in sorted(clusters.items(), key=lambda kv: -len(kv[1]))[:5]:
    print(f"{ticker or '-':6} {name[:30]:30} {len(insiders)} insiders")
print()
for x in sorted(buys, key=lambda x: -x["usd"])[:8]:
    o = x["owners"][0]
    print(f"{x['tx_date']}  {(x['ticker'] or '-'):6} {x['issuer_name'][:28]:28} {o['name'][:24]:24} {role(o)[:22]:22} ${x['usd']:>13,.0f}")

Output:

34121 rows filed in September and October 2026
778 open market purchases of $100,000 or more
55 issuers with purchases by 2+ insiders

BBD    BANK BRADESCO                  22 insiders
SBLK   Star Bulk Carriers Corp.       6 insiders
NONE   Blackstone Multi-Strategy Hedg 5 insiders
ADRX   ADARx Pharmaceuticals, Inc.    5 insiders
GME    GameStop Corp.                 4 insiders

2026-09-15  TYRA   Tyra Biosciences, Inc.       Shah Rajeev M.           Director               $  199,999,994
2026-09-01  PBLSX  AMG Pantheon Infrastructure  Pantheon Infrastructure  10% owner              $   99,379,127
2026-09-18  LEN, LEN.B LENNAR CORP /NEW/            BERKSHIRE HATHAWAY INC   10% owner              $   93,075,791
2026-09-04  RSG    REPUBLIC SERVICES, INC.      GATES WILLIAM H III      10% owner              $   59,791,602
2026-09-03  RSG    REPUBLIC SERVICES, INC.      GATES WILLIAM H III      10% owner              $   56,956,600
2026-09-08  RSG    REPUBLIC SERVICES, INC.      GATES WILLIAM H III      10% owner              $   55,425,276
2026-09-30  LEN    LENNAR CORP /NEW/            BUFFETT WARREN E         10% owner              $   52,120,753
2026-09-09  RSG    REPUBLIC SERVICES, INC.      GATES WILLIAM H III      10% owner              $   47,227,619

What the screen does

  1. Keeps rows where kind is transaction and code is P (open market purchase). Holdings rows and other codes, such as grants (A), option exercises (M) and tax withholding (F), are dropped.
  2. Computes the dollar amount as shares times price, keeps $100,000 and up, and drops prices above $5,000 per share, which are almost always data entry errors by the filer.
  3. Groups by issuer and counts distinct insiders to find cluster purchases.

The owners field carries the reporting person's name and relationship flags (isDirector, isOfficer with officerTitle, isTenPercentOwner), which is how the role column is built.

Step 2: drill into one company

For one issuer, call /v1/insider-trading with a ticker:

import os
import requests

BASE = "https://secfeedapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"}

r = requests.get(f"{BASE}/v1/insider-trading", params={"ticker": "NVDA", "size": 50}, headers=HEADERS, timeout=120)
r.raise_for_status()
data = r.json()
print(data["total"], "NVDA rows")
for t in data["transactions"]:
    if t["kind"] != "transaction":
        continue
    print(t["tx_date"], t["code"], t["codeDescription"], t["owners"][0]["name"], t["shares"], t["price"], "10b5-1" if t["aff10b5one"] else "")

Output:

6965 NVDA rows
2026-10-07 G Bona fide gift COXE TENCH 500000.0 0.0 10b5-1
2026-09-21 S Open market or private sale Teter Timothy S. 13478.0 223.05 10b5-1
2026-09-21 S Open market or private sale Teter Timothy S. 12483.0 222.19 10b5-1
2026-09-21 S Open market or private sale Teter Timothy S. 4499.0 223.75 10b5-1
2026-09-18 S Open market or private sale STEVENS MARK A 1356000.0 219.72 
2026-09-18 S Open market or private sale STEVENS MARK A 10000.0 220.42 
2026-09-16 F Payment of exercise price or tax by delivering securities HUANG JEN HSUN 45728.0 212.17 
2026-09-16 F Payment of exercise price or tax by delivering securities Kress Colette 40747.0 212.17 10b5-1
2026-09-16 F Payment of exercise price or tax by delivering securities Teter Timothy S. 35738.0 212.17 
2026-09-16 F Payment of exercise price or tax by delivering securities Shoquist Debora 35008.0 212.17 
2026-09-17 S Open market or private sale Kress Colette 20369.0 219.02 10b5-1
2026-09-17 S Open market or private sale Kress Colette 9868.0 219.59 10b5-1
2026-09-17 S Open market or private sale Kress Colette 4681.0 218.01 10b5-1
2026-09-16 F Payment of exercise price or tax by delivering securities GAWEL SCOTT 2932.0 212.17 
2026-09-17 G Bona fide gift HUANG JEN HSUN 438000.0 0.0 
2026-09-09 A Grant or award Parker Nicholas P. 172507.0 0.0 
2026-09-04 S Open market or private sale STEVENS MARK A 237820.0 231.17 
2026-09-03 S Open market or private sale STEVENS MARK A 200000.0 229.87 
2026-09-04 S Open market or private sale STEVENS MARK A 197180.0 230.4 
2026-09-03 S Open market or private sale STEVENS MARK A 198707.0 227.7 
2026-09-04 S Open market or private sale STEVENS MARK A 182628.0 233.46 
2026-09-04 S Open market or private sale STEVENS MARK A 4611.0 234.0 
2026-09-03 S Open market or private sale STEVENS MARK A 1293.0 228.27 
2026-09-02 G Bona fide gift COXE TENCH 500000.0 0.0 10b5-1

Each row includes codeDescription, so you do not need a lookup table, and aff10b5one tells you whether the filer checked the Rule 10b5-1 trading plan box.

Notes

  • Purchases by 10% owners are often funds or corporate holders, not executives. Filter on isOfficer or isDirector if you only want management.
  • This is research data, not a recommendation to trade.

Next steps

More ownership & fund analysis tutorials

Run this tutorial with your own key

500 free API calls a month. Every example on this page works on the free plan.

Get free API key →