Home / Guides / Ownership & holdings
Ownership & holdings

List an ETF's holdings from Form N-PORT

Read a fund's Form N-PORT from SecFeed, list its largest holdings with shares, value and weight, and break the portfolio down by asset category.

Registered funds, ETFs included, file Form N-PORT every month with a complete schedule of investments. The SEC makes the report for the third month of each fiscal quarter public 60 days after the period ends, so N-PORT is the authoritative public source for what a fund held at quarter end: every security, with identifiers, quantity, fair value, percentage of net assets and asset category.

This tutorial lists the holdings of one ETF series from its N-PORT filing.

Fetch the filing

GET /v1/n-port returns N-PORT filings parsed to JSON. Filter by cik (the registrant, usually a trust that holds many series) and a date range. Each filing covers one series, named in genInfo.seriesName, so pick the series you want from the trust's filings.

This example uses Running Oak Efficient Growth ETF, a series of Strategic Trust (CIK 1873280).

import os
from collections import Counter
import requests

BASE = "https://secfeedapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"}
CIK, SERIES = 1873280, "Running Oak Efficient Growth ETF"

r = requests.get(f"{BASE}/v1/n-port", params={"cik": CIK, "startDate": "2026-09-01"}, headers=HEADERS, timeout=120)
r.raise_for_status()
filing = next(f for f in r.json()["filings"] if f["data"]["primary_doc"]["formData"]["genInfo"]["seriesName"] == SERIES)
form = filing["data"]["primary_doc"]["formData"]
gen, fund = form["genInfo"], form["fundInfo"]
holdings = form["invstOrSecs"]["invstOrSec"]
if isinstance(holdings, dict):
    holdings = [holdings]

print(f"{gen['seriesName']} ({gen['seriesId']}), filed {filing['filedAt']}")
print(f"Period end {gen['repPdDate']} | net assets ${float(fund['netAssets']):,.0f} | holdings {len(holdings)}")
print()
print(f"{'Holding':<34}{'CUSIP':<11}{'Shares':>12}{'Value (USD)':>16}{'Weight':>8}")
for h in sorted(holdings, key=lambda h: -float(h["pctVal"]))[:10]:
    print(f"{h['name'][:33]:<34}{h['cusip']:<11}{float(h['balance']):>12,.0f}{float(h['valUSD']):>16,.0f}{float(h['pctVal']) / 100:>8.2%}")

cats = Counter()
for h in holdings:
    cats[h.get("assetCat") or "other"] += float(h["pctVal"])
print()
print("Weight by asset category:", {k: f"{v:.1f}%" for k, v in cats.most_common()})
print(f"Top 10 weight: {sum(sorted((float(h['pctVal']) for h in holdings), reverse=True)[:10]):.1f}%")

Output:

Running Oak Efficient Growth ETF (S000080468), filed 2026-10-09
Period end 2026-08-31 | net assets $415,687,534 | holdings 59

Holding                           CUSIP            Shares     Value (USD)  Weight
SERVICENOW INC                    81762P102        72,594      10,743,186   2.58%
ZEBRA TECHNOLOGIES CORP           989207105        29,488      10,425,482   2.51%
IQVIA HOLDINGS INC                46266C105        36,690       9,583,061   2.31%
FACTSET RESEARCH SYSTEMS INC      303075105        29,160       9,143,118   2.20%
AUTOMATIC DATA PROCESSING INC     053015103        30,897       8,841,486   2.13%
CACI INTERNATIONAL INC            127190304        13,937       8,682,890   2.09%
ARTHUR J. GALLAGHER & COMPANY     363576109        32,796       8,597,471   2.07%
STATE STREET INSTITUTIONAL INVEST 857492888     8,474,306       8,474,306   2.04%
BROWN & BROWN INC                 115236101       115,656       8,346,894   2.01%
JACOBS SOLUTIONS INC              46982L108        55,152       8,220,406   1.98%

Weight by asset category: {'EC': '97.9%', 'STIV': '2.0%'}
Top 10 weight: 21.9%

Where the fields come from

FieldN-PORT itemMeaning
name, cusipC.1Issuer name and CUSIP of each holding
balance, unitsC.2Quantity held and its unit (NS = shares, PA = principal amount)
valUSDC.2Fair value in US dollars
pctValC.2Value as a percent of net assets (2.58 means 2.58%)
assetCatC.4Asset category: EC equity common, STIV short-term investment vehicle, DBT debt and so on
fundInfo.netAssetsB.1Net assets of the series

pctVal is already a percentage, so the script divides by 100 before formatting it with %. The 2.0% in STIV is the fund's cash sweep, a money market fund position.

Compare snapshots

Each quarterly report gives you a snapshot. Fetch the same series a quarter earlier and join on CUSIP to see positions added, removed and resized. Because ETFs publish holdings daily on their own sites, N-PORT is most useful as the official, comparable record across many funds: you can run the same code over every series of a trust, or over every ETF in a date range.

A filing can hold a single holding as an object rather than a list; the script normalizes that case.

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 →