> ## Documentation Index
> Fetch the complete documentation index at: https://docs.secapi.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Monitor Institutional Holdings Changes

> Track 13F institutional holdings changes quarter over quarter using the SEC API. Includes curl, Python, and JavaScript examples.

Institutional investment managers that meet the Form 13F reporting threshold disclose qualifying holdings after each quarter. This tutorial shows how to retrieve one manager's report, compare the latest two parsable reports, and keep enough filing context to describe a position change accurately.

13F data is delayed and incomplete by design. It is useful for research, but it is not a live portfolio or a complete picture of a manager's exposures.

## Prerequisites

* An SEC API key (set as `SECAPI_API_KEY`)
* Basic familiarity with REST APIs
* (Optional) Python 3.8+ or Node.js 18+ for SDK examples

## Step 1 — Retrieve current 13F holdings

Use `/v1/owners/13f` to pull the latest holdings for an institutional investor by their CIK number.

### curl

```bash theme={null}
curl -H "x-api-key: $SECAPI_API_KEY" \
  "https://api.secapi.ai/v1/owners/13f?cik=0001067983&limit=20"
```

### Python

```python theme={null}
from secapi_client import SecApiClient

client = SecApiClient(api_key="your-api-key")

holdings = client.latest_13f(cik="0001067983", limit=20)

for holding in holdings["holdings"]:
    value_usd = (holding["valueUsdThousands"] or 0) * 1000
    print(f"{holding['issuer']}: ${value_usd:,.0f} ({holding['shares']:,} shares)")
```

### JavaScript

```ts theme={null}
import { SecApiClient } from "@secapi/sdk-js";

const client = new SecApiClient({
  apiKey: process.env.SECAPI_API_KEY!,
});

const holdings = await client.latest13F({
  cik: "0001067983",
  limit: 20,
});

for (const holding of holdings.holdings) {
  const valueUsd = (holding.valueUsdThousands ?? 0) * 1000;
  console.log(
    `${holding.issuer}: $${valueUsd.toLocaleString()} (${holding.shares.toLocaleString()} shares)`
  );
}
```

The report also includes `managerName`, `reportDate`, `filingDate`, `accessionNumber`, and `provenance`. Keep those fields with each holding; they establish which disclosed report you are comparing.

## Step 2 — Compare holdings across quarters

The `/v1/owners/13f/compare` endpoint compares the latest two **parsable** reports available for one manager. It surfaces new positions, closed positions, and share-count changes, but it does not accept two arbitrary reporting-period selectors.

### curl

```bash theme={null}
curl -H "x-api-key: $SECAPI_API_KEY" \
  -H "content-type: application/json" \
  -X POST \
  -d '{"cik":"0001067983","limit":20}' \
  "https://api.secapi.ai/v1/owners/13f/compare"
```

### Python

```python theme={null}
diff = client.compare_13f(
    cik="0001067983",
    limit=20,
)

for change in diff["rows"]:
    direction = "+" if (change["deltaShares"] or 0) > 0 else ""
    print(f"{change['issuer']}: {direction}{change['deltaShares'] or 0:,} shares ({change['status']})")
```

### JavaScript

```ts theme={null}
const diff = await client.compare13F({
  cik: "0001067983",
  limit: 20,
});

for (const change of diff.rows) {
  const direction = (change.deltaShares ?? 0) > 0 ? "+" : "";
  console.log(
    `${change.issuer}: ${direction}${(change.deltaShares ?? 0).toLocaleString()} shares (${change.status})`
  );
}
```

The response identifies `currentFilingDate` and `previousFilingDate` and gives each row a status (`added`, `removed`, `changed`, or `unchanged`). Verify both dates and the returned source provenance before calling a result a specific quarter-over-quarter change. List the manager's filings and retrieve the selected reports when you need both accession numbers.

## Step 3 — Build a quarterly monitoring workflow

Combine the comparison endpoint with a filing list to build a review script. Run it on a cadence that fits your process, then use the filing dates in the response to determine whether a manager has published a new report.

### Python

```python theme={null}
MANAGERS = [
    {"name": "Berkshire Hathaway", "cik": "0001067983"},
    {"name": "Bridgewater Associates", "cik": "0001350694"},
]

THRESHOLD_PCT = 10  # flag changes over 10%

for manager in MANAGERS:
    diff = client.compare_13f(
        cik=manager["cik"],
        limit=20,
    )

    significant = [
        c for c in diff["rows"]
        if c["status"] in ("added", "removed")
        or abs((c["deltaShares"] or 0) / max(c["previousShares"] or 1, 1)) > THRESHOLD_PCT / 100
    ]

    if significant:
        print(f"\n--- {manager['name']} ---")
        for c in significant:
            print(f"  {c['issuer']}: {c['status']} ({c['deltaShares'] or 0:+,} shares)")
```

## Next steps

* **Set up webhooks**: Use the [Filing Monitor tutorial](/tutorials/build-filing-monitor) to get notified when new 13F filings are published.
* **Cross-reference with insider trades**: Combine 13F data with [insider trading patterns](/tutorials/analyze-insider-trading) for a complete ownership picture.
* **Track sector exposure**: Aggregate holdings by sector to monitor allocation shifts.
* **Choose a specific report**: Use `GET /v1/owners/13f/filings` before requesting `reportDate=YYYY-MM-DD` when your work requires a named period rather than the latest report.

See the [Ownership Workflows](/ownership-workflows) guide for additional patterns.
