# SEC Form 4 Insider Trades Scraper - Purchases and Sales (`groundplanes/sec-form4-insider-purchases`) Actor

Insider purchases (default) and sales from SEC Form 4 filings on EDGAR, newest first: issuer, ticker, insider name and role, shares, VWAP price, total value, shares owned after and cluster count. Filter by code, 10b5-1 plans, value and ticker. Data only, not advice.

- **URL**: https://apify.com/groundplanes/sec-form4-insider-purchases.md
- **Developed by:** [John](https://apify.com/groundplanes) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 insider transactions

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Form 4 Insider Trades Scraper - Purchases and Sales

Extract and aggregate open-market insider stock purchases (`P`) and sales (`S`) directly from SEC EDGAR Form 4 and Form 4/A XML ownership filings. Filter out pre-scheduled Rule 10b5-1 trading plans, enforce minimum dollar transaction thresholds, and identify multi-insider cluster buying across public companies.

**Purchases by default, sales optional.** The default input returns open-market purchases (`P`) only. Add `S` to `transactionCodes` to get sales too.

**Default runs cover the last 3 days.** With no dates given, the Actor scans the 3 days ending today, and the 240-second `maxScanSeconds` default typically reads every Form 4 in that window. On the busiest 3-business-day windows (roughly 1,600 or more filings) the default run may end `PARTIAL` at about 85% coverage; the status message always reports the exact scanned count, and raising `maxScanSeconds` (and the run timeout) scans the rest. Wider date ranges are scanned newest-first until the scan budget runs out and return a `PARTIAL` result; raise `maxScanSeconds` (and the run timeout) for fuller coverage of wide windows.

### What it does

Whenever a corporate officer, director, or 10% beneficial owner buys or sells shares of their company, they must file Form 4 with the U.S. Securities and Exchange Commission (SEC) within two business days. Raw Form 4 filings often split a single trade across multiple execution lines, mix option exercises or tax-withholding dispositions with open-market trades, and bury the Rule 10b5-1 checkbox inside the XML.

This Actor:

- Discovers Form 4 and Form 4/A filings from the official SEC EDGAR daily index across your date window.
- Parses the primary `<ownershipDocument>` XML (never scraping fragile HTML stylesheets).
- Aggregates multiple transaction lines per `accessionNumber + reportingOwnerCik + transactionCode` into a single row with summed `shares`, volume-weighted `avgPriceUsd`, `totalValueUsd`, `transactionCount`, `sharesOwnedAfter`, and `ownershipIncreasePct`.
- Reads the `aff10b5One` XML flag to mark or exclude pre-arranged Rule 10b5-1 plan trades.
- Calculates `clusterInsiderCount` (how many distinct insiders at the same issuer executed matching transactions during the window).

### Who it's for

- **Equity research and quantitative analysts** monitoring discretionary open-market insider buying and cluster purchases.
- **Investor relations and governance teams** tracking executive and director ownership changes.
- **Financial newsletters and alerting pipelines** building daily digests of high-conviction Form 4 filings.
- **B2B wealth and executive services** identifying corporate liquidity and equity acquisition events.

### Input

All filters have sensible defaults that return recent open-market insider purchases (`P`) of at least $10,000 outside Rule 10b5-1 plans.

- **`dateFrom`** *(string, YYYY-MM-DD)* — Start of the SEC filing date range. Defaults to 3 days before `dateTo`.
- **`dateTo`** *(string, YYYY-MM-DD)* — End of the SEC filing date range. Defaults to today (UTC).
- **`transactionCodes`** *(array of strings)* — `["P"]` (open-market purchases), `["S"]` (open-market sales), or `["P", "S"]`. Default: `["P"]`.
- **`exclude10b51Plans`** *(boolean)* — Exclude transactions flagged with `aff10b5One = 1`. Default: `true`.
- **`minTransactionValueUsd`** *(integer)* — Minimum aggregated transaction value in USD. Default: `10000`.
- **`minClusterInsiders`** *(integer)* — Minimum distinct insiders transacting in the same company during the window (`2` or higher isolates cluster buying). Default: `1`.
- **`tickers`** *(array of strings)* — Optional list of issuer ticker symbols (e.g. `["AAPL", "MSFT"]`). Default: `[]` (all issuers).
- **`includeDerivative`** *(boolean)* — Include `derivativeTable` transactions in addition to `nonDerivativeTable`. Default: `false`.
- **`secUserAgent`** *(string, secret)* — Optional custom SEC Fair-Access `User-Agent` header (`Organization contact@domain.com`). Uses the built-in compliant User-Agent when blank.
- **Related-entity duplicates:** funds often file the same trade under several related entities (the fund, its general partner, its manager). Rows with the same issuer, code, transaction date, shares and average price are merged into one, and the other filers are listed in `relatedReportingOwners`. They are not counted again in `clusterInsiderCount`.
- **Tickers:** placeholders such as `NONE` or `[NONE]` are returned as `null`.
- **`maxScanSeconds`** *(integer)* — Time spent reading individual filings, default `240`, typically enough to scan the full default 3-day window (the busiest windows may end `PARTIAL` at ~85% with the exact scanned count reported). Open-market purchases are a few percent of Form 4s; for wider windows the scan proceeds newest-first and may return a `PARTIAL` result — raise it (and the run timeout) for fuller coverage.
- **`maxItems`** *(integer)* — Maximum rows to return. Default: `200`.

#### Example input JSON

```json
{
  "dateFrom": "2026-10-16",
  "dateTo": "2026-10-23",
  "transactionCodes": ["P"],
  "exclude10b51Plans": true,
  "minTransactionValueUsd": 10000,
  "minClusterInsiders": 1,
  "tickers": [],
  "includeDerivative": false,
  "maxItems": 200
}
```

#### Example run options (default max charge $50)

When starting a run via the Apify API or Console, set a default maximum charge of **$50** (`"maxTotalChargeUsd": 50`) in your run options:

```json
{
  "maxTotalChargeUsd": 50
}
```

### Output

Results are sorted newest first by `filingDate` descending, then by `totalValueUsd` descending, and deduplicated by `accessionNumber + reportingOwnerCik + transactionCode`.

| Field | Type | Description |
|---|---|---|
| `accessionNumber` | `string` | 18-digit SEC accession number (with hyphens). |
| `filingDate` | `string` | SEC filing date (`YYYY-MM-DD`). |
| `issuerCik` | `string` | 10-digit zero-padded CIK of the public company. |
| `issuerName` | `string \| null` | Legal name of the public company. |
| `issuerTicker` | `string \| null` | Issuer trading symbol. |
| `reportingOwnerCik` | `string` | 10-digit zero-padded CIK of the reporting insider. |
| `reportingOwnerName` | `string \| null` | Name of the reporting insider as published on Form 4. |
| `reportingOwnerTitle` | `string \| null` | Officer title or relationship (`Director`, `10% Owner`, etc.). |
| `isDirector` | `boolean` | Whether the reporting owner is a director. |
| `isOfficer` | `boolean` | Whether the reporting owner is an officer. |
| `isTenPercentOwner` | `boolean` | Whether the reporting owner is a 10% beneficial owner. |
| `transactionDate` | `string \| null` | Execution date of the transaction (`YYYY-MM-DD`). |
| `transactionCode` | `string` | `P` (open-market purchase) or `S` (open-market sale). |
| `is10b51Plan` | `boolean \| null` | Value of `aff10b5One` (`true`, `false`, or `null` on pre-April 2023 filings). |
| `transactionCount` | `integer` | Number of individual transaction lines combined into this row. |
| `shares` | `number` | Total shares purchased or sold across combined lines. |
| `avgPriceUsd` | `number` | Volume-weighted average price per share in USD. |
| `totalValueUsd` | `number` | Total dollar value (`shares * avgPriceUsd`) in USD. |
| `sharesOwnedAfter` | `number \| null` | Beneficial shares held following the transaction. |
| `ownershipIncreasePct` | `number \| null` | Percentage change in beneficial holdings from this trade. |
| `clusterInsiderCount` | `integer` | Distinct insiders at the same issuer with matching trades in the window. |
| `filingUrl` | `string` | Direct URL to the primary Form 4 XML document on `sec.gov`. |

#### Sample output row (synthetic example)

```json
{
  "accessionNumber": "0001999999-26-000142",
  "filingDate": "2026-10-22",
  "issuerCik": "0001888888",
  "issuerName": "Example Industrial Corp",
  "issuerTicker": "EXIC",
  "reportingOwnerCik": "0001777777",
  "reportingOwnerName": "DOE ALEX R",
  "reportingOwnerTitle": "Chief Financial Officer",
  "isDirector": false,
  "isOfficer": true,
  "isTenPercentOwner": false,
  "transactionDate": "2026-10-21",
  "transactionCode": "P",
  "is10b51Plan": false,
  "transactionCount": 2,
  "shares": 5000,
  "avgPriceUsd": 24.5,
  "totalValueUsd": 122500.0,
  "sharesOwnedAfter": 45000,
  "ownershipIncreasePct": 12.5,
  "clusterInsiderCount": 2,
  "filingUrl": "https://www.sec.gov/Archives/edgar/data/1888888/000199999926000142/form4.xml"
}
```

### How pricing works

Pay per result: $0.003 per insider transaction row returned ($3.00 per 1,000), plus a tiny Apify actor-start fee. You only pay for rows returned. The Actor respects your run's maximum spending limit (recommended default: `"maxTotalChargeUsd": 50`) and stops cleanly if that limit is reached.

### Data source and terms

- **Official source:** [SEC EDGAR Daily Index & Ownership Filings](https://www.sec.gov/Archives/edgar/daily-index/) and [SEC Developer Resources & Fair Access Policy](https://www.sec.gov/os/webmaster-faq#developers).
- **Update frequency:** SEC EDGAR accepts and posts Form 4 filings continuously on business days (Monday–Friday, 6:00 AM to 10:00 PM ET, excluding federal holidays) and updates daily index files throughout each trading day.
- **Disclaimers:**
  - **Not investment advice.** Data is provided for informational and research purposes only and does not constitute financial, investment, or legal advice.
  - This Actor is an independent tool and is **not affiliated with or endorsed by the U.S. Securities and Exchange Commission (SEC)**.
  - Insider names and titles are public statutory disclosures required under Section 16(a) of the Securities Exchange Act of 1934; no personal contact information is collected or output.

### Limits and FAQ

- **Why did a narrow weekend date range return 0 rows?**
  SEC EDGAR does not process Form 4 filings on Saturdays, Sundays, or federal holidays. Use a window of at least 5–7 calendar days to span multiple trading days.
- **How is SEC rate limiting handled?**
  The SEC enforces a Fair Access limit of 10 requests/second. This Actor throttles requests to at most 6 requests/second and retries HTTP 429/5xx responses with exponential backoff and jitter, honoring the SEC's `Retry-After` header (up to 8 retries per request, with bounded total wait). If a filing still cannot be fetched after all retries, it is counted and named in the run status — never silently dropped.
- **Why are option exercises (`M`) or awards (`A`) excluded?**
  This scraper specifically targets open-market cash purchases (`P`) and sales (`S`), which carry stronger discretionary signal than routine equity grants or option exercises.

### Troubleshooting

- **My run ended `PARTIAL`.** The status message always names the exact reason and counts:
  - *Scan budget* — the date window held more filings than `maxScanSeconds` could read. Raise `maxScanSeconds` (and the run timeout) to scan more.
  - *Charge limit* — the run's `maxTotalChargeUsd` was reached while returning rows. Raise the limit to get more rows.
  - *Filings not fully read* — some filings could not be fetched from the SEC, or failed to parse (for example after an SEC format change). Those filings are excluded and counted in the status message rather than silently skipped.
- **A filing I expected is missing.** Check the run status first: if it reports filings that could not be fully read, yours may be one of them. A run that reads every filing in its window ends `Complete` and lists the exact filing count scanned.
- **Found a problem or need a field? Open an issue on the Issues tab; we respond quickly.**

If this keeps happening, please open an issue on this actor's Issues tab and we'll fix it fast.

### Changelog

- **v0.4 (2026-10-27):** Honest-failure hardening. Filings that fail to fetch (after up to 8 retries honoring `Retry-After`) or fail to parse are now counted and reported in the run status, and such runs end `PARTIAL` instead of `Complete`; every row is validated against the output schema before it is returned. The scan time budget is now honored even before the first matching row is found; reaching `maxItems` is reported as a `Complete` with the reason stated; charge-limit stops use the standard `PARTIAL` status. Default-window wording qualified (busiest windows may end `PARTIAL` at ~85%).

- **v0.3 (2026-10-27):** Default lookback window narrowed from 7 days to 3 days (`dateFrom` left empty now means 3 days before `dateTo`), so the default run scans its full window and returns `Complete`; `maxScanSeconds` default is 240 seconds (raised from 60 in v0.2).

- **v0.1 (2026-10-23):** Initial release with EDGAR daily index discovery, single-pass `<ownershipDocument>` XML extraction, Rule 10b5-1 filtering, volume-weighted aggregation, and cluster-insider counting.

# Actor input Schema

## `dateFrom` (type: `string`):

Start of SEC filing date window in YYYY-MM-DD format. Leave empty for today minus 3 days.

## `dateTo` (type: `string`):

End of SEC filing date window in YYYY-MM-DD format. Leave empty for today.

## `transactionCodes` (type: `array`):

Form 4 transaction codes to include: P (open-market purchase, default) and/or S (open-market sale).

## `exclude10b51Plans` (type: `boolean`):

When true, excludes transactions executed under a pre-arranged Rule 10b5-1 trading plan (aff10b5One = 1) so results focus on discretionary insider trades.

## `minTransactionValueUsd` (type: `integer`):

Minimum aggregated dollar value (shares \* price) per filing/owner/transactionCode.

## `minClusterInsiders` (type: `integer`):

Set to 2 or higher to keep only companies where at least that many distinct insiders transacted in the date window.

## `tickers` (type: `array`):

Optional list of stock ticker symbols to filter (e.g. \["AAPL", "NVDA"]). Leave empty for all public issuers.

## `includeDerivative` (type: `boolean`):

When false (default), parses only nonDerivativeTable transactions and skips derivative-only filings.

## `secUserAgent` (type: `string`):

Optional custom SEC User-Agent header (Organization contact@domain.com). Uses the built-in compliant User-Agent when left blank.

## `maxScanSeconds` (type: `integer`):

How long to spend reading individual Form 4 filings. The 240-second default typically covers the default 3-day date window inside the Actor's 300-second run timeout; on the busiest 3-business-day windows the scan may end PARTIAL with the exact scanned count reported in the run status. Open-market trades are only a few percent of all Form 4s. Raise this (and the run timeout) for fuller coverage.

## `maxItems` (type: `integer`):

Maximum number of insider transaction rows to return.

## Actor input object example

```json
{
  "transactionCodes": [
    "P"
  ],
  "exclude10b51Plans": true,
  "minTransactionValueUsd": 10000,
  "minClusterInsiders": 1,
  "tickers": [],
  "includeDerivative": false,
  "maxScanSeconds": 240,
  "maxItems": 200
}
```

# Actor output Schema

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

No description

# 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 = {
    "transactionCodes": [
        "P"
    ],
    "exclude10b51Plans": true,
    "minTransactionValueUsd": 10000,
    "minClusterInsiders": 1,
    "tickers": [],
    "includeDerivative": false,
    "maxItems": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("groundplanes/sec-form4-insider-purchases").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 = {
    "transactionCodes": ["P"],
    "exclude10b51Plans": True,
    "minTransactionValueUsd": 10000,
    "minClusterInsiders": 1,
    "tickers": [],
    "includeDerivative": False,
    "maxItems": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("groundplanes/sec-form4-insider-purchases").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 '{
  "transactionCodes": [
    "P"
  ],
  "exclude10b51Plans": true,
  "minTransactionValueUsd": 10000,
  "minClusterInsiders": 1,
  "tickers": [],
  "includeDerivative": false,
  "maxItems": 200
}' |
apify call groundplanes/sec-form4-insider-purchases --silent --output-dataset

```

## MCP server setup

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

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/t75ze2yXU3cmbRGbM/builds/KGnSMGTn5724tOerE/openapi.json
