/api/v1/form-13f/fund/{cik}/cusip/{cusip}/historyPer-quarter share + value history for a single (fund, CUSIP) position — the chronological trail of one security held by one filer across every 13F-HR quarter we have on file.
Per-quarter share + value history for a single (fund, CUSIP) position — the chronological trail of one security held by one filer across every 13F-HR quarter we have on file.
Why use this
Common use case
Returns the full chronological trail of ONE position held by ONE filer — every quarter we have a 13F filing where the (CIK, CUSIP) appeared, the shares held, the reported value, the QoQ share delta, and a transaction-type classification. Used by the FinRadar.ai filer-page transaction-timeline chart on the chevron-expanded position row (replaces the placeholder that used to read 'Transaction-timeline chart coming next — needs per-ticker quarterly history endpoint').
Why CUSIP-keyed (not ticker): tickers change. Mergers (SHEN → CALX 2024), spinoffs (GOOG/GOOGL 2014), ticker reuse, and corporate-action chains (SIRI's 2024 reverse split flipped CUSIP 82968B103 → 829933100 mid-quarter) all break ticker-keyed timelines. CUSIPs are the canonical 13F security identifier — frontend rows from GET /api/v1/form-13f/fund/{cik} already carry both cusip and canonicalCusip, pass either back here.
Filing selection per quarter matches GET /api/v1/form-13f/fund/{cik} and CostBasisService exactly — for each period_of_report we pick the filing with the MOST non-option holdings (#264 amendment-handling). So a fund that filed a 36-holding 13F-HR and then a 4-holding 13F-HR/A 'correction' amendment shows the 36-holding original on this endpoint, NOT the 4-holding amendment.
Closed positions appear in the history up through their last reported quarter — once the fund has zero shares of the security, there is no row for subsequent quarters (no synthetic 0-share emit). Frontends rendering the chart should interpret 'no entry for quarter X' as 'position closed before X'; the chart axis can drop to 0 by interpolating. Share counts are SPLIT-ADJUSTED — forward-adjusted to the latest split basis via the split feed (ticker-keyed) so a stock split is NOT mislabelled as a phantom buy/sell; the timeline is continuous and only real trading moves the line. Each row also carries shares_raw (the literal as-filed count), and the response carries top-level split_adjusted + splits[] for reconciliation. value is left raw (13F value = shares × quarter-end price, already split-invariant). If the ticker can't be resolved or the split-feed read fails, the endpoint degrades to raw (unadjusted) shares rather than erroring.
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, `0001536411` for Duquesne Family Office). Server-side normalization handles 6/7/10-char input forms (the endpoint accepts `1067983` and pads it for you). | 0001067983 |
| cusip | path | required | — | — | Canonical 9-character CUSIP for the security (`current_cusip` column in `normalized_holdings_deduped`). Pass the `cusip` (or `canonicalCusip` for chained-CUSIP fallback) from any holdings row on [GET /api/v1/form-13f/fund/{cik}](/docs/institutional-holdings/form-13f-api/get-form-13f-fund-cik). Validated server-side: alphanumeric only, ≤ 12 chars — anything else returns 400 BAD_REQUEST. | 037833100 |
Response schema
| Field | Type | Nullable | Description |
|---|---|---|---|
| status | string | no | — |
| request_id | string | yes | — |
| timestamp | string | no | — |
| data | object | no | — |
| data.cik | string | no | — |
| data.cusip | string | no | — |
| data.ticker | string | yes | — |
| data.name | string | yes | — |
| data.quarters | integer | no | — |
| data.split_adjusted | boolean | no | — |
| data.splits | array | no | — |
| data.splits[].date | string | no | — |
| data.splits[].ratio | number | no | — |
| data.history | array | no | — |
| data.history[].quarter_date | string | no | — |
| data.history[].shares | integer | no | — |
| data.history[].shares_raw | integer | no | — |
| data.history[].value | integer | no | — |
| data.history[].shares_delta | integer | no | — |
| data.history[].transaction_type | string | no | — |
Sample response
- "cik": "0001067983"
- "cusip": "037833100"
- "ticker": "AAPL"
- "name": "APPLE INC"
- "quarters": 4
- "split_adjusted": true
- "splits":
- "history":
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/0001067983/cusip/037833100/history" \
-H "X-API-Key: YOUR_API_KEY"Generate an API key in /account/credentials to run live queries (literal YOUR_API_KEY placeholder shown until then).