Sometimes you do not want a pipeline. You want Berkshire's latest portfolio in a spreadsheet, in the next thirty seconds.
This is the shortest useful thing you can do with the API: one call to find the filing, one to stream its holdings, and a CSV on disk.
Everything below runs on the free Community tier: every dataset, 10,000 requests per day (600 per minute), no credit card.
Setup
pip install py3spread
export THREESPREAD_API_KEY="sk_live_..."The whole thing
import csv
import itertools
from decimal import Decimal
from py3spread import Client
MANAGER_CIK = "1067983" # Berkshire Hathaway
OUT = "holdings.csv"
with Client() as client:
filings = client.institutional_holdings.list(filing_manager_cik=MANAGER_CIK, limit=1)["data"]
if not filings:
print(f"no 13F filings for cik {MANAGER_CIK}")
raise SystemExit
filing = filings[0]
print(f"{filing['filer_name']}, period {filing['period_of_report']}")
rows = itertools.islice(
client.institutional_holdings.iter_holdings(filing_id=filing["filing_id"]),
5000,
)
fields = ["name_of_issuer", "title_of_class", "cusip", "ssh_prnamt", "ssh_prnamt_type", "value_usd"]
count = 0
total = Decimal(0)
with open(OUT, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
writer.writeheader()
for row in rows:
writer.writerow(row)
total += Decimal(row["value_usd"])
count += 1
print(f"wrote {count} holdings (${total:,.0f} reported value) to {OUT}")That is it. Open holdings.csv in anything.
The parts that are load-bearing
limit=1 gets you the latest filing. Filings come back newest first, so you
do not need to sort or filter on a date to find the current one. Then
filing_id is what ties the filing to its positions: the list endpoint gives you
filings, iter_holdings(filing_id=...) gives you the rows inside one.
Decimal, not float. The API returns dollar values as strings at full
precision, deliberately. If you parse them as floats to sum them, you are
introducing rounding error into a number that was exact when it arrived. On a
portfolio the size of Berkshire's, that shows up in the total.
extrasaction="ignore". The API rows carry more fields than the six in the
CSV. Without this, DictWriter raises on the first row it sees. With it, you
get exactly the columns you asked for, and adding one is a matter of putting its
name in fields.
One note on the terms
This exports a single filing for your own analysis, which is exactly what the Community tier is for. It is not a license to mirror the dataset: the terms rule out bulk downloads and systematically copying or storing substantial portions of it, and the Community tier is for personal, non-commercial research. Analysis you build on top of the data is yours. The data itself is not.
If your use case genuinely needs a local copy of a lot of it, that is a conversation worth having, and sales is where it starts.
Where to take it
Change MANAGER_CIK and you have any manager's book. You can find the CIK
through the manager index, which is what client.institutional_holdings exposes
alongside the filings themselves.
Export two consecutive quarters for the same manager and diff them in a
spreadsheet, and you have their trades: what they opened, exited, added to and
trimmed. That is the whole "what did the big funds buy" exercise, and it is now
a VLOOKUP.
The dataset
Full endpoint reference and response schemas are on the 13F institutional holdings dataset page.