# SEC Form 4 Insider Cluster Scanner (`johnveds15/sec-form4-insider-clusters`) Actor

Finds clusters of insider BUYING in SEC Form 4 filings — 3+ distinct insiders purchasing the same issuer inside a rolling window — with the 10b5-1 affirmative-defence flag, officer titles, ten-percent-owner status and footnote text that summary-table scrapers miss.

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

## Pricing

from $0.40 / 1,000 form 4 filings

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 Cluster Scanner

**Finds groups of corporate insiders buying their own company's stock at the same time — and tells you whether the buying was pre-scheduled or a live decision.** It reads SEC Form 4 filings straight from EDGAR, groups open-market purchases by issuer inside a rolling window, and returns each cluster with the officers' titles, the dollars, the share bought under a Rule 10b5-1 plan, and a link to every filing behind it. Built for retail and independent investors, newsletter writers, and quant hobbyists who want insider-buying signal without paying for a terminal.

### What a cluster is

A **cluster** is an issuer where **several different insiders** — distinct people, not one person filing twice — each made an **open-market purchase** of the company's stock inside the same window. One director buying is routine. A chief operating officer, a co-CFO, a chief strategy officer and four directors all buying in the same week is an event. You set the thresholds: how many insiders, how many dollars in aggregate, how many days wide the window is. The Actor returns the clusters that clear them, largest first.

### A real result

This is an actual record the Actor produced from live EDGAR data. Seven insiders at Star Bulk Carriers bought $6.75 million of stock on a single day, and **none of it was pre-scheduled**:

```json
{
  "type": "cluster",
  "ticker": "SBLK",
  "issuerName": "Star Bulk Carriers Corp.",
  "insiderCount": 7,
  "txCount": 8,
  "totalDollar": 6750876.0,
  "firstBuy": "2026-09-15",
  "lastBuy": "2026-09-15",
  "plan10b51DollarPct": 0.0,
  "officerTitles": ["Chief Operating Officer", "Chief Strategy Officer", "Head of Operations", "co CFO"],
  "hasTenPercentOwner": false,
  "insiders": [
    {
      "name": "Pappas Alexandros",
      "role": "Head of Operations",
      "shares": 74400.0,
      "dollar": 2103288.0,
      "is10b51": false,
      "filingUrls": ["https://www.sec.gov/Archives/edgar/data/2107497/000210749726000002/"]
    },
    { "name": "Zagari Raffaele", "role": "Director", "dollar": 1696200.0, "is10b51": false, "...": "..." },
    { "name": "Spyrou Symeon", "role": "co CFO", "dollar": 424050.0, "is10b51": false, "...": "..." },
    { "name": "Reskos Nikolaos", "role": "Chief Operating Officer", "dollar": 282700.0, "is10b51": false, "...": "..." }
  ],
  "windowDays": 21,
  "scanEndDate": "2026-09-18"
}
```

This example was produced with a 21-day window and a $50,000 aggregate threshold — wider than the defaults of 14 days and $250,000 in the Input table below — which is why the record shows `"windowDays": 21`. The same cluster clears the defaults comfortably: seven insiders and $6.75 million inside a single day.

Every insider carries the EDGAR URL of the filing the numbers came from. Nothing in a record is inferred; it is read from the filing XML.

### What makes it different from the incumbents

Most Form 4 scrapers read the summary table. This one parses the filing XML, so it can output four things they cannot:

| Field | Why it matters |
|---|---|
| `plan10b51DollarPct` | Share of the cluster's dollars bought under a Rule 10b5-1 plan. A pre-scheduled purchase carries **no** signal about what the insider knows today. A cluster that is 90% 10b5-1 is noise wearing a signal's clothes. Read from the form-level affirmative defense flag **and** per-transaction footnote references. |
| `officerTitles` | CFO buying is not the same event as a non-employee director buying. |
| `hasTenPercentOwner` | A 10% holder topping up is a different trade from an officer's first open-market purchase. |
| `insiders[].filings` / `filingUrls` | Every claim links back to the EDGAR document it came from. |

Footnote text is parsed, not skipped — that is where the 10b5-1 disclosure usually lives when the form-level flag is absent.

### Output

One dataset, two record shapes, both in the **Clusters** and **Transactions** table views:

- **`type: "cluster"`** — the product. One record per issuer that clears your thresholds, ranked by total dollars, with the fields shown above.
- **`type: "transaction"`** — every individual purchase parsed in the run, with the insider's name, title, ten-percent-owner status, date, shares, price, dollar value and 10b5-1 flag. On by default (`includeTransactions`), so a run always returns rows even in a quiet week; turn it off if you only want clusters.

### Input

| Field | Default | What it does |
|---|---|---|
| `secContactEmail` | the maintainer's address | The SEC requires every automated request to identify its sender; this goes in the `User-Agent` sent to sec.gov and nowhere else. **Replace it with your own address** — the SEC identifies the requester by it. |
| `lookbackDays` | 7 | How many days of EDGAR daily indices to scan. Seven always spans several filing days, including holiday weekends. |
| `windowDays` | 14 | Insiders must all have bought inside this many days to count as one cluster. |
| `minInsiders` | 2 | Distinct insiders required. Raise to 3 to filter harder. |
| `minAggregateUsd` | 250,000 | Total open-market dollars across the cluster. |
| `maxFilings` | 300 | Cap on filings parsed per run. Bounds both runtime and cost. Raise it for a full week's coverage. |
| `includeTransactions` | true | Also emit every parsed transaction. |
| `endDate` | today | Scan a past window by setting the last day (`YYYY-MM-DD`). |

Requests to EDGAR are paced at about 7.5 per second, under the SEC's published limit. That pacing is not configurable, by design.

### Pricing

You pay per event, so the bill tracks what the run actually did:

| Event | Price | Charged when |
|---|---|---|
| Actor start | $0.01 | once per run |
| Form 4 filing parsed | $0.0004 | per filing actually downloaded and parsed. Cached, missing or errored filings are not charged. |
| Insider cluster detected | $0.02 | per cluster returned — the output you came for |

**A realistic full-week run.** During testing, a six-day window with the threshold lowered to $50,000 parsed 1,400 filings and found 15 clusters: **$0.01 + 1,400 × $0.0004 + 15 × $0.02 = $0.87**. At the shipped $250,000 default, 7 of those 15 clusters qualify and the same run costs **$0.71**. Run it weekly and it is about $3 to $3.50 a month. A default run (300 filings) costs well under $0.20. Apify platform usage is billed separately and is small: this is a plain Python container with no browser.

### Why the margin is real

- **No headless browser.** EDGAR serves static `.idx` and `.xml` over plain HTTP. A competitor running Playwright pays for a browser image and the RAM to match, for identical bytes.
- **No residential proxy.** EDGAR does not block datacenter IPs. It asks for an identifying `User-Agent` and a request rate below 10/s, and it means it. This Actor sends a real contact address and holds ~7.5 req/s.
- **No paid data source.** Every byte is free from sec.gov.

The compute bill is a plain Python container and the data bill is zero. That is why the per-record price can be what it is.

### What this does not do

- **It is not investment advice.** A cluster is a fact about filings, not a recommendation. Insiders buy for many reasons, and a filed purchase is public information the market already has.
- **Buying only.** It counts open-market purchases (Form 4 transaction code P, non-derivative). It does not report sales, option exercises, grants, or gifts, and it does not compute net insider activity.
- **Form 4 only.** Form 3 (initial holdings) and Form 5 (annual) are not read.
- **Not real time.** It works from EDGAR's daily index, so a filing appears in the day's index, not the moment it is accepted. Weekends and federal holidays have no filings.
- **One issuer, one window.** It does not track an insider across companies or build a history; run it with `endDate` for past windows.

### Using it

Set your `secContactEmail`, keep the other defaults for a first run, and start it. Results are in the default dataset in the **Clusters** view; download as JSON or CSV, or call it from the API and read the dataset in code. Schedule it weekly to get a standing list of the companies whose officers put their own money in that week — and how much of it was decided rather than pre-programmed.

# Actor input Schema

## `secContactEmail` (type: `string`):

The SEC's fair-access policy requires every automated request to identify its sender. This address is placed in the User-Agent header and is sent ONLY to sec.gov — never stored, never shared. EDGAR returns 403 without it. Defaults to the Actor owner's address so the platform's automated daily test can run; replace it with your own when you run this yourself — the SEC identifies the requester by it.

## `lookbackDays` (type: `integer`):

How many days of EDGAR daily indices to sweep, counting back from End date.

## `endDate` (type: `string`):

Last day of the scan window. Defaults to today. EDGAR publishes no index on weekends or federal holidays; those days are simply skipped.

## `windowDays` (type: `integer`):

Rolling window in which separate insiders must all have bought to count as one cluster.

## `minInsiders` (type: `integer`):

How many DIFFERENT people (distinct reporting-owner CIKs) must buy before it is a cluster. Two co-founders buying together is noise; five officers is a signal.

## `minAggregateUsd` (type: `integer`):

Total dollar value of open-market purchases (transaction code P, non-derivative) across the cluster.

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

Hard cap on XML documents fetched this run. Directly bounds both runtime and your pay-per-event cost. Default 300 keeps the worst-case run (a holiday weekend, 4,000+ filings indexed) well inside Apify's 5-minute automated-test window; 500 measured 282 s on a laptop, too close to call.

## `includeTransactions` (type: `boolean`):

Push individual insider transactions to the dataset in addition to clusters. Much larger output; no extra charge. On by default so a run with no cluster above threshold still ships records — clusters are sparse on a holiday weekend, transactions are not.

## Actor input object example

```json
{
  "secContactEmail": "you@example.com",
  "lookbackDays": 7,
  "windowDays": 14,
  "minInsiders": 3,
  "minAggregateUsd": 500000,
  "maxFilings": 2000,
  "includeTransactions": true
}
```

# Actor output Schema

## `clusters` (type: `string`):

Records with type=cluster: one per issuer where minInsiders+ distinct insiders bought inside the window. Carries plan10b51DollarPct, officerTitles, hasTenPercentOwner and per-insider EDGAR filing URLs. Field shapes are defined in dataset\_schema.json.

## `transactions` (type: `string`):

Records with type=transaction: every non-derivative Form 4 transaction parsed this run, with officer title, ten-percent-owner status and the per-transaction 10b5-1 flag.

## `summary` (type: `string`):

filingsIndexed, filingsParsed, parseOutcomes, clusters, and the pay-per-event counts charged this run.

# 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 = {
    "secContactEmail": "you@example.com",
    "lookbackDays": 7,
    "windowDays": 14,
    "minInsiders": 3,
    "minAggregateUsd": 500000,
    "maxFilings": 2000,
    "includeTransactions": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("johnveds15/sec-form4-insider-clusters").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 = {
    "secContactEmail": "you@example.com",
    "lookbackDays": 7,
    "windowDays": 14,
    "minInsiders": 3,
    "minAggregateUsd": 500000,
    "maxFilings": 2000,
    "includeTransactions": False,
}

# Run the Actor and wait for it to finish
run = client.actor("johnveds15/sec-form4-insider-clusters").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 '{
  "secContactEmail": "you@example.com",
  "lookbackDays": 7,
  "windowDays": 14,
  "minInsiders": 3,
  "minAggregateUsd": 500000,
  "maxFilings": 2000,
  "includeTransactions": false
}' |
apify call johnveds15/sec-form4-insider-clusters --silent --output-dataset

```

## MCP server setup

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

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/cEuMyu7mmDCSFAHNZ/builds/mlFoPErpBzwqRTHfX/openapi.json
