?>
ESC to close

ETF Holdings History API

Maintained by Piyush Arora

Last updated:

Return one row per holding with its portfolio weight in every reporting period the fund has filed — the pivot a position-over-time table needs, assembled server-side instead of in your client. Positions are keyed on a stable security identity rather than the instrument name, which matters because filers rewrite names between quarters: ARK's 58 distinct holdings across six periods would scatter into far more rows if keyed on the text. Holdings the fund has since exited are still returned, at the bottom, because what a manager sold is half the reason to read a table like this. The API is free to use.

Use this endpoint to:

Chart A Position Over Time Find Opens, Adds, Trims And Exits Skip Client-Side Pivoting
GET https://data.businessquant.com/funds/holdings/matrix?ticker={ticker}&limit={limit}&api_key={api_key}

Related endpoints: For a single period in full position-level detail, use the Fund Holdings API. For allocation over time by bucket rather than by holding, use the Portfolio Exposure API with periods. The Reporting Periods API lists the periods that become the keys of the weights object, and the Fund Overview API carries fees and objective.

1. Request Parameters

One identifier plus optional paging. There is no period argument, because the point of this endpoint is that every period arrives at once.

ParameterDescription
api_key
Required
Your unique API key for authentication.
ticker
Conditional Identifier
The fund or ETF ticker. A ticker identifies a share class, not a fund — VOO and VFIAX are two classes of the same portfolio, so the fees you get back are the ones that class charges. Either ticker or seriesid is required.
Example: ticker=VOO
seriesid
Conditional Identifier
The SEC Series ID of the fund. Resolves to the fund's lowest-cost share class. Use this when you want the fund rather than a particular class, or for funds that have no ticker of their own.
Example: seriesid=S000002839
page
Optional
Page number, 1-based. Default 1.
Example: page=2
limit
Optional
Holdings per page. Range 1–5000, default 100. Note this pages holdings, not holding-periods — each row already carries every period.
Example: limit=500

Rows are ordered by current weight, not by history. The largest position the fund holds today sorts first. A holding that has been fully exited has a current weight of zero and sorts to the bottom, ranked among its peers by the largest weight it ever reached — so the tail of the table is the fund's disposal history, in order of how big those positions once were.

What identifies a holding across quarters. Not the name. Measured on IVV: 549 distinct securities produce 1,278 distinct instrument_name strings across the filing history, because filers rewrite them — “NVIDIA”, “NVIDIA Corp.”, “NVIDIA CORPORATION” — from one quarter to the next. Grouping on the name would scatter one position across three rows. Rows here are grouped on a stable security identity, and the label you get back is the most recent name the filer used, because that is the one a reader will recognise. The identity key itself is never returned.

Example cURL Request
curl -X GET "https://data.businessquant.com/funds/holdings/matrix?ticker=ARKK&limit=8&api_key=YOUR_API_KEY"
Sample Response (ARKK, limit=8)
{
    "metadata": {
        "ticker": "ARKK",
        "seriesid": "S000042977",
        "classid": "C000133121",
        "cik": 1579982,
        "fund_name": "ARK Innovation ETF",
        "class_name": "ARK Innovation ETF",
        "gross_expense_ratio_pct": 0.75,
        "net_expense_ratio_pct": 0.75,
        "net_assets_usd": 6482466914.09,
        "net_assets_asof": "2026-04-30",
        "periods": [
            "2026-04-30",
            "2026-01-30",
            "2025-10-31",
            "2025-07-31",
            "2025-04-30",
            "2025-01-31"
        ],
        "period_count": 6,
        "period_latest": "2026-04-30",
        "period_earliest": "2025-01-31",
        "holdings_count": 58,
        "returned": 8,
        "pagination": {
            "current_page": 1,
            "limit": 8,
            "total_records": 58,
            "total_pages": 8
        }
    },
    "data": [
        {
            "instrument_name": "TESLA INC",
            "instrument_ticker": "TSLA",
            "issuer_name": "TESLA INC COM",
            "sector": "Consumer Discretionary",
            "industry": "Auto Manufacturers",
            "country_name": "United States",
            "asset_class": "Equity",
            "periods_held": 6,
            "weight_latest_pct": 9.7352,
            "weight_max_pct": 13.4234,
            "weights": {
                "2025-01-31": 13.4234,
                "2025-04-30": 11.9455,
                "2025-07-31": 9.814,
                "2025-10-31": 12.3106,
                "2026-01-30": 10.8803,
                "2026-04-30": 9.7352
            }
        },
        {
            "instrument_name": "TEMPUS AI INC",
            "instrument_ticker": "TEM",
            "issuer_name": "TEMPUS AI INC CL A",
            "sector": "Healthcare",
            "industry": "Diagnostic Labs",
            "country_name": "United States",
            "asset_class": "Equity",
            "periods_held": 6,
            "weight_latest_pct": 5.3555,
            "weight_max_pct": 5.3555,
            "weights": {
                "2025-01-31": 3.6009,
                "2025-04-30": 5.0923,
                "2025-07-31": 4.2153,
                "2025-10-31": 5.0353,
                "2026-01-30": 4.872,
                "2026-04-30": 5.3555
            }
        },
        {
            "instrument_name": "ADVANCED MICRO DEVICES INC",
            "instrument_ticker": "AMD",
            "issuer_name": "ADVANCED MICRO DEVICES INC COM",
            "sector": "Technology",
            "industry": "Semiconductor Design",
            "country_name": "United States",
            "asset_class": "Equity",
            "periods_held": 6,
            "weight_latest_pct": 5.1839,
            "weight_max_pct": 5.1839,
            "weights": {
                "2025-01-31": 1.2131,
                "2025-04-30": 1.6782,
                "2025-07-31": 3.5779,
                "2025-10-31": 4.2662,
                "2026-01-30": 3.9753,
                "2026-04-30": 5.1839
            }
        },
        {
            "instrument_name": "CRISPR THERAPEUTICS AG",
            "instrument_ticker": "CRSP",
            "issuer_name": "CRISPR THERAPEUTICS AG NAMEN AKT",
            "sector": "Healthcare",
            "industry": "Gene and Cell Therapy",
            "country_name": "Switzerland",
            "asset_class": "Equity",
            "periods_held": 6,
            "weight_latest_pct": 4.9803,
            "weight_max_pct": 5.4807,
            "weights": {
                "2025-01-31": 4.1767,
                "2025-04-30": 4.8185,
                "2025-07-31": 5.3379,
                "2025-10-31": 5.0598,
                "2026-01-30": 5.4807,
                "2026-04-30": 4.9803
            }
        },
        {
            "instrument_name": "SHOPIFY INC",
            "instrument_ticker": "SHOP",
            "issuer_name": "SHOPIFY INC CL A SUB VTG SHS",
            "sector": "Technology",
            "industry": "E-commerce Software",
            "country_name": "Canada",
            "asset_class": "Equity",
            "periods_held": 6,
            "weight_latest_pct": 4.3743,
            "weight_max_pct": 4.742,
            "weights": {
                "2025-01-31": 4.3086,
                "2025-04-30": 4.5018,
                "2025-07-31": 4.675,
                "2025-10-31": 4.742,
                "2026-01-30": 4.245,
                "2026-04-30": 4.3743
            }
        },
        {
            "instrument_name": "ROKU INC",
            "instrument_ticker": "ROKU",
            "issuer_name": "ROKU INC COM CL A",
            "sector": "Technology",
            "industry": "Operating Systems",
            "country_name": "United States",
            "asset_class": "Equity",
            "periods_held": 6,
            "weight_latest_pct": 4.3298,
            "weight_max_pct": 9.1137,
            "weights": {
                "2025-01-31": 9.1137,
                "2025-04-30": 7.7826,
                "2025-07-31": 6.9316,
                "2025-10-31": 5.7217,
                "2026-01-30": 5.0014,
                "2026-04-30": 4.3298
            }
        },
        {
            "instrument_name": "ROBINHOOD MARKETS INC",
            "instrument_ticker": "HOOD",
            "issuer_name": "ROBINHOOD MARKETS INC COM CL A",
            "sector": "Financial Services",
            "industry": "Retail Brokerage",
            "country_name": "United States",
            "asset_class": "Equity",
            "periods_held": 6,
            "weight_latest_pct": 4.3018,
            "weight_max_pct": 5.1095,
            "weights": {
                "2025-01-31": 5.1095,
                "2025-04-30": 4.7845,
                "2025-07-31": 4.1809,
                "2025-10-31": 4.7924,
                "2026-01-30": 3.9982,
                "2026-04-30": 4.3018
            }
        },
        {
            "instrument_name": "COINBASE GLOBAL INC",
            "instrument_ticker": "COIN",
            "issuer_name": "COINBASE GLOBAL INC COM CL A",
            "sector": "Technology",
            "industry": "Blockchain Infrastructure",
            "country_name": "United States",
            "asset_class": "Equity",
            "periods_held": 6,
            "weight_latest_pct": 4.2318,
            "weight_max_pct": 8.4906,
            "weights": {
                "2025-01-31": 8.4906,
                "2025-04-30": 7.8933,
                "2025-07-31": 7.2271,
                "2025-10-31": 5.8517,
                "2026-01-30": 4.6229,
                "2026-04-30": 4.2318
            }
        }
    ]
}

2. Response Fields

The row shape here differs from every other endpoint in this group: weights is an object keyed by reporting period, not a scalar. That is what makes one row a complete time series.

metadata

FieldTypeDescription
ticker, seriesid, classid, cikstringFund and share-class identity.
fund_name, class_namestringRegistered fund name and resolved share class.
net_assets_usd, net_assets_asoffloat, dateFund net assets and the date reported.
periodsarrayEvery reporting period on file, newest first. These are exactly the keys that appear in each row's weights object.
period_count, period_latest, period_earliestinteger, date, dateSize and bounds of the period list.
holdings_countintegerDistinct securities the fund has held across the entire window, before paging.
returnedintegerRows in this page of data.
paginationobjectcurrent_page, limit, total_records, total_pages.

data — one row per holding

FieldTypeDescription
instrument_namestringName of the security, taken from the most recent period it appears in.
instrument_tickerstringExchange ticker where one exists. null for bonds and derivatives.
issuer_namestringName of the issuing entity.
sectorstringSector classification of the issuer.
industrystringIndustry classification, one level below sector.
country_namestringCountry of the issuer.
asset_classstringEquity, Fixed Income, Derivative, Fund & Cash Equivalents or Other.
periods_heldintegerHow many of the fund's reporting periods this security appears in. Equal to period_count means held throughout.
weight_latest_pctfloatWeight in the most recent period. 0 for a position the fund has exited. Rows are sorted on this, descending.
weight_max_pctfloatThe largest weight this position ever reached across the window.
weightsobjectWeight per reporting period, keyed by date — {"2026-04-30": 9.7352, …}. Periods where the security was not held are absent from the object rather than present as zero.

Absent means not held; it does not mean zero. A period missing from a row's weights object is a period in which the fund did not hold that security at all. Rendering it as 0 is usually the right visual choice, but keep the distinction if you are computing anything — an average weight over held periods and an average over all periods are different numbers, and periods_held is the divisor for the first.

3. Response Viewer

A real response for ticker=ARKK&limit=8. Each row is one holding and each dated column is a key from that row's weights object. Colour marks the direction of change against the prior period and a dash marks a period the security was not held — both computed in this page, not returned by the API.

API Response — Holdings Over Time

ARK Innovation ETF  ·  ticker=ARKK&limit=8

Fund ARK Innovation ETF
Ticker ARKK
Distinct Holdings 58
Periods 6
Window 2025-01-31 → 2026-04-30
Holding Ticker Sector Held 2025-01-31 2025-04-30 2025-07-31 2025-10-31 2026-01-30 2026-04-30
TESLA INC TSLA Consumer Discretionary 6/6 13.42% 11.95% 9.81% 12.31% 10.88% 9.74%
TEMPUS AI INC TEM Healthcare 6/6 3.60% 5.09% 4.22% 5.04% 4.87% 5.36%
ADVANCED MICRO DEVICES INC AMD Technology 6/6 1.21% 1.68% 3.58% 4.27% 3.98% 5.18%
CRISPR THERAPEUTICS AG CRSP Healthcare 6/6 4.18% 4.82% 5.34% 5.06% 5.48% 4.98%
SHOPIFY INC SHOP Technology 6/6 4.31% 4.50% 4.68% 4.74% 4.25% 4.37%
ROKU INC ROKU Technology 6/6 9.11% 7.78% 6.93% 5.72% 5.00% 4.33%
ROBINHOOD MARKETS INC HOOD Financial Services 6/6 5.11% 4.78% 4.18% 4.79% 4.00% 4.30%
COINBASE GLOBAL INC COIN Technology 6/6 8.49% 7.89% 7.23% 5.85% 4.62% 4.23%

4. More Request Examples

One call returns the whole grid; paging walks the holdings axis only.

Every holding an active ETF has ever reported
https://data.businessquant.com/funds/holdings/matrix?ticker=ARKK&limit=500&api_key=YOUR_API_KEY
Top 25 current positions of an index fund, with full history
https://data.businessquant.com/funds/holdings/matrix?ticker=VOO&limit=25&api_key=YOUR_API_KEY
By Series ID
https://data.businessquant.com/funds/holdings/matrix?seriesid=S000042977&limit=100&api_key=YOUR_API_KEY
Second page of a large portfolio
https://data.businessquant.com/funds/holdings/matrix?ticker=IVV&limit=200&page=2&api_key=YOUR_API_KEY

5. Use Cases

Everything below would otherwise require one request per period plus a client-side join.

Position Weight Sparklines

Each row's weights object is already a time series keyed by date — feed it straight to a sparkline beside the holding name with no reshaping.

Conviction Screening

Compare weight_latest_pct against weight_max_pct. A position at a fraction of its peak is being wound down; one at its peak is being built. periods_held separates a long-held core position from a recent addition.

Exit Analysis

Rows with weight_latest_pct of zero are positions the fund has fully exited. Combine the exit period with prices from the Stock Quotes API to measure what the manager's sell decisions were actually worth.

Turnover Attribution

Aggregate per-period weight changes across all rows to compute realised turnover from filings, rather than relying on the single annual figure a fund reports in its prospectus.

Manager Consistency Scoring

The ratio of holdings with periods_held equal to period_count against total holdings is a direct measure of how much of a portfolio is genuinely long-term.

Backtest Input Assembly

One request produces the full holdings panel for a fund. Combine with the Stock Quotes API to reconstruct what the fund's portfolio would have returned under different weighting rules.

Frequently Asked Questions

The questions developers ask most often about this endpoint.

Why does a period sometimes not appear in a row's weights object?

Because the fund did not hold that security in that period. Absent means not held — it is not the same as a zero weight, and the API does not pad the object. Render it as a gap or a zero as your chart requires, but use periods_held rather than the count of periods when you average.

How are holdings matched across quarters when the name changes?

On a stable security identity, not on the name. Filers rewrite instrument names between filings — measured on IVV, 549 distinct securities generate 1,278 distinct name strings — so name-based grouping would split one position across several rows. The label returned is the most recent name the filer used, because that is the recognisable one; the underlying key is never exposed in the response.

What is the difference between this and calling /funds/holdings once per period?

Result shape and request count. Calling the holdings endpoint per period returns one row per (holding, period) and leaves you to pivot several thousand rows in the client to render a few hundred. This endpoint does that pivot server-side and returns it in a single request, with holdings the fund has exited already included — which a per-period loop would silently drop unless you unioned every period yourself.

Why is a position I know the fund holds showing a weight of zero in the latest period?

A weight_latest_pct of zero means the security is absent from the most recent period the fund filed — check metadata.period_latest, which may predate the trade you are thinking of. N-PORT is filed periodically, not continuously, so the most recent filed portfolio can be several weeks behind the live one.

Does limit control periods or holdings?

Holdings. Every row already contains every period, so limit and page walk the security axis only. metadata.holdings_count is the total number of distinct securities the fund has held across the whole window, which is what pagination is counting.

Is the ETF Holdings History API free to use?

Yes, the API is free to use. Sign up for an API key and start pulling per-holding weight histories immediately — no credit card required.