Build Your Own 13F Tracker: Free Automation with Python + SEC EDGAR

Want to know what Buffett added last quarter, or what Burry loaded up on? Most people reach for Whalewisdom (USD 300 per year, roughly USD 25 per month) or the free but slower-to-update Dataroma. But these sites all draw on the same source: SEC EDGAR, which is public, free, and has an API. This piece shows you how to pull straight from the source, and along the way spells out how to read 13F data correctly and where its limits lie.

What a 13F Is

Limits First, Method Second

A 13F has three structural limits you have to understand up front:

  1. 45-day lag: what you see is the position as of quarter end; the manager may already have reversed course by now.
  2. No shorts: hedge funds often run paired long/short trades, and the short leg never shows up on a 13F, so reading the long side alone can point you in the wrong direction. Purchased puts, by contrast, do show up (marked PUT): Burry's famous bearish bets were read straight out of the PUT column of his 13F.
  3. Position size carries different meaning: USD 10M is pocket change for Berkshire but a heavy bet for a small fund. Changes tell you more than absolute holdings, and portfolio weight tells you more than dollar amounts.

The Core Workflow: Four Steps

Step 1: Pull the filer's filing list by CIK

Every filer has a CIK number (Berkshire's is 0001067983). SEC's submissions API returns all of that filer's filings in a single call:

import requests

HEADERS = {"User-Agent": "YourName your@email.com"}  # SEC requires this
CIK = "0001067983"  # Berkshire Hathaway

url = f"https://data.sec.gov/submissions/CIK{CIK}.json"
data = requests.get(url, headers=HEADERS).json()

filings = data["filings"]["recent"]
idx = [i for i, f in enumerate(filings["form"]) if f == "13F-HR"]

Step 2: Parse the 13F XML holdings table

import xml.etree.ElementTree as ET

ns = {"ns": "http://www.sec.gov/edgar/document/thirteenf/informationtable"}
root = ET.fromstring(xml_text)

positions = []
for it in root.findall(".//ns:infoTable", ns):
    positions.append({
        "name":  it.find("ns:nameOfIssuer", ns).text,
        "cusip": it.find("ns:cusip", ns).text,
        "value": int(it.find("ns:value", ns).text),
        "shares": int(it.find("ns:shrsOrPrnAmt/ns:sshPrnamt", ns).text),
    })

Step 3: Map CUSIP to ticker

A 13F identifies securities by CUSIP, not ticker. Maintain a lookup table (or lean on SEC's company_tickers.json), and once you have built out the common large caps you can reuse it.

Step 4: Quarter-over-quarter diff

Align this quarter's and last quarter's holdings tables by CUSIP, compute the change in share counts, and you get four kinds of signal (added, trimmed, new position, exited), output sorted by the dollar value of the change.

The Four Pitfalls Beginners Hit Most

PitfallConsequenceFix
No User-Agent setSEC rejects the request outrightPut "name + email" in the header
Requests too fastExceeding 10 req/sec gets you temporarily blocked (SEC's official wording is a "brief period"; access resumes once you drop back below the limit)Add rate limiting: sleep 0.15 seconds before fetching the next one
value unit mix-upFilings before 2023 are denominated in thousands of USD, USD thereafter. Mixing them across years throws your numbers off by 1000xDetermine the unit from the filing period, then normalize
Comparing only the latest two quartersYou miss the accumulation trend (three consecutive quarters of adding is a stronger signal than a single quarter)Keep at least four to eight quarters of history

Scheduling the Automation

13F filings cluster in a flood between day 40 and day 45 after quarter end. My setup: Windows Task Scheduler runs the script once a week, switching to daily during the filing peak; output writes to Excel, and when a change clears a threshold it emails a notice to me. The variable cost of the whole system is zero; the only investment is the few hours it takes to build it the first time.

The next level up is wiring 13F signals into an AI research pipeline: when two or more of your tracked managers add the same stock in the same quarter, automatically trigger a deep read of that company's filings. I wrote up how that part works in the AI research workflow article (in Chinese).

Thoughts after reading?

Questions about this piece, or a different take? Email hello@taxcodeusstocks.com.

Sources

  • SEC EDGAR full-text search and submissions API (data.sec.gov)
  • SEC Form 13F official instructions and XML technical specification
  • The code is a simplified version of what the author actually runs, tested on Python 3.13
This article is educational content, not investment advice. 13F data lags up to 45 days and covers long positions only; do not blindly copy institutional holdings. Full disclaimer here.