# SEC 13F Holdings by Fund with Quarterly Changes (`codecraftco/sec-13f-holdings`) Actor

Every position from a fund's SEC 13F-HR filings, one row per holding, with dollar values, share counts, voting authority, portfolio weight and quarter-over-quarter changes (new, increased, decreased, closed).

- **URL**: https://apify.com/codecraftco/sec-13f-holdings.md
- **Developed by:** [Code Craft Colorado](https://apify.com/codecraftco) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 holdings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## SEC 13F Holdings by Fund with Quarterly Changes

Get the full portfolio of any institutional investor from its SEC Form 13F filings: one row
per position, with market value in dollars, share count, voting authority, weight in the
portfolio, and what changed since the previous quarter. Give it a fund's SEC CIK and it
returns the parsed holdings of the newest 13F-HR, or as many quarters of history as you ask for, with
every position labelled NEW, INCREASED, DECREASED, UNCHANGED or CLOSED.

The data comes straight from the SEC's official EDGAR endpoints over plain HTTP: no API key,
no login, no browser, no proxy. A 13F is available here as soon as EDGAR publishes it. The
client identifies itself as the SEC's fair-access policy requires and stays under the SEC's
request-rate limit, so you can leave it running on a schedule.

### At a glance

Three rows from Berkshire Hathaway's 13F-HR for the quarter ended 2026-06-30:

| nameOfIssuer | valueUsd | shares | change | sharesChange | portfolioPercent |
| --- | --- | --- | --- | --- | --- |
| ALPHABET INC | 23523689772 | 65824467 | INCREASED | 24541369 | 7.8608 |
| D R HORTON INC | 580504 | 3564 | NEW | null | 0.0002 |
| CONSTELLATION BRANDS INC | 0 | 0 | CLOSED | -632890 | 0.0 |

### What data does it extract?

Each row is one entry from the information table of one 13F-HR filing.

**Filing**

- `filerCik`: the 10-digit CIK of the filing manager.
- `filerName`: the manager's name from the filing's cover page.
- `periodOfReport`: the quarter-end date the holdings are as of (YYYY-MM-DD).
- `filingDate`: the date the SEC received the filing.
- `accessionNumber`: EDGAR's unique ID for the filing.
- `form`: `13F-HR`, or `13F-HR/A` when amendments are included.
- `sourceUrl`: the filing's index page on EDGAR, where the original documents can be checked.

**Security**

- `nameOfIssuer`: issuer name as written by the filer, for example `ALPHABET INC`.
- `titleOfClass`: the class of security, for example `COM`, `CL A`, `CAP STK CL C`.
- `cusip`: the nine-character CUSIP, the most reliable key for joining to other data.
- `figi`: the Financial Instrument Global Identifier when the filer reports one, otherwise null.
- `putCall`: `Put` or `Call` for options positions, null for everything else.

**Position**

- `valueUsd`: market value at quarter end, normalized to whole dollars for every period.
- `shares`: the number of shares, or the principal amount for debt.
- `sharesType`: `SH` when `shares` counts shares, `PRN` when it is a principal amount.
- `portfolioPercent`: this row's share of the filing's total value, as a percentage rounded
  to four decimals.

**Control**

- `investmentDiscretion`: who decides on the position, as the filer coded it (see below).
- `otherManagers`: sequence numbers of other managers on the filing who share the position,
  as the filer wrote them, for example `["4", "11"]`.
- `votingSole`, `votingShared`, `votingNone`: how many of the shares the manager votes alone,
  votes jointly with others, or has no vote on.

**Change since the previous filing**

- `change`: `NEW`, `INCREASED`, `DECREASED`, `UNCHANGED` or `CLOSED`; null when
  `includeChanges` is off.
- `previousShares`: the total shares held in the position in the previous filing.
- `sharesChange`: current total minus previous total; negative when trimmed or closed.

### How to read a 13F row

#### The `change` labels

Each filing is compared with the filer's next older filing. Positions are matched on CUSIP
plus `putCall`, so a stock, a put on it and a call on it are three separate positions. Rows in
the same filing that share a key are added together first, and the comparison is on total
share count, not value.

| `change` | How it is decided | `previousShares` | `sharesChange` |
| --- | --- | --- | --- |
| `NEW` | The key is not in the previous filing, or there is no previous filing to compare with | null | null |
| `INCREASED` | Total shares now are greater than the previous total | previous total | positive |
| `DECREASED` | Total shares now are less than the previous total | previous total | negative |
| `UNCHANGED` | Totals are equal | previous total | 0 |
| `CLOSED` | The key was in the previous filing but is absent now | previous total | minus the previous total |

Two consequences of matching on totals:

- When a filer lists the same security on several lines (Berkshire reports Apple on twelve,
  one per group of sub-managers), every line carries the same `change`, `previousShares` and
  `sharesChange`, and they describe the whole position. The Alphabet line above with
  65,824,467 shares shows a previous total of 54,249,798 because that is the sum of all
  Alphabet Class A lines in the prior filing.
- `CLOSED` rows are not in the filing; the actor synthesizes one per exited position so you
  never miss a sale. A CLOSED row has `shares` 0, `valueUsd` 0, `portfolioPercent` 0 and
  null voting columns. Issuer, class, CUSIP, FIGI, discretion and other-manager fields are
  copied from the previous filing; `periodOfReport`, `filingDate`, `accessionNumber`, `form`
  and `sourceUrl` are those of the current filing, because the exit was observed there.

To label the oldest quarter you ask for, the actor fetches one extra older filing. That extra
filing's own rows are not returned and not charged. If a fund has filed fewer 13F-HRs than you
ask for, the oldest available filing has nothing to compare against and every position in it
is `NEW`.

#### `valueUsd` and the 2023 unit change

Until the end of 2022, filers reported values in thousands of dollars; for periods ending on
or after 2023-01-01 the SEC requires whole dollars. The actor multiplies values from earlier
periods by 1,000, so every `valueUsd` is in dollars and quarters on either side of the
cutover compare directly. If a filer mis-stated units in the original filing, the normalized
value follows the filing.

#### Codes that appear in a row

| Field | Value | Meaning |
| --- | --- | --- |
| `sharesType` | `SH` | `shares` is a number of shares |
| `sharesType` | `PRN` | `shares` is a principal amount, used for debt securities |
| `putCall` | `Put` | The position is put options on the issuer |
| `putCall` | `Call` | The position is call options on the issuer |
| `putCall` | null | Not an options position |
| `investmentDiscretion` | `SOLE` | The filing manager alone decides on the position |
| `investmentDiscretion` | `DFND` | Discretion is shared with another manager under a defined agreement (Berkshire codes every position this way) |
| `investmentDiscretion` | `OTR` | Discretion is shared in some other way |

`investmentDiscretion` is passed through exactly as the filer wrote it, so spelled-out
variants such as `DEFINED` can also appear.

#### Voting authority and portfolio weight

`votingSole`, `votingShared` and `votingNone` split the shares by voting authority and
normally add up to `shares`. `portfolioPercent` is the row's `valueUsd` divided by the sum of
`valueUsd` across all rows of the same filing, times 100, so the rows of one filing add up to
100 and a security reported on several lines has its weight spread over them.

#### Why the newest quarter may be older than you expect

A manager whose holdings are reported inside another manager's combined filing files a
13F-NT, a notice with no holdings table, instead of a 13F-HR. The actor only counts 13F-HR
(and 13F-HR/A when enabled), so the newest returned quarter is the newest one with a table
under that CIK. Vanguard, for example, has filed 13F-NT for recent quarters. `quarters`
counts filings, not calendar quarters, so skipped quarters do not consume your count.

### Input

| Field | Meaning | Default | When to use it |
| --- | --- | --- | --- |
| `ciks` | One or more SEC Central Index Keys of 13F filers, for example `"0001067983"` for Berkshire Hathaway. Leading zeros are optional. | required | Always. Funds have no ticker, so the CIK is the identifier; search the fund name on the SEC's EDGAR company search to find it. |
| `quarters` | How many of the most recent 13F-HR filings to return per fund, newest first. | `1` | Raise it to backfill history; 8 filings is two years for a fund that files every quarter. |
| `includeAmendments` | Also treat 13F-HR/A filings as periods. | `false` | Turn on when you want restatements as separate rows. Off by default because an amendment may restate the whole table or only add positions, so comparing it to the previous filing can mislabel changes. |
| `includeChanges` | Compare each filing with the previous one and label every position; emit CLOSED rows for exits. | `true` | Turn off when you only want the holdings as filed; `change`, `sharesChange` and `previousShares` are then null and no CLOSED rows are added. |

The latest quarter for one fund, with changes:

```json
{
    "ciks": ["0001067983"]
}
```

Two years of history for two funds, amendments included:

```json
{
    "ciks": ["0001067983", "0001350694"],
    "quarters": 8,
    "includeAmendments": true
}
```

Each fund costs one request for its filing list and each filing three more (the file index,
the cover page and the holdings table). At the default five requests per second, eight
quarters of one fund load in a few seconds, and the largest filers, such as Vanguard with more
than 17,000 positions in one filing, complete in about ten to fifteen seconds. A long list of funds takes
proportionally longer. Leave the memory at the default 512 MB, which is also the minimum; the
largest filers need it.

### Output example

One row from Berkshire Hathaway's filing for the quarter ended 2026-06-30, the Alphabet
Class A line held with sub-managers 4 and 11:

```json
{
    "filerCik": "0001067983",
    "filerName": "Berkshire Hathaway Inc",
    "periodOfReport": "2026-06-30",
    "filingDate": "2026-08-14",
    "accessionNumber": "0001193125-26-352200",
    "form": "13F-HR",
    "nameOfIssuer": "ALPHABET INC",
    "titleOfClass": "CAP STK CL A",
    "cusip": "02079K305",
    "figi": null,
    "valueUsd": 23523689772.0,
    "shares": 65824467.0,
    "sharesType": "SH",
    "putCall": null,
    "investmentDiscretion": "DFND",
    "otherManagers": ["4", "11"],
    "votingSole": 65824467.0,
    "votingShared": 0.0,
    "votingNone": 0.0,
    "change": "INCREASED",
    "sharesChange": 24541369.0,
    "previousShares": 54249798.0,
    "portfolioPercent": 7.8608,
    "sourceUrl": "https://www.sec.gov/Archives/edgar/data/1067983/000119312526352200/0001193125-26-352200-index.htm"
}
```

Rows land in the run's default dataset, one per holding, in filing order with each quarter's
CLOSED rows after its filed positions. Download it as JSON, CSV, Excel or XML from Console or
the API. The dataset's `Holdings` view shows the columns most people want first: fund, period,
issuer, class, CUSIP, value, shares, put/call, weight, change, share change, filing date and
a link to the filing.

### Errors and edge cases

Anything the actor cannot process is recorded in the `ERRORS` record of the run's default
key-value store, and the run's status message says how many errors there were. Nothing that
fails is charged. Each entry carries the `cik`, an `error` message and, when a specific filing
failed, its `accessionNumber` and `sourceUrl`.

| Situation | What happens |
| --- | --- |
| A CIK is not numeric | Recorded as `CIK must be numeric`; the other CIKs still run. |
| No 13F-HR found for a CIK | Recorded as `No 13F-HR filings found`. This includes companies that are not 13F filers and managers that only file 13F-NT. |
| A filing has no readable holdings table | Recorded with the filing's accession number and link, for example when `primary_doc.xml` or the information table XML is missing or unparseable. The fund's other quarters are still returned. |
| The filing before a returned quarter could not be read | That quarter's rows are all `NEW` and it gets no `CLOSED` rows, because there is nothing to compare against. |
| The SEC throttles the run | The actor pauses (30 seconds the first time, then 60, 120, 300 and 600), halves its request rate, retries, and only climbs back toward the normal rate after a stretch of successful requests. A throttled run finishes late rather than failing. |
| The SEC keeps throttling for 20 minutes in one run | The run stops with a status message saying so. Every row already delivered stays in the dataset. |
| A transient server error from EDGAR | Retried with increasing delay, up to four times, before the filing is recorded as an error. |
| Your run's spending limit is reached | The actor stops pushing rows and finishes cleanly; the rows already saved are kept. |

### How much does it cost?

You pay per dataset row. One row is one holding in one filing, and CLOSED rows count because
they are results you asked for (they exist only when `includeChanges` is on). Besides the
standard Apify actor start event there is nothing else: no fee per filing or per request, no
charge for the extra older filing fetched to label the oldest quarter, and no charge for
anything recorded in `ERRORS`.

A concentrated fund like Berkshire Hathaway is a few dozen to a hundred rows per quarter; a
broad index or quant manager can be many thousands. Choose `quarters` and the list of CIKs
accordingly. The current price per row is on the Pricing tab.

### Use cases

- Follow a manager you respect: what it opened, added to, trimmed and exited each quarter.
- Build a "smart money" screen across several funds by joining rows on `cusip`.
- Give a research agent a dependable answer to "what does fund X hold and how has it changed?"
- Backfill two years of holdings history for a set of funds with `quarters: 8`.
- Compute concentration and turnover metrics from `portfolioPercent` and `sharesChange`.
- Catch exits the day a 13F is published by scheduling a run and filtering on `change = CLOSED`.

### Run it on a schedule, from the API, or from an agent

Run it by hand from Console, on a Console schedule (13F-HRs are due 45 days after quarter
end, so a daily run in the second half of February, May, August and November catches them as
they arrive), from the Apify API and its JavaScript and Python clients, or from an AI agent
through Apify's MCP server. The input is the same JSON everywhere and the output is the same
dataset.

### Related actors

- [SEC Form 4 Insider Trades by Ticker](https://apify.com/codecraftco/sec-insider-trades):
  every insider transaction from Form 4 filings, one row per transaction, with role, price,
  shares, holdings after, and the 10b5-1 flag.
- [SEC 8-K Filings by Ticker with Item Codes](https://apify.com/codecraftco/sec-8k-filings):
  8-K filings with item codes and titles, exhibits, and optional extracted text.
- [Wayback Machine Snapshots and Archived Page Content](https://apify.com/codecraftco/wayback-snapshots):
  every Wayback Machine capture of a URL, host, or domain, with optional archived page text.

All four run against official public APIs with no proxies or credentials.

### FAQ

**Is this legal?** Yes. 13F filings are public records published by the SEC for anyone to
read. This actor uses the SEC's official EDGAR endpoints, identifies itself as the SEC's
fair-access policy requires, and stays under the SEC's request-rate limit.

**Where do I find a fund's CIK?** Search the fund name on the SEC's EDGAR company search; the
CIK is shown next to the name. Leading zeros are optional. Funds have no ticker, so a ticker
will not work here.

**How current is the data?** A 13F appears here as soon as EDGAR publishes it. Managers must
file within 45 days of quarter end, and most file in the last days of that window.

**Why is `previousShares` larger than `shares` on a row marked UNCHANGED?** The filer reported
that security on more than one line. `previousShares` and `sharesChange` describe the total
position across all lines with the same CUSIP and put/call, and every line carries the same
values. Add up the lines for the current total.

**Why do CLOSED rows carry the current quarter's filing details?** They describe an exit
observed in the current filing, so they are dated to it and link to it. `previousShares` is
the size of the position that was closed.

**Why is a value a thousand times off from what I expected?** Check `periodOfReport`. Values
are normalized to dollars using the SEC's 2023 cutover from thousands. If the filer itself
used the wrong units, the normalized value follows the filing.

**How are amendments handled?** Skipped unless `includeAmendments` is on. When it is on, each
13F-HR/A is its own period and is compared with the next older filing in the list, usually the
original 13F-HR for the same quarter. A full restatement then shows what was corrected; an
amendment that only adds positions shows those as NEW and everything else as CLOSED, which
is why the option is off by default.

**Does it cover 13D, 13G, N-PORT or mutual fund holdings?** No. It reads 13F-HR information
tables only, one job per actor.

**Something is missing or wrong?** Open an issue on the actor's Issues tab and it will be
looked at.

# Actor input Schema

## `ciks` (type: `array`):

SEC Central Index Keys of the 13F filers to fetch, e.g. 0001067983 for Berkshire Hathaway. Funds have no ticker, so the CIK is the identifier. Find it with the SEC's company search.

## `quarters` (type: `integer`):

How many of the most recent 13F-HR filings to return per fund, newest first. Each is one quarter of history.

## `includeAmendments` (type: `boolean`):

Also treat 13F-HR/A amendments as periods. Off by default because amendments may restate or only add positions.

## `includeChanges` (type: `boolean`):

Compare each period with the prior filing and label every position NEW, INCREASED, DECREASED, UNCHANGED or CLOSED. Closed positions are emitted as extra rows with zero shares.

## Actor input object example

```json
{
  "ciks": [
    "0001067983"
  ],
  "quarters": 1,
  "includeAmendments": false,
  "includeChanges": true
}
```

# Actor output Schema

## `holdings` (type: `string`):

One row per 13F holding: issuer, CUSIP, value in dollars, shares, voting authority, and the change versus the prior quarter.

## `errors` (type: `string`):

Inputs that could not be processed, with the reason. Present only when something failed; no row is charged for a failure.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "ciks": [
        "0001067983"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("codecraftco/sec-13f-holdings").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "ciks": ["0001067983"] }

# Run the Actor and wait for it to finish
run = client.actor("codecraftco/sec-13f-holdings").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "ciks": [
    "0001067983"
  ]
}' |
apify call codecraftco/sec-13f-holdings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,codecraftco/sec-13f-holdings"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/K2boQ7LzrOdcDbTr0/builds/ahlCbkH8fE7noLrxL/openapi.json
