# SEC Insider Trading — EDGAR Form 4 Buys & Sells as JSON (`shelvick/sec-insider-trades`) Actor

SEC insider trades from EDGAR Form 4 filings as JSON: insider trading data by ticker or CIK. Who traded (officer, director, 10% owner), insider buying and insider selling, grants, exercises, shares, price, value, shares held after, dates. Also Form 3 and Form 5. Parsed from public SEC data.

- **URL**: https://apify.com/shelvick/sec-insider-trades.md
- **Developed by:** [Scott Helvick](https://apify.com/shelvick) (community)
- **Categories:** AI, Developer tools, Business
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.50 / 1,000 insider filing parseds

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

Corporate insiders — officers, directors, and 10% owners — must report every trade in their own company's stock to the SEC on Form 4 (and Form 3/5), but the filings are buried in EDGAR as raw ownership XML with cryptic single-letter transaction codes. Give this Actor a ticker or CIK and it returns the recent insider filings as clean, normalized JSON: who traded, whether it was a buy or a sell, how many shares, at what price, what they own now, and when.

*Not affiliated with or endorsed by the U.S. Securities and Exchange Commission (SEC).*

**Give it a ticker, get one record per filing:**

```json
{
  "issuerTicker": "AAPL",
  "filingDate": "2026-05-29",
  "reportingOwner": { "name": "LEVINSON ARTHUR D", "relationship": "Director" },
  "transactions": [
    { "transactionType": "Open-market sale", "shares": 50000, "pricePerShare": 311.02 }
  ],
  "summary": { "totalDisposedValue": 15551000.0, "netShares": -50000 }
}
```

The input is prefilled with two well-known tickers (`AAPL`, `NVDA`) — the first run needs zero setup, so just start it and real filings come back.

### What this does

- Resolves a stock **ticker** (e.g. `AAPL`, `BRK.B`) or 10-digit **CIK** to the issuer via SEC's official ticker map.
- Reads the issuer's EDGAR filing history and selects the recent **Form 4** (changes in ownership), **Form 3** (initial holdings), and/or **Form 5** (annual) filings you ask for.
- Fetches each filing's structured ownership XML and parses it **deterministically** — no LLM, no guesswork — into one dataset record per filing.
- Decodes the SEC transaction code into a human-readable type: open-market **purchase** / **sale**, **grant/award**, **option exercise**, **gift**, **tax withholding**, and the rest of the Form 4/5 code table.
- Captures both the **non-derivative** table (common stock) and the **derivative** table (options, RSUs, warrants) with underlying security, exercise price, and expiration.
- Identifies the **reporting owner** and their relationship (director, officer + title, 10% owner) so you can tell an executive's trade from a passive holder's.
- Adds a per-filing **summary**: transaction count, total acquired (buy) value, total disposed (sell) value, and net share change — for quick scanning.
- Bounds cost and runtime with `maxFilings` (per company) and an optional `sinceDate` floor.

Use it to:

- **Monitor a watchlist** of tickers for new insider buying or selling since a given date.
- **Flag cluster buys** — several insiders at one company purchasing in a short window.
- **Track a specific executive's** acquisitions and dispositions over their recent filings.
- **Feed an investment-research agent** structured insider-activity data without writing an EDGAR parser.
- **Build an ownership-change dataset** across a set of companies for downstream analysis.

### Why deterministic parsing matters

The data is already public and free — SEC publishes every Form 4 as machine-readable XML. The problem isn't access; it's that the raw document is awkward to consume: transaction amounts are nested several elements deep, buys and sells are distinguished by a one-letter code (`P`, `S`, `A`, `M`, `F`, `G`, …) plus a separate acquired/disposed flag, and options live in a wholly separate table with their own schema.

Insider-trade figures are the kind of data people act on, so the extraction has to be exact. Every value this Actor returns is copied verbatim from the filing's XML — share counts, prices, and post-transaction holdings are the filer's own reported numbers, and the only interpretation layer is mapping the SEC's documented transaction codes to readable labels. There is no language model in the path to hallucinate a number. That also keeps the cost floor near zero, so the price reflects the normalization work, not an inference bill.

### How it compares to the alternatives

| Approach | Normalized fields | Codes decoded | Per-filing aggregates | Derivatives | Agent-callable |
|---|---|---|---|---|---|
| Raw EDGAR XML / full-text search | — | — | — | raw only | — (you parse it) |
| Generic web scraper on a finance site | partial | sometimes | — | inconsistent | brittle |
| **SEC Insider Trades** | yes | yes | yes | yes | yes |

Raw EDGAR is authoritative but unstructured — you own the parsing, the code table, and the derivative-table edge cases. A scraper pointed at a third-party finance site inherits that site's layout changes and gaps. This Actor goes straight to the primary source and returns a stable, documented JSON shape.

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `identifiers` | array of strings | yes | — | Tickers or 10-digit CIKs (1–50). Tickers resolve to CIK via SEC's official map; `BRK.B`/`BRK-B` variants are handled. |
| `formTypes` | array of strings | | `["4"]` | Which ownership forms to include: `4` (changes — buys/sells), `3` (initial holdings), `5` (annual). Validated at runtime. |
| `maxFilings` | integer | | `20` | Max filings per company, newest first (1–100). Bounds runtime and cost (one charge per filing). |
| `sinceDate` | string | | — | Optional ISO date floor (`YYYY-MM-DD`); only filings filed on/after are returned. Blank = most recent regardless of date. |

One dataset record is produced per filing. Filings come from the issuer's recent EDGAR history (the latest ~1000 filings); deep historical archives beyond that window are out of scope.

### Output

One record per insider filing. Nullable fields are `null` on a failed record.

```json
{
  "identifier": "AAPL",
  "status": "completed",
  "cik": "0000320193",
  "issuerName": "Apple Inc.",
  "issuerTicker": "AAPL",
  "formType": "4",
  "filingDate": "2026-05-29",
  "periodOfReport": "2026-05-27",
  "accessionNumber": "0001140361-26-023363",
  "reportingOwner": {
    "name": "LEVINSON ARTHUR D",
    "cik": "0001214128",
    "isDirector": true,
    "isOfficer": false,
    "isTenPercentOwner": false,
    "isOther": false,
    "officerTitle": null,
    "relationship": "Director"
  },
  "transactions": [
    {
      "securityTitle": "Common Stock",
      "transactionDate": "2026-05-27",
      "transactionCode": "S",
      "transactionType": "Open-market sale",
      "acquiredDisposed": "D",
      "shares": 50000,
      "pricePerShare": 311.02,
      "transactionValue": 15551000.0,
      "sharesOwnedAfter": 3764576,
      "directOrIndirect": "D",
      "isDerivative": false,
      "underlyingSecurity": null,
      "underlyingShares": null,
      "exercisePrice": null,
      "expirationDate": null
    }
  ],
  "summary": {
    "transactionCount": 1,
    "totalAcquiredValue": 0.0,
    "totalDisposedValue": 15551000.0,
    "netShares": -50000
  },
  "sourceFilingUrl": "https://www.sec.gov/Archives/edgar/data/320193/000114036126023363/form4.xml",
  "error": null,
  "errorClass": null,
  "notice": "Figures are from public SEC EDGAR filings (see sourceFilingUrl); parsing is best-effort and provided as-is, not investment advice — verify against the primary filing before relying on any value."
}
```

A failed record carries `status: "failed"`, a clean `error` explanation, an `errorClass` for machine routing, and `transactions: []`.

Every record includes `retry`, derived from `errorClass`: `null` on success, `later` to retry after a short delay, `with_changes` to correct the input described in `error`, or `never` to avoid repeating the same request.

#### Failure taxonomy

`source_error` means a temporary SEC EDGAR outage or garbled response and is retryable. `no_match`, `malformed_input`, and `normalize_error` are not retryable by the caller.

### Example

Input — latest 10 insider filings at each of two companies, since a date:

```json
{
  "identifiers": ["AAPL", "NVDA"],
  "formTypes": ["4"]
}
```

```bash
curl -X POST "https://api.apify.com/v2/acts/shelvick~sec-insider-trades/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"identifiers":["AAPL","NVDA"],"formTypes":["4"]}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("shelvick/sec-insider-trades").call(
    run_input={"identifiers": ["AAPL", "NVDA"], "formTypes": ["4"]}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["issuerTicker"], item["reportingOwner"]["relationship"], item["summary"])
```

### Calling from an AI agent

**Apify MCP server (`mcp.apify.com`)** — the Actor is exposed as a callable tool whose input schema is self-documenting, so an LLM can construct a valid call from the tool description alone (ticker or CIK in; normalized insider filings out). Pay per call via x402 (USDC on Base) or Skyfire managed tokens.

**Apify SDK (Python)** — `from apify_client import ApifyClient`, then `client.actor("shelvick/sec-insider-trades").call(run_input=...)` and iterate the dataset (see the example above).

**REST API** — `POST /v2/acts/shelvick~sec-insider-trades/run-sync-get-dataset-items?token=...` for synchronous runs; the async `/runs` endpoint for large multi-company pulls that may exceed the 5-minute sync window.

### Pricing

A run stops delivering at its Max cost per run limit and returns the remaining units as uncharged rows with errorClass `budget_exhausted` (status `failed`); raise the limit and re-run only the inputs named in those rows.

Pay-per-event, billed only on success: one charge per insider filing that is fetched and parsed, after that record is pushed to the dataset. Companies that don't resolve, and filings that can't be retrieved or parsed, are **free**. Because billing is per filing, `maxFilings` is also your spend cap — a quick latest-activity scan is a handful of charges; a deep multi-company pull scales linearly and is bounded by your run's max-charge setting.

See the **Pricing** tab on this Store page for the current per-event rate and any active subscriber discounts.

### Behavior

**Run-level failures (rare)** — input validation only: an empty `identifiers` list, more than 50 identifiers, an unknown value in `formTypes`, or `maxFilings` out of the 1–100 range are rejected before any work.

**Per-record outcomes (common)** — each company/filing yields a record; failures are isolated and never charged. Failed records carry a clean `error` explanation and an `errorClass` for machine routing.

**Telemetry:** to improve reliability and parsing coverage, this Actor reports anonymous usage metrics and diagnostic events to the developer — run outcome counts, the requested form types, per-filing failure classes, and, only when something goes wrong, the relevant input fields. No account identifiers are collected, and telemetry never affects a run.

**Performance** — requests are paced under SEC's ~10 requests/second fair-access ceiling. Cost is one filing-list fetch per company plus one document fetch per filing, so wall-clock scales with total filings: a single company at the default `maxFilings` finishes in a few seconds; a 50-company pull at depth runs longer and may need the async endpoint rather than the 5-minute sync window.

### FAQ

**Do you charge for a company with no insider filings?**
No. A company that doesn't resolve, or has no filings of the requested type, returns a `failed` record and is not charged. You only pay per filing actually fetched and parsed.

**How do I get just buys, or just sells?**
The Actor returns all transactions in each filing; filter on `transactions[].transactionCode` (`P` = purchase, `S` = sale) or `acquiredDisposed` (`A`/`D`) in your own code. The per-filing `summary` already separates acquired vs disposed value.

**Why is a price `0` on some transactions?**
Grants, gifts, and certain exempt transactions have no market price — they're reported with a zero or absent price-per-share. Those rows still appear, with `transactionValue` of `0` or `null`.

**How far back can I go?**
The Actor reads the issuer's recent EDGAR filing window (the latest ~1000 filings) and takes the newest `maxFilings` after any `sinceDate`. Deep historical archives beyond that window aren't fetched in this version.

**Can I pass a CIK instead of a ticker?**
Yes — any mix of tickers and 10-digit CIKs (zero-padding optional). CIKs skip the ticker-map lookup.

### What this doesn't do

- **No real-time alerting.** It returns the current state of recent filings on demand; it isn't a streaming feed or a push notifier.
- **No analysis or signals.** It returns the transactions as filed — no buy/sell scoring, sentiment, or recommendations. It is not investment advice.
- **No Form 13D/13F or institutional holdings.** This is insider ownership (Forms 3/4/5), not activist stakes or fund holdings.
- **No full historical backfill.** Only the recent EDGAR filing window, not the entire multi-decade archive.
- **No deduplication of amended filings.** Amendments are returned as their own records; reconcile them yourself if needed.

For standardized company **financial statements** (income statement, balance sheet, cash flow) from SEC XBRL, use a fundamentals-normalization Actor instead. For company **enforcement and litigation history**, use a regulatory-records profiler. For arbitrary page-to-JSON extraction from non-EDGAR sources, use a structured-extraction Actor.

### Related Actors

| Actor | Use it when |
|---|---|
| [SEC Financials Normalizer](https://apify.com/shelvick/sec-financials-normalizer) | you need normalized income statement, balance sheet, and cash flow — not transactions |
| [Enforcement Record Profiler](https://apify.com/shelvick/enforcement-record-profiler) | you want the company's enforcement and violation history from official US sources |

# Actor input Schema

## `identifiers` (type: `array`):

Companies whose insider filings to fetch, as ticker symbols (e.g. AAPL, BRK.B) or 10-digit SEC CIK numbers (zero-padding optional). Tickers are resolved to CIK via SEC's official ticker map. Insider Form 3/4/5 filings are read from the issuer's EDGAR filing history. One dataset record is produced per filing. Public SEC data only; 1-50 companies per run.

## `formTypes` (type: `array`):

Which insider ownership form types to include. Valid values: 4 (changes in ownership — the open-market buys and sells), 3 (initial statement of holdings), 5 (annual statement of deferred/exempt transactions). Defaults to 4 only, which carries the buy/sell signal most users want. Values are validated at runtime; unknown values are rejected with a clear error.

## `maxFilings` (type: `integer`):

Maximum number of matching insider filings to fetch per company, newest first. Bounds both run time and cost — one charge per filing parsed. Increase for a deeper history; lower for a quick scan of the latest activity.

## `sinceDate` (type: `string`):

Optional ISO date filter (YYYY-MM-DD). When set, only filings filed on or after this date are returned, applied before the max-filings cap. Leave blank to take the most recent filings regardless of date. Useful for monitoring recent insider activity since a known point in time.

## Actor input object example

```json
{
  "identifiers": [
    "AAPL",
    "NVDA"
  ],
  "formTypes": [
    "4"
  ],
  "maxFilings": 20
}
```

# Actor output Schema

## `results` (type: `string`):

Parsed insider-filing records for this run.

## `output` (type: `string`):

Run-level outcome counts.

# 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 = {
    "identifiers": [
        "AAPL",
        "NVDA"
    ],
    "formTypes": [
        "4"
    ],
    "maxFilings": 20,
    "sinceDate": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("shelvick/sec-insider-trades").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 = {
    "identifiers": [
        "AAPL",
        "NVDA",
    ],
    "formTypes": ["4"],
    "maxFilings": 20,
    "sinceDate": "",
}

# Run the Actor and wait for it to finish
run = client.actor("shelvick/sec-insider-trades").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 '{
  "identifiers": [
    "AAPL",
    "NVDA"
  ],
  "formTypes": [
    "4"
  ],
  "maxFilings": 20,
  "sinceDate": ""
}' |
apify call shelvick/sec-insider-trades --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,shelvick/sec-insider-trades"
        }
    }
}
```

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/rClafNve0tyfWyFCf/builds/R9b2hFqbQDYAjK5hd/openapi.json
