Home / Guides / Ownership & holdings
Ownership & holdings

Who owns a stock: 13F holders by CUSIP

List every institutional manager that reported a stock on Form 13F for a quarter, clean the obvious filer errors, and rank holders by shares.

Institutional managers with more than $100 million in 13(f) securities file Form 13F every quarter, listing each position by CUSIP. Flip the data around, from manager to security, and you get the institutional holder list for any stock.

This tutorial builds the holder list for NVIDIA common stock (CUSIP 67066G104) for the quarter ended June 30, 2026.

Find the CUSIP

If you start from a ticker, map it first. GET /v1/mapping/cusip?cusip=... goes the other way (CUSIP to issuer and ticker), and the 13F holdings rows themselves carry the issuer name, so you can confirm you have the right security in the first result.

Fetch every holding of the CUSIP

GET /v1/form-13f/holdings with cusip and period returns the rows sorted by value, up to 1,000 per page. Page with from until a page comes back short.

import os
from collections import defaultdict
import requests

BASE = "https://secfeedapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"}
CUSIP, PERIOD = "67066G104", "06-30-2026"  # NVIDIA common stock, Q2 2026 reports


def holders(cusip, period):
    rows, offset = [], 0
    while True:
        r = requests.get(f"{BASE}/v1/form-13f/holdings", headers=HEADERS, timeout=120,
                         params={"cusip": cusip, "period": period, "from": offset, "size": 1000})
        r.raise_for_status()
        page = r.json()
        rows += page
        if len(page) < 1000:
            return rows
        offset += 1000


rows = holders(CUSIP, PERIOD)

# Sanity check: value / shares is the implied price. 13F rows are typed by hand, and some filers report value in
# thousands or swap the columns. Keep rows whose implied price is within 2x of the median.
prices = sorted(h["value"] / h["shares"] for h in rows if h["shares"] and not h["put_call"])
median = prices[len(prices) // 2]
bad = [h for h in rows if h["shares"] and not h["put_call"] and not (median / 2 <= h["value"] / h["shares"] <= median * 2)]
print(f"Median implied price ${median:,.2f}; rows outside 0.5x to 2x of it: {len(bad)}")
for h in sorted(bad, key=lambda h: -h["shares"])[:3]:
    print(f"  {h['filer'][:40]:<40} shares {h['shares']:>16,.0f}  value {h['value']:>18,.0f}")
bad_ids = {(h["accession"], h["seq"]) for h in bad}
rows = [h for h in rows if (h["accession"], h["seq"]) not in bad_ids]
print()

by_filer = defaultdict(lambda: {"shares": 0.0, "value": 0.0, "options": 0})
for h in rows:
    f = by_filer[h["filer"]]
    if h["put_call"]:
        f["options"] += 1
        continue  # puts and calls are reported on the underlying shares, keep them out of the share totals
    f["shares"] += h["shares"]
    f["value"] += h["value"]

total_shares = sum(f["shares"] for f in by_filer.values())
print(f"{rows[0]['issuer']} ({CUSIP}), period {PERIOD}")
print(f"Holdings rows: {len(rows):,} | managers: {len(by_filer):,} | shares held: {total_shares:,.0f}")
print(f"Managers that also report puts or calls: {sum(1 for f in by_filer.values() if f['options']):,}")
print(f"\n{'Manager':<40}{'Shares':>16}{'Value':>20}{'Share of 13F total':>20}")
for name, f in sorted(by_filer.items(), key=lambda x: -x[1]["shares"])[:10]:
    print(f"{name[:39]:<40}{f['shares']:>16,.0f}{f['value']:>20,.0f}{f['shares'] / total_shares:>20.1%}")

Output:

Median implied price $200.09; rows outside 0.5x to 2x of it: 798
  CALIFORNIA STATE TEACHERS RETIREMENT SYS shares    7,007,449,934  value         35,021,490
  PRICE T ROWE ASSOCIATES INC /MD/         shares      334,210,126  value         66,872,105
  SG Americas Securities, LLC              shares       62,298,007  value         12,465,208

NVIDIA CORPORATION (67066G104), period 06-30-2026
Holdings rows: 9,386 | managers: 5,760 | shares held: 17,367,848,482
Managers that also report puts or calls: 344

Manager                                           Shares               Value  Share of 13F total
STATE STREET CORP                          2,019,566,874     404,095,135,818               11.6%
BlackRock, Inc.                            1,941,918,386     388,558,449,933               11.2%
VANGUARD CAPITAL MANAGEMENT LLC            1,540,816,358     308,301,945,072                8.9%
FMR LLC                                    1,026,051,548     205,302,654,261                5.9%
GEODE CAPITAL MANAGEMENT, LLC                606,216,492     120,957,505,504                3.5%
VANGUARD PORTFOLIO MANAGEMENT LLC            517,816,949     103,609,993,325                3.0%
JPMORGAN CHASE & CO                          449,404,578      87,620,410,963                2.6%
MORGAN STANLEY                               356,420,089      71,316,096,193                2.1%
Invesco Ltd.                                 329,593,488      65,948,361,031                1.9%
NORGES BANK                                  325,951,734      65,219,682,456                1.9%

Why the sanity check matters

13F information tables are prepared by each manager, and errors are common in a dataset this size. The script computes each row's implied price (value divided by shares) and drops rows that are more than a factor of two away from the median. For this quarter that removed 798 of 10,184 rows. About two thirds of them (508) have an implied price close to one thousandth of the real one, the signature of value entered in thousands of dollars, the format the SEC used before 2023. The largest are worse: one row reports more than 7 billion shares, and another reports a position worth about 20 cents a share.

Without the check, those rows would put a pension fund at the top of the holder list with 28% of all reported shares. Always validate before you rank.

What the numbers mean

  • Managers counts distinct 13F filers. Large firms often file more than one 13F (for example separate Vanguard entities) and report a position in several rows, so the script sums rows per filer.
  • Shares held is the total across 13F filers, not the share of shares outstanding. Divide by the issuer's shares outstanding from /v1/shares-outstanding to estimate institutional ownership.
  • Puts and calls are reported on the underlying shares. They are excluded from the share totals and counted separately.

Compare quarters

Run the same request with the previous period (for example 03-31-2026) and join on filer to see who added, trimmed or exited. 13F reports are due 45 days after quarter end, so a quarter's list is complete by mid February, May, August or November.

Next steps

More ownership & holdings guides

Run this guide with your own key

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

Get free API key →