Portfolio exposure API

View as Markdown What changed

What does a portfolio of ETFs hold underneath, and where does its risk come from? GET /api/v1/lookthrough?portfolio=VOO:60,QQQ:40 weighs each fund's holdings by its share of the portfolio and adds them up: every company with its percent of the portfolio, its value when amounts are given, and the funds it comes through. The MCP tool get_etf_portfolio_risk adds the portfolio's volatility and each fund's share of the risk, from up to three years of weekly returns.

Live example

The request: GET /api/v1/lookthrough?portfolio=VOO:60,QQQ:40&top=5

60% VOO and 40% QQQ on this build, from VOO's file of and QQQ's of : 518 companies underneath, the ten largest 41.1% of the portfolio. The five largest, with the part each fund adds:

Part of the answer, data through 2026-10-12
{
  "holdings_as_of": {"VOO": "2026-08-31", "QQQ": "2026-10-12"},
  "distinct_companies": 518,
  "top_10_weight_pct": 41.1,
  "companies": [
    {
      "ticker": "NVDA",
      "weight_pct": 8.136,
      "through": [
        {"fund": "VOO", "weight_pct": 4.8556},
        {"fund": "QQQ", "weight_pct": 3.2806}
      ]
    },
    {
      "ticker": "AAPL",
      "weight_pct": 7.219,
      "through": [
        {"fund": "VOO", "weight_pct": 4.2259},
        {"fund": "QQQ", "weight_pct": 2.993}
      ]
    },
    {
      "ticker": "MSFT",
      "weight_pct": 5.839,
      "through": [
        {"fund": "VOO", "weight_pct": 3.422},
        {"fund": "QQQ", "weight_pct": 2.4168}
      ]
    },
    {
      "ticker": "AMZN",
      "weight_pct": 3.993,
      "through": [
        {"fund": "VOO", "weight_pct": 2.3092},
        {"fund": "QQQ", "weight_pct": 1.6836}
      ]
    },
    {
      "ticker": "GOOGL",
      "weight_pct": 3.052,
      "through": [
        {"fund": "VOO", "weight_pct": 1.8069},
        {"fund": "QQQ", "weight_pct": 1.2448}
      ]
    }
  ]
}

Code

Each block below is a file that runs against each night's build of this site that has the files it reads, before it is published, through the same Worker that answers the API: the code shown is the code that ran. A program that calls the API on its own should name its project in its User-Agent; it is a request, not a condition.

cURL

Prints the whole answer as JSON; add | jq to pick fields.

Terminal
curl -s "https://tickerinside.com/api/v1/lookthrough?portfolio=VOO:60,QQQ:40&top=5"

Python

Needs Python 3.9 or later and the tickerinside client, pip install tickerinside, which uses only the standard library.

portfolio-exposure.py
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"])

JavaScript

Runs as a module in Node.js 20 or later (save it as a .mjs file), in Deno, or in a browser page: CORS is open.

portfolio-exposure.mjs
const url = "https://tickerinside.com/api/v1/lookthrough"
  + "?portfolio=VOO:60,QQQ:40&top=5";
const mix = await (await fetch(url)).json();
for (const [fund, date] of Object.entries(mix.holdings_as_of)) {
  console.log(fund, date);
}
console.log(mix.distinct_companies, mix.top_10_weight_pct);
for (const c of mix.companies) console.log(c.ticker, c.weight_pct);

Google Sheets

Paste it into a cell: Sheets fills the rows below with the CSV twin of the answer, the date on every row. Excel and Power BI read the same address with Power Query; the queries are on ETF holdings in Excel and Google Sheets.

Cell A1
=IMPORTDATA("https://tickerinside.com/api/v1/lookthrough.csv?portfolio=VOO:60,QQQ:40")

MCP tool, over HTTP

In Claude, ChatGPT, Cursor or VS Code, add the MCP server once (how to add it) and ask in words; the assistant makes this same call.

Terminal
curl -s https://tickerinside.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params":
      {"name": "get_etf_portfolio_risk", "arguments": {"positions": [
        {"ticker": "VOO", "amount": 60}, {"ticker": "QQQ", "amount": 40}]}}}'

Fields to read

  • companies: The companies underneath, largest first, each with weight_pct (its percent of the portfolio), value and through (the funds it comes through, with the part each adds).
  • distinct_companies: How many companies the portfolio holds through its funds.
  • top_10_weight_pct: The weight of the ten largest companies together, in percent.
  • holdings_as_of: The date of each fund's holdings file.
  • holdings_source: Where each fund's holdings come from.

Limits

  • One call takes 1 to 6 funds, whose files weigh at most 30 KB and hold at most 1,100 lines between them. The MCP tools take 1 to 60 funds.
  • The amounts are proportions: VOO:60,QQQ:40 and VOO:6000,QQQ:4000 give the same percentages, and without amounts every fund counts equally. top keeps the largest companies, 25 by default.
  • Funds whose holdings are not shares, such as bond funds, commodity pools and leveraged or inverse funds, are named in not_looked_through and left out of the percentages.
  • Risk needs a year of weekly prices for every fund in the portfolio.
  • /api/v1/lookthrough.csv gives one row per company, with the funds it comes through and their dates.

Reference