Skip to main content
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

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

Python

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

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

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

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

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

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 to get notified when new 13F filings are published.
  • Cross-reference with insider trades: Combine 13F data with insider trading patterns 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 guide for additional patterns.