Skip to main content

tickerinside

TickerInside is an open, machine-readable database of US ETF holdings, with the source and date of each holding. This is the Python client for its free API, which serves the full holdings of US ETFs and of the largest US mutual funds (FXAIX, AGTHX, VFIAX), the funds that hold each stock, and the overlap and look-through computed from them. No API key, no account.

The funds come from two sources: the issuers' own holdings files, read every weekday night, and, for the other US stock ETFs, their latest SEC Form N-PORT filing, which is months older. This README gives no count, because it is published far less often than the data moves: the counts of the day are in the coverage object of https://tickerinside.com/api/v1/index.json, and ti.funds() lists the funds of both sources.

Install

pip install tickerinside

The package uses only the Python standard library and runs on Python 3.9 or later. There is no API key and no account to create. pandas is optional: install it (or pip install "tickerinside[pandas]") and every result also offers a .df DataFrame.

Examples

import tickerinside as ti

The holdings of VOO as a DataFrame, heaviest first:

voo = ti.holdings("VOO", names=True)
voo.holdings_as_of        # '2026-08-31', the date of the issuer's file
voo.df.head(3)
#   ticker        name  weight_pct
# 0   NVDA      Nvidia      8.0927
# 1   AAPL  Apple Inc.      7.0432
# 2   MSFT   Microsoft      5.7033

Which ETFs hold NVDA, and at what weight:

nvda = ti.holders("NVDA")
nvda.data["counts"]       # how many funds hold it, per source: {'issuer': ..., 'sec_nport': ...}
nvda.df[["fund", "weight_pct", "holdings_as_of"]].head(3)
#    fund  weight_pct holdings_as_of
# 0  GXPT     19.9100     2026-09-24
# 1   VGT     17.7869     2026-08-31
# 2  VUSG     16.9500     2026-09-25

Funds covered from their issuers' own files and funds covered from SEC Form N-PORT filings, which are months older, are kept apart in nvda.data["issuer"] and nvda.data["sec_nport"] (None only for a stock file that carries no list from SEC filings). .df lists the issuer rows first and the SEC rows after them, without merging the two by weight.

How much VOO and QQQ overlap:

c = ti.compare("VOO", "QQQ")
c.data["overlap"]["by_weight"]    # 53.9, the smaller weight of each shared company, summed
c.data["overlap"]["share_of_b"]   # 93.7, the part of QQQ held in companies VOO also holds
c.data["correlation_3y"]          # 0.95, from three years of weekly returns

What a small portfolio holds underneath, in percent and in money:

lt = ti.look_through({"VOO": 6000, "QQQ": 3000, "SCHD": 1000})
lt.df.head(2)
#   ticker  weight_pct   value  via_VOO  via_QQQ  via_SCHD
# 0   NVDA       7.312  731.24   4.8556   2.4568       0.0
# 1   AAPL       6.449  644.88   4.2259   2.2229       0.0

What changed in SPY's holdings from one issuer file to the next, since a date, one row per line that entered, exited or moved by 0.05 points or more:

ch = ti.changes("SPY", since="2026-09-18")
ch.df[["date", "previous", "change", "ticker", "weight_before", "weight_after"]].head(6)
#          date    previous   change ticker  weight_before  weight_after
# 0  2026-09-18  2026-09-17  entered     BE            NaN        0.1187
# 1  2026-09-18  2026-09-17  entered   ILMN            NaN        0.0549
# 2  2026-09-18  2026-09-17  entered      P            NaN        0.0525
# 3  2026-09-18  2026-09-17   exited    TAP         0.0095           NaN
# 4  2026-09-18  2026-09-17   exited   BLDR         0.0094           NaN
# 5  2026-09-18  2026-09-17   exited    TTD         0.0093           NaN

ti.history("SPY") gives the whole file: each file date with its position count and top 10 weight, each step with its turnover measures, and how the fund's overlap with its ten closest funds moved. A step that is a change of the keys or of the reading of the files rather than a trade is marked restated. The history covers the funds read from their issuers' own files, from the first file the archive holds, and it is published under the TickerInside history terms rather than CC BY 4.0 (see below).

Citing a figure, with the line every result carries:

print(c.cite)
# TickerInside, VOO vs QQQ, holdings files of 2026-08-31 and 2026-09-24, https://tickerinside.com/compare/voo-vs-qqq/, CC BY 4.0
c.page_url                # the page where a reader can check it

The figures above are from the files of 25 September 2026 and change as issuers publish new ones.

Use cases

Each of these tasks has a page on tickerinside.com with a live example, the fields to read and the limits. The code below is the code of those pages, and it runs against each night's build of the site before it is published.

ETF holdings API: What does a US ETF hold, at what weight, and from which file?

import tickerinside as ti

voo = ti.holdings("VOO")
print(voo.holdings_as_of, voo.source)
for row in voo.data[:5]:
    print(row["ticker"], row["weight_pct"])

ETF overlap API: How much do two ETFs hold in common?

import tickerinside as ti

pair = ti.compare("VOO", "QQQ")
for fund, date in pair.holdings_as_of.items():
    print(fund, date)
o = pair.data["overlap"]
print(o["by_weight"], o["share_of_a"], o["share_of_b"], o["companies_in_both"])

Which ETFs hold a stock, by API: Which ETFs hold a given stock, and at what weight?

import tickerinside as ti

nvda = ti.holders("NVDA")
print(nvda.data["counts"]["issuer"], nvda.data["counts"]["sec_nport"])
for row in nvda.data["issuer"][:5]:
    print(row["fund"], row["weight_pct"], row["holdings_as_of"])

ETF holdings history API: How did an ETF's holdings change from one file to the next?

import tickerinside as ti

spy = ti.history("SPY").data
print(spy["first"], spy["last"], spy["file_dates"])
for step in spy["changes"][-5:]:
    for ticker, weight in step.get("entered", []):
        print(step["date"], "entered", ticker, weight)
    for ticker, weight in step.get("exited", []):
        print(step["date"], "exited", ticker, weight)

Portfolio exposure API: What does a portfolio of ETFs hold underneath, and where does its risk come from?

import tickerinside as ti

mix = ti.look_through({"VOO": 60, "QQQ": 40}, top=5)
for fund, date in mix.holdings_as_of.items():
    print(fund, date)
print(mix.data["distinct_companies"], mix.data["top_10_weight_pct"])
for company in mix.data["companies"]:
    print(company["ticker"], company["weight_pct"])

ETF screener API: Which ETFs hold a stock at a given weight, under a given cost? The client has no call for the screen, so this one reads the endpoint with the standard library.

import json
from urllib.request import urlopen

url = ("https://tickerinside.com/api/v1/screen"
       "?holds=NVDA&min_weight=10&expense_ratio_max=0.4&limit=5")
screen = json.load(urlopen(url))
print(screen["total"])
for f in screen["funds"]:
    print(f["ticker"], f["weight_pct"], f["holdings_as_of"], f["expense_ratio"])

Citing the data

citation() gives the credit of an answer in every form of the attribution kit (https://tickerinside.com/attribution/): plain text, Markdown, HTML, a Jupyter notebook cell, BibTeX, CSL-JSON and one sentence an assistant can append to an answer. Each form is the result's .cite line in another shape, with the date of the file it comes from and the address where it can be checked.

ti.citation("VOO").text             # a fund's file, or a stock's: ti.citation("NVDA")
# TickerInside, Vanguard S&P 500 ETF (VOO), holdings from the issuer's file of 2026-08-31, https://tickerinside.com/etf/voo/, CC BY 4.0
print(ti.citation("VOO", "bibtex"))
# @misc{tickerinside_etf_voo_20260831,
#   author       = {{TickerInside}},
#   title        = {{Vanguard S\&P 500 ETF (VOO), holdings from the issuer's file of 2026-08-31}},
#   year         = {2026},
#   howpublished = {\url{https://tickerinside.com/etf/voo/}},
#   note         = {Licence: CC BY 4.0}
# }
c.citation("markdown")              # any result, in any form
ti.citation()                       # the data as a whole, dated from index.json

The credit also travels with the tables themselves. A result's .df carries its provenance in df.attrs (source, cite, license, url and the dates), which pandas keeps through copies and writes into the Parquet files it saves. result.to_html() gives the table with the credit line under it, "Data: TickerInside (CC BY 4.0)" linked to the page or file the figures come from, ready for a page, a report or a dashboard, and result.credit_html() gives that line alone. In a Jupyter notebook, a result left as the last line of a cell shows its table with the same line under it, so a notebook shared as it is credits its data.

The forms are text, markdown, html, notebook, bibtex, csl_json and ai: ti.citation(x) returns a Citation with each of them as an attribute, and ti.citation(x, "bibtex") or result.citation("bibtex") one of them as a string. In a notebook, a Citation left as the last line of a cell shows as the linked line. ti.citation() also takes an API answer as served, a dict: a file with cite_as, or an answer the API computes when asked, with url= the address that was called, since such an answer carries no address of its own. A result of history() or changes() names the history terms in place of CC BY 4.0.

What each call returns

The functions are funds(), fund(ticker), holdings(ticker), holders(stock_ticker), security(ticker), compare(a, b), overlap_matrix(tickers), look_through(positions), census(), history(ticker) and changes(ticker, since=None). Each returns a result with these attributes:

  • .data: the answer as plain dicts and lists.
  • .df: the same answer as a pandas DataFrame, when pandas is installed.
  • .as_of: the date of the price data, and .holdings_as_of: the date of the holdings file, or a dict of dates by fund when the answer uses several funds.
  • .source: where the holdings come from, issuer for the issuer's own file or sec_nport for a fund covered from its latest SEC Form N-PORT filing, which is months older; sec_nport_bonds, sec_nport_multi_asset, sec_nport_derivatives, trust_filings, sponsor_files (a fund too new for a Form N-PORT, from its sponsor's own file) and prospectus (a fund with no holdings yet) for the layers beside them. Like .holdings_as_of, it is a dict by fund when the answer uses several funds.
  • .cite: a citation line naming the source, its date, a link and the licence.
  • .url: the API file the answer was read from, and .page_url: the page on tickerinside.com where it can be checked.

A fixed income, multi-asset, derivative-based, trust, pool, sponsor-file or prospectus fund has a file of its own under /etf/, and /fund/ answers for it too, in the shape of a stock fund's file: holdings_kind says what its holdings map holds and listed_pct how much of its net assets the map shows. fund() returns that answer; holdings(), compare(), overlap_matrix() and look_through() read stock holdings only, and say so for one of these funds (NotCovered, or left out and named with skip_missing=True) instead of measuring it as if it held shares. The MCP server's compare_etfs and get_etf_holdings answer for them.

Overlap, correlation and look-through are computed on your machine from the fund files, with the same arithmetic as the TickerInside website and MCP server, so the figures agree with theirs. A ticker that is not covered raises tickerinside.NotCovered with the API's own message, and a fund given to holders() or a stock given to fund() gets a message naming the call that fits it. A network failure raises tickerinside.APIError with the URL that failed, and so does a 404 that is not the API's own answer, which usually means a wrong base URL. Each file is kept in memory for an hour, so asking twice costs one request. ti.Client(base_url=..., timeout=..., cache_ttl=...) gives a session with other settings, and the TICKERINSIDE_API environment variable, when it is set, replaces the default base URL https://tickerinside.com/api/v1 (the TickerInside MCP server reads the same variable). Requests identify themselves with the User-Agent tickerinside-python/<version>, and nothing else is sent anywhere.

Licence and attribution

The code of this package is released under the MIT licence. The data it reads is published by TickerInside under the Creative Commons Attribution 4.0 licence (CC BY 4.0): you may use it, commercially as well, provided you credit TickerInside, with a link to the page the figure comes from wherever the medium allows a link, and by name where it does not. How you mark the link is up to you. The .cite line on every result gives that credit, and citation() gives it in every form of the attribution kit. The holdings history read by history() and changes() is the exception: it is published under the TickerInside history terms (https://tickerinside.com/legal/#history), free per request, commercial use included, with attribution, a link to the file and its date, and not for republishing the archive as a whole or extracting it systematically; its .cite line gives the link and the dates. The figures are measurements of what funds hold and how they have behaved, not investment advice.

The API reference, with every field and endpoint, is at https://tickerinside.com/api/, and the same reference as one text file for coding agents at https://tickerinside.com/llms-full.txt. In Claude, the TickerInside MCP server is available in Claude's connector directory at https://claude.ai/directory/tickerinside; any other MCP client, or Claude as a custom connector, takes https://tickerinside.com/mcp, and a client that runs local servers only npx -y tickerinside-mcp@latest.

Metadata

Release files for tickerinside 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tickerinside 0.5.0
File Size Uploaded
tickerinside-0.5.0.tar.gz 35.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tickerinside 0.5.0
File Interpreter ABI Platform
tickerinside-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 76.2 kB

Release files / tickerinside-0.5.0.tar.gz

Download URL tickerinside-0.5.0.tar.gz
Size 35.5 kB
Tags Source
SHA-256 checksum
How to use checksums
ee21dee1569b89755c22abadd20979b2545f8fb20f7de0a6c361f77a6d6a8433
BLAKE2b-256 checksum
How to use checksums
6750bd57b44e89a4584f3829ceeb488df70b94666d61999fe0808455769e576d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / tickerinside-0.5.0-py3-none-any.whl

Download URL tickerinside-0.5.0-py3-none-any.whl
Size 40.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
193bb1a10831ce95477c36c54f85e2ae481e08465e796ecd1b9186226bd5679a
BLAKE2b-256 checksum
How to use checksums
3c0f95eff58f0befefaca81c3eee1a3397a2c52ae6c3f83228b0f179837dc086
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.15

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page