/api/v1/form-13f/fund/{cik}/holdings-analyticsPer-holding cost-basis analytics for a fund: avg buy price, unrealized P&L, holding period, sector, and last-transaction quarter — long-equity only.
Per-holding cost-basis analytics for a fund: avg buy price, unrealized P&L, holding period, sector, and last-transaction quarter — long-equity only.
Why use this
Common use case
Returns per-holding cost-basis economics for a fund's CURRENT 13F book — the analytics drill-down behind the 'Holdings' tab on /filer/<slug>-<cik>/. Computes VWAP-estimated avg_buy_price by walking the fund's tracked-holdings history chronologically, using the mean of daily closing prices during each quarter of share increase. Cost basis resets on a full sell-out (so a buy → full exit → re-entry path will report cost basis from the re-entry, not the original buy).
Response rows surface the standard cost-basis fields (avg_buy_price, cost_basis, market_value, unrealized_pnl, unrealized_pnl_pct), holding-period telemetry (first_added, holding_period_days, holding_period_quarters), AND three Phase 68 enrichments designed for the per-filer analytics page: sector (GICS-style classification via cusip_mappings.ticker → all_stocks_list.sector with holding_info.sector fallback), last_transaction_quarter (most-recent quarter with non-zero split-adjusted share delta), and last_share_delta (signed share movement on that quarter). The last-transaction tracking is split-adjustment-aware: a 4:1 split alone does NOT register as a transaction.
Long-equity ONLY by design. Options positions (puts/calls) are excluded — for the options book on the same fund, use GET /api/v1/form-13f/fund/{cik} with ?include_options=true and filter client-side on is_option=true. For aggregated cross-fund views of a single ticker (instead of per-fund-per-ticker), see GET /api/v1/tickers/{ticker}/fund-trends which is fully split-adjusted on the aggregation surface.
Avg buy prices are VWAP estimates from quarterly snapshots, NOT actual transaction prices — meta.disclaimer surfaces this verbatim and should be displayed on any UI that exposes avg_buy_price or unrealized_pnl_pct. All monetary amounts are in USD (post-Plan-51 thousands-vs-actual normalization). Share counts are split-adjusted (Phase 52) before cost-basis and last-transaction tracking.
Parameters
| Name | In | Required | Default | Allowed | Description | Example |
|---|---|---|---|---|---|---|
| cik | path | required | — | — | Fund CIK in 10-char zero-padded form (e.g. `0001067983` for Berkshire Hathaway, `0001364742` for BlackRock, `0000884144` for Vanguard, `0001037389` for Renaissance Technologies). Server-side normalization handles 6/7/10-char input forms. | 0001067983 |
| sort_by | query | optional | market_value | — | Sort key for `holdings[]`. Allowed values: `market_value`, `unrealized_pnl_pct`, `holding_period_days`, `avg_buy_price`, `cost_basis`. Returns 400 INVALID_PARAM on any other value. Use `holding_period_days` for a 'longest-held positions' view; `unrealized_pnl_pct` for a 'biggest gainers/losers' view. | — |
| order | query | optional | desc | — | Sort order: `desc` (default) or `asc`. Combined with `sort_by` — e.g. `sort_by=unrealized_pnl_pct&order=asc` surfaces the worst-performing positions first. | — |
Response schema
| Field | Type | Nullable | Description |
|---|---|---|---|
| cik | string | no | 10-char zero-padded fund CIK echoed back from the request. |
| holdings_count | integer | no | Number of current positions in the response (positions with `final_shares > 0` after the chronological cost-basis walk). Excludes positions that have been fully sold out — those reset cost basis on sell-out and are not surfaced as 'current' holdings. |
| summary | object | no | Portfolio-level rollups: `{ total_cost_basis, total_market_value, total_unrealized_pnl, total_unrealized_pnl_pct, avg_holding_period_days }`. All USD; null when current prices are unavailable. |
| summary.total_cost_basis | number | yes | Sum of `holdings[].cost_basis` across the portfolio. Reflects the cumulative VWAP-estimated dollar amount the fund has put to work in current positions. Null when no holdings have a computed cost basis. |
| summary.total_market_value | number | yes | Sum of `holdings[].market_value` across the portfolio (using each position's most recent close × split-adjusted shares). USD. Null when current prices are unavailable for all holdings. |
| summary.total_unrealized_pnl | number | yes | `total_market_value - total_cost_basis`. Positive = portfolio in the money on a cost basis; negative = portfolio underwater. Excludes any realized P&L from sold positions (those reset on sell-out). |
| summary.total_unrealized_pnl_pct | number | yes | `(total_unrealized_pnl / total_cost_basis) × 100`. Portfolio-wide unrealized return percentage on cost basis. Null when `total_cost_basis` is null or zero. |
| summary.avg_holding_period_days | integer | yes | Mean of `holdings[].holding_period_days` across positions with a populated value. Useful as a 'patience proxy' — Berkshire-style books show 1500+ days; quant books show 90-180 days. |
| holdings | array | no | Per-position rows sorted by `sort_by` / `order`. Each row carries cost-basis economics, holding period, and (Phase 68) sector + last-transaction enrichment. Long-equity only — options are excluded by design (use `/fund/{cik}?include_options=true` for the options book). |
| holdings[].cusip | string | no | 9-char CUSIP — natural primary key for the position. Apple's canonical CUSIP is `037833100`. |
| holdings[].ticker | string | yes | Resolved ticker via `cusip_mappings`. Null for unmapped CUSIPs (~3% in steady state). Multi-class issuers preserve the actual filed class (e.g. `BRK-A` vs `BRK-B`). |
| holdings[].name | string | no | Issuer name as filed (typically all-caps EDGAR convention, e.g. `APPLE INC`). Falls back to the CUSIP itself when no name is available. |
| holdings[].shares | integer | no | Current split-adjusted share count (final position size after the chronological walk). Phase 52 split-adjustment is honored — a 4:1 split is reflected in this number, not just in `last_share_delta`. |
| holdings[].avg_buy_price | number | yes | VWAP-estimated average buy price across all share-increase quarters in this position's history. Computed using the mean daily closing price during each quarter of share increase. Resets on full sell-out. NOT actual transaction prices — surfaced verbatim in `meta.disclaimer`. Null when the fund has held the position with zero shares throughout the window. |
| holdings[].current_price | number | yes | Most-recent close price for the resolved ticker. Null when the ticker is unmapped or the price feed is missing recent data (e.g. delisted issuers). |
| holdings[].cost_basis | number | yes | `avg_buy_price × final_shares` (USD). The total dollar amount the fund has put to work in this position on a VWAP-estimated basis. Null when `avg_buy_price` is null. |
| holdings[].market_value | number | yes | `current_price × final_shares` (USD). Position size at most-recent close. Null when `current_price` or `avg_buy_price` is unavailable. |
| holdings[].unrealized_pnl | number | yes | `market_value - cost_basis` (USD). Positive = position in the money; negative = position underwater. Null when `market_value` is null. |
| holdings[].unrealized_pnl_pct | number | yes | `(current_price / avg_buy_price - 1) × 100`. Unrealized return percentage on the position. Null when `avg_buy_price` is null or zero. Use as the sort key for a 'biggest gainers/losers' table view. |
| holdings[].holding_period_days | integer | yes | Days from `first_added` (the first quarter the fund acquired any shares) to the latest quarter in the response. Null when the position has been held with zero shares throughout the window. |
| holdings[].holding_period_quarters | integer | yes | Number of quarterly 13F-HR snapshots in which the fund reported a non-zero share count for this position since `first_added`. Diverges from `holding_period_days / 90` when the fund had a temporary full sell-out and re-entered. |
| holdings[].first_added | string | yes | ISO `YYYY-MM-DD` quarter-end date of the first 13F filing in which the fund reported any shares of this CUSIP. Survives across temporary sell-outs (the chronological walk resets cost basis but not `first_added`). |
| holdings[].sector | string | yes | Phase 68 enrichment: GICS-style sector for the holding. Resolution priority: (1) `cusip_mappings.ticker` → `all_stocks_list.sector` (canonical, Phase 53 hyphen-normalized); (2) `holding_info.sector` fallback (excluding the literal `'Equity'` which is not a real sector). Null for un-mapped CUSIPs and for CUSIPs whose all_stocks_list row has a null/empty sector. Use to populate a Sector column + Industry filter dropdown on per-filer Holdings tables. |
| holdings[].last_transaction_quarter | string | yes | Phase 68 enrichment: ISO `YYYY-MM-DD` quarter-end date of the most-recent quarter where the fund's split-adjusted share count CHANGED (non-zero delta). Tracked AFTER split-adjustment, so a 4:1 stock split alone does NOT register as a transaction (delta == 0 post-adjustment) — real buys/sells layered on top of a split quarter still register correctly. Null for positions that have been static since `first_added`. Powers the 'Latest Trades' sort mode on `/filer/<slug>-<cik>/`. |
| holdings[].last_share_delta | integer | no | Phase 68 enrichment: signed split-adjusted share movement on `last_transaction_quarter`. Positive = added shares; negative = trimmed shares; 0 = position has been static since `first_added` (in which case `last_transaction_quarter` is null). Use the magnitude to surface the size of the most recent activity ('Berkshire trimmed AAPL by 100M shares in Q3 2025'). |
| meta.sort_by | string | no | Echoed-back sort key from the request. |
| meta.order | string | no | Echoed-back sort order from the request. |
| meta.disclaimer | string | no | Cost-basis estimation disclaimer surfaced verbatim: average buy prices are VWAP estimates of daily closing prices during the quarter of each share increase, NOT actual transaction prices. Display this on any UI that surfaces `avg_buy_price` or `unrealized_pnl_pct` to set user expectations correctly. |
Sample response
- "cik": "0001067983"
- "holdings_count": 47
- "summary":
- "total_cost_basis": 130000000000
- "total_market_value": 308000000000
- "total_unrealized_pnl": 178000000000
- "total_unrealized_pnl_pct": 136.92
- "avg_holding_period_days": 1842
- "holdings":
- "meta":
- "sort_by": "market_value"
- "order": "desc"
- "disclaimer": "Average buy prices are estimated from quarterly 13F filings using VWAP of daily closing prices during the reported quarter. Not actual transaction prices."
Errors
| Status | Label | Description |
|---|---|---|
| 200 | OK | Request succeeded. |
| 400 | Bad Request | Invalid query, body, or path parameter. |
| 401 | Unauthorized | Missing or invalid Authorization header / api_Token. |
| 402 | Payment Required | Insufficient token balance for this call. Top up |
| 429 | Too Many Requests | Rate limit exceeded for your tier (see /pricing for tier limits). Tier limits |
| 500 | Server Error | Unexpected server-side failure. Retry with backoff; report if persistent. |
Code samples
curl "https://api.finradar.ai/api/v1/form-13f/fund/{cik}/holdings-analytics" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"Generate an API key in /account/credentials to run live queries (literal YOUR_API_KEY placeholder shown until then).