Unusual Whales API - ETF endpoints
Layer mapping: L1 (0DTE) load-bearing. SPY is an ETF. The ETF endpoints describe SPY directly: its holdings (the top-weight names that drive intraday SPY moves), its sector + country composition, its in/outflow tape, and its fund-level metadata. For L2/L3, the same endpoints describe other ETFs the strategy might trade.
Five endpoints, all path-scoped by ETF ticker.
1. Base contract
| Property | Value |
|---|---|
| Base URL | https://api.unusualwhales.com |
| Auth | Authorization: Bearer <token> header |
| Response envelope | { "data": ... } |
| Status codes | 200, 422, 500 |
| Rate limits | Not specified |
2. Endpoint matrix
| # | Path | Purpose | L1 use |
|---|---|---|---|
| 1 | GET /api/etfs/{ticker}/info | Fund metadata (AUM, expense ratio, holdings count, options volume) | Once-per-day context |
| 2 | GET /api/etfs/{ticker}/holdings | Per-constituent holdings list with pricing + options flow | Daily; identify top-25 SPY weights |
| 3 | GET /api/etfs/{ticker}/weights | Sector + country weight breakdown | Daily; sector regime overlay |
| 4 | GET /api/etfs/{ticker}/in-outflow | Daily in/outflow time series with FOMC flag | Per-day flow tape for SPY |
| 5 | GET /api/etfs/{ticker}/exposure | Which ETFs hold ticker X (inverse lookup) | L2 mostly - “which ETFs own AAPL” |
Key insight. Endpoints 1-4 use the path ticker as the ETF
itself (SPY). Endpoint 5 inverts: the path ticker is the
holding (e.g. AAPL), and the response lists ETFs holding it.
3. GET /api/etfs/{ticker}/info
Fund-level metadata.
Path: ticker = ETF symbol (e.g. SPY).
Query: none documented.
Response fields:
| Field | Type | Description |
|---|---|---|
name | string | Full ETF name |
etf_company | string | Issuer (e.g. SPDR / State Street) |
description | string | Prospectus summary |
aum | string | Assets under management |
expense_ratio | string | Annual expense ratio (%) |
inception_date | ISO date | Launch date |
domicile | string | Country of domicile |
website | string | Issuer URL |
holdings_count | int | Number of constituents |
avg30_volume | string | 30-day average daily share volume |
stock_vol | int | Today’s stock volume |
has_options | bool | Options trade |
opt_vol | int | Today’s total options volume |
call_vol | int | Today’s call volume |
put_vol | int | Today’s put volume |
MK3 use: session-context. Pull once at session start. The
put_vol / call_vol ratio is a real-time put-call indicator at the
ETF level (different from the historical UW P/C ratio).
4. GET /api/etfs/{ticker}/holdings
Full constituent list with per-name pricing, options flow, and weights.
Path: ticker = ETF symbol.
Query: none documented.
Response per constituent:
| Field | Type | Description |
|---|---|---|
ticker | string | Constituent symbol |
name, short_name | string | Company name + abbreviation |
sector | string | GICS sector |
type | string | E.g. "stock" |
weight | string | Portfolio weight (%) |
shares | int | Number of shares held by the ETF |
close, open, high, low, prev_price | string | Today’s OHLC + prior close |
volume, avg30_volume | int / string | Today’s + 30d avg volume |
week52_high, week52_low | string | 52-week range |
has_options | bool | Options trade |
call_volume, put_volume | int | Today’s option volumes |
call_premium, put_premium | string | Today’s option premium |
bullish_premium, bearish_premium | string | UW-classified directional premium |
MK3 use (high value). This is the single most useful endpoint for an SPY hunter:
- Identify the top-25 SPY weights. The top ~7 names (AAPL, MSFT, NVDA, AMZN, META, GOOGL, GOOG) typically account for 25%+ of SPY. When they move together, SPY moves; when they diverge, internal index dispersion is high.
- Per-constituent option flow.
bullish_premium - bearish_premiumon the top-25 = a basket-level flow signal. Weighted byweightit approximates a SPY-equivalent flow proxy that’s richer than just looking at SPY’s own options flow. - Dispersion check. If top names have heavy call flow but mid-cap constituents have heavy put flow, SPY is being held up by mega-caps only - thin breadth. Setup Hunter can gate on breadth.
This is also the path to building a “SPY-implied flow tape” feature
that improves on the raw flow_alerts for SPY.
5. GET /api/etfs/{ticker}/weights
Sector + country weight breakdown.
Path: ticker = ETF symbol.
Query: none documented.
Response:
{
"data": {
"sector": [
{ "sector": "Technology", "weight": "29.71" },
...
],
"country": [
{ "country": "United States", "weight": "96.93" },
...
]
}
}MK3 use. Daily session-start pull. Sector weights change slowly, but knowing SPY is ~30% Tech (vs the historical 25%) frames how sector-specific news flows back to the index. Country weights for SPY are nearly 100% US - useful confirmation only.
6. GET /api/etfs/{ticker}/in-outflow
Daily in/outflow time series for the ETF itself.
Path: ticker = ETF symbol.
Query parameters:
| Param | Type | Required | Default |
|---|---|---|---|
start_date | date | no | last trading date |
end_date | date | no | last trading date |
Response per row:
| Field | Type | Description |
|---|---|---|
date | ISO date | Trading date |
change | int | Net share change (creations minus redemptions) |
change_prem | string | Dollar value of the share change |
close | string | Closing price |
volume | int | Day’s volume |
is_fomc | bool | FOMC meeting on this date |
MK3 use. The is_fomc flag is convenient - it lets you tag
flows on FOMC days separately from normal days. Pull a rolling
30-day window at session start; large recent outflows on SPY = real
risk-off context that the price tape alone won’t show.
7. GET /api/etfs/{ticker}/exposure
Inverse lookup: which ETFs hold this ticker.
Path: ticker = the constituent symbol (e.g. AAPL), not
the ETF.
Query: none documented.
Response per row:
| Field | Type | Description |
|---|---|---|
etf | string | ETF ticker holding the constituent |
full_name | string | ETF full name |
weight | string | Constituent’s weight in that ETF |
shares | int | Shares held by that ETF |
last_price, prev_price | string | ETF prices |
MK3 use. Low for L1. For L2 (single-stock swings), this matters: when buying AAPL, knowing it’s in 600+ ETFs means broad-market flow pulls it - reduces alpha vs SPY. Filed for L2.
8. Cross-endpoint workflow for SPY hunter
Session start (08:00 ET):
-> GET /api/etfs/SPY/info (one row, AUM + option volumes)
-> GET /api/etfs/SPY/holdings (~500 rows, top-25 are the load)
-> GET /api/etfs/SPY/weights (sector + country snapshot)
-> GET /api/etfs/SPY/in-outflow?start_date=T-30 (30d flow tape)
Derived features cached for the session:
- spy_top25_symbols + weights
- spy_sector_weights
- spy_30d_net_flow
- spy_30d_fomc_flow_delta (FOMC vs non-FOMC)
Intraday:
-> No further /etfs polling needed for SPY (these are slow-moving)
-> Use top-25 list to drive per-name option flow + dark-pool queries
9. Nautilus integration shape
- One-time session-start fetch for endpoints 1-4 against SPY.
- Cache as a
UWEtfContextsnapshot in memory. - Expose
top_25_constituentsandsector_weightsas queryable attributes for downstream signal Actors. - Endpoint 5 (
/exposure) is on-demand only - probably not wired into L1 at all.
10. Known gaps
- No realtime push for in/outflow. Daily only.
- Holdings cadence not documented - assumed daily, but the
weightfield changes very slowly (rebalance schedule). Verify how often it refreshes. - The “options flow” fields on holdings (
bullish_premium,bearish_premium) overlap withuw-api-alertsflow_alertsnoti_type - need to confirm whether they aggregate the same source or different.
11. Source URLs
https://api.unusualwhales.com/docs/operations/PublicApi.EtfController.infohttps://api.unusualwhales.com/docs/operations/PublicApi.EtfController.holdingshttps://api.unusualwhales.com/docs/operations/PublicApi.EtfController.weightshttps://api.unusualwhales.com/docs/operations/PublicApi.EtfController.in_outflowhttps://api.unusualwhales.com/docs/operations/PublicApi.EtfController.exposure
cortana-north-star uw-api-gex-greeks uw-api-darkpool uw-api-alerts 2026-05-15-mk3-setup-hunter-architecture