# Insider Purchase Cluster Monitor - SEC Form 4 (`maydit/insider-purchase-cluster-monitor`) Actor

Extract SEC Form 4 purchases with source evidence, ownership details and conservative insider cluster counts. Bounded watchlists, saved comparisons and CSV/JSON output.

- **URL**: https://apify.com/maydit/insider-purchase-cluster-monitor.md
- **Developed by:** [Brandt May](https://apify.com/maydit) (community)
- **Categories:** Automation
- **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 purchase observations

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

![SEC Form 4 purchase evidence, observed insider counts and saved comparisons](https://api.apify.com/v2/key-value-stores/nemaYfVcrTgloz8Wn/records/insider-purchase-cluster-monitor-c7ac13850b5a.png?signature=gCFjjiocicbB9G3wzsze)

### Follow disclosed insider purchases

Build an issuer research timeline from SEC Form 4 purchase disclosures. See who reported a purchase, the shares and reported price, and whether multiple officers or directors appear in the inspected purchase window. Designed for analysts and research teams reviewing insider activity alongside company filings.

[Open this Actor in Console](https://console.apify.com/actors/1lDc6DDHL1Wcl3k9x/input) · [First run](#try-this-actor) · [Results](#result-preview) · [Saved comparisons](#build-a-repeatable-workflow) · [Full guide](#full-guide)

### Try this Actor

Start with one historical Occidental Petroleum purchase. The June 23, 2026 transaction reports **4,770 shares at 52.38 per share**. This fixed SEC example demonstrates the output; its date stays attached to the observation. No SEC key or paid source subscription is required.

**Inspect an SEC Form 4 purchase** — paste this input into the Console JSON editor.

```json
{
  "filingUrls": [
    "https://www.sec.gov/Archives/edgar/data/797468/000162828026045313/wk-form4_1782342238.xml"
  ],
  "maxResults": 5
}
```

Open the dataset to review the purchase and follow `sourceUrl` to the filing. Then open **Run report** or **SUMMARY** to see what was inspected. This one-insider sample does not establish a cluster.

**Read the code correctly:** SEC transaction code `P` covers an **open-market or private purchase**. It does not establish which of the two occurred.

### Result preview

One row is one purchase-coded, non-derivative transaction. The overview brings the research essentials together:

| Research question | Output fields |
| --- | --- |
| Which issuer and when? | `ticker`, `issuerName`, `transactionDate` |
| What was reported? | `shares`, `pricePerShare`, `reportedValue` |
| Who reported it? | `reportingOwners`, `ownershipNature` |
| Were other eligible insiders observed? | `observedDistinctInsiders`, `clusterDetected` |
| What changed since the saved observation? | `comparisonStatus`, `changedFields`, `previous` |
| Where is the evidence? | `sourceUrl`, `accessionNumber`, `observedAt` |

Cluster counts cover the inspected filings. The price’s currency is not inferred, and missing or nonnumeric prices remain null.

### Usage and pricing

Base result rate on the FREE tier: **$5 per 1,000 saved purchase observations**. The [Pricing tab](https://apify.com/maydit/insider-purchase-cluster-monitor/pricing) shows your plan’s result rate and the **Actor Start** charge. Set `maxResults` and a platform spending limit before running a larger watchlist. A result cap is an upper bound, not a promise of that many results.

Saved unchanged observations are billable. `onlyChanges` skips unchanged rows after a baseline; skipped rows, source errors and summary files are not dataset-item charges.

### Build a repeatable workflow

1. **Choose a small issuer watchlist.** Use the Occidental watchlist example below, then replace its CIK with your tickers or SEC CIKs. Review filing coverage before expanding the scope.
2. **Save a baseline.** Use the saved-comparison example below with `comparePrevious: true`. Keep the same input scope, `snapshotName` and named history store between runs.
3. **Review the changes.** Check `comparisonStatus`, `changedFields`, ownership details and the linked Form 4. A new accession/transaction line has its own identity.
4. **Deliver the evidence.** Export the completed dataset to your research sheet or connect it through your own workflow. Use `id` as the transaction key and retain the source link. After the baseline, `onlyChanges: true` can keep unchanged transactions out of later exports.

Run a comparison namespace sequentially. Check SUMMARY before interpreting an empty result or routing changes; source gaps cannot establish that an insider stopped buying.

### Full guide

#### Choose your evidence

Use either `companies` or `filingUrls`. A specified company query never silently falls back to the historical sample. Direct documents are inspected regardless of age. Company discovery searches only the SEC’s returned recent submissions index; it does not traverse archived index files.

- `companies`: up to 10 ticker symbols or SEC CIKs.
- `filingUrls`: up to 30 raw SEC archive XML URLs. SEC XSL viewer prefixes are normalized to raw XML.
- `lookbackDays`: bounds company discovery by filing date and company-watchlist purchases by transaction date.
- `maxFilingsPerCompany`: bounds matching filings before XML inspection. A company can appear as a reporting owner in another issuer’s filing; issuer mismatches are excluded after parsing, so a narrow cap can miss matches.
- `clusterWindowDays`, `minimumInsiders` and `onlyClusters`: select the observed cluster definition and optional output filter described below.

#### Field reference

| Field | Meaning |
| --- | --- |
| `issuerCik`, `issuerName`, `ticker` | Issuer identity reported in the ownership XML. Ticker may be missing or historical. |
| `transactionDate`, `transactionCode` | The transaction date and SEC code `P`. |
| `shares`, `pricePerShare`, `reportedValue` | Reported shares and price; value is shares multiplied by price, rounded to six decimals. Missing or nonnumeric price remains null. Currency is not inferred. |
| `reportingOwners` | Filing owners, CIKs, director/officer flags and officer titles. |
| `ownershipNature`, `sharesOwnedFollowing` | Direct/indirect ownership and the reported post-transaction amount. |
| `footnotes`, `remarks`, `rule10b51Flag` | Source qualifications and the filing’s plan flag when present. Footnotes apply at filing level; review the original for precise references. |
| `clusterDetected`, `observedDistinctInsiders` | Conservative count within the observed transaction window. |
| `sourceUrl`, `accessionNumber` | Raw SEC evidence and filing identifier. |
| `filingDate`, `acceptedAt`, `observedAt` | SEC discovery metadata when available, and our observation timestamp. Direct URLs have null filing/acceptance metadata; transaction dates are not publication times. |

Grants, sales, withholding, exercises, derivative transactions and holdings-only entries are excluded.

#### How the cluster count works

For each purchase, the window ends on its transaction date and includes that date plus the preceding `clusterWindowDays - 1` days. Only observed purchases for the same issuer and case-insensitively matching security title are considered. Counts use distinct owner CIKs, not numbers of transactions.

A counted owner must be a director or officer, be the sole reporting owner in that filing, and report direct ownership. Joint filings, indirect ownership and institutional-only/other ownership remain visible as purchase rows but do not increase the cluster count. Separate security titles are not assumed to represent the same class.

Form 4/A amendments are excluded and recorded in the run summary to prevent counting the same acquisition again. This release does not resolve amendment chains: an original filing may later be corrected. The output exposes that limitation. It does not estimate total company insider spending or infer that an insider stopped buying.

#### Saved comparison behavior

Enable `comparePrevious` and keep the same input scope, `snapshotName` and named store across scheduled runs. The first saved observation is `new`. Reobserved transactions are `changed` or `unchanged`, with previous values and changed fields. A newly filed transaction has its own accession/line identity.

`onlyChanges` skips unchanged rows and can correctly produce zero output. `onlyClusters` can also produce zero output—including the default one-insider example. Neither mode fabricates replacement rows. Saved state advances only after a row is confirmed written; spending limits do not consume unsaved history. Use one writer per comparison namespace.

#### Coverage, limits and failures

Open **Run report** for the readable outcome, **SUMMARY** for source-by-source coverage, or export **JSON/CSV** from the dataset. Filings are inspected before cluster enrichment so counts use the full inspected set even when fewer rows are saved. The filing cap, recent-index boundary, excluded amendments, failed sources and transaction-date bounds all limit what a cluster can establish.

Requests identify Maydit to the SEC and are serialized at approximately one per second. Requests have bounded time and response size; temporary server errors receive one retry. Access refusals are reported without bypassing them. An upstream failure never becomes a paid data row. An all-failed run fails honestly; an empty valid purchase filter succeeds with an explicit summary.

#### Three example inputs

The first exact example is [Inspect an SEC Form 4 purchase](#try-this-actor) above. The other saved examples are:

##### Review a bounded Occidental insider purchase window

Inspect up to 12 recent Form 4 filings for Occidental and extract eligible purchases within 365 days.

```json
{
  "companies": [
    "0000797468"
  ],
  "maxFilingsPerCompany": 12,
  "lookbackDays": 365,
  "maxResults": 10
}
```

##### Save an insider purchase comparison baseline

Save the documented Form 4 purchase and compare its observed cluster details on later runs. First run returns a new observation.

```json
{
  "filingUrls": [
    "https://www.sec.gov/Archives/edgar/data/797468/000162828026045313/wk-form4_1782342238.xml"
  ],
  "comparePrevious": true,
  "snapshotName": "oxy-purchase-example",
  "maxResults": 5
}
```

#### Research use and sources

Use the observations to prioritize filing review, maintain issuer timelines, or combine disclosed purchases with earnings and financing evidence. The Actor does not calculate buy/sell recommendations or predict share-price moves.

[SEC ownership transaction codes](https://www.sec.gov/edgar/searchedgar/ownershipformcodes.html) · [SEC Developer Resources](https://www.sec.gov/about/developer-resources) · [SEC automated access guidance](https://www.sec.gov/about/webmaster-frequently-asked-questions)

### Help with your run

Open **Run report** in the run’s Output tab for a readable outcome and **SUMMARY** for detailed coverage and source issues. Export CSV for flat spreadsheet columns or JSON to preserve nested evidence.

For help, open this Actor’s **Issues** tab with the run URL, relevant input and expected result. Remove credentials and private information before posting.

[Explore more Mayd-It Actors](https://apify.com/maydit)

# Actor input Schema

## `companies` (type: `array`):

Up to 10 US stock tickers or SEC CIKs. Searches each issuer’s recent submissions index within the lookback and filing cap. Does not traverse older index files. Choose this OR filing URLs. When both lists are empty, runs the documented historical SEC example.

## `filingUrls` (type: `array`):

Up to 30 raw SEC archive XML URLs; SEC XSL viewer prefixes are normalized to raw XML. Use either this list or companies. Direct documents are inspected regardless of age; their filing date and acceptance time stay null.

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

Bounds company discovery by filing date and company-watchlist purchases by transaction date. Direct historical XML examples are inspected regardless of age.

## `maxFilingsPerCompany` (type: `integer`):

Bounds matching recent filings before XML inspection. Filings where this CIK is only a reporting owner are excluded after parsing, so a narrow cap can miss issuer matches. SUMMARY reports the cap and every inspected source.

## `clusterWindowDays` (type: `integer`):

For each purchase date, counts distinct eligible insiders from that date back this many days inclusive. Counts only observed direct, single-owner officer/director purchases of the same issuer and security title.

## `minimumInsiders` (type: `integer`):

A cluster is an observed count, not complete market coverage. Joint filings, indirect ownership and non-officer/director ownership do not contribute.

## `onlyClusters` (type: `boolean`):

When enabled, the historical one-insider default may return zero rows. Zero is legitimate; no fallback data is created.

## `maxResults` (type: `integer`):

Maximum confirmed saved rows. One row is one eligible purchase transaction. Source failures appear in SUMMARY, not in paid results.

## `comparePrevious` (type: `boolean`):

First saved observations establish history and are marked new. Later runs show prior values and changed fields. Only confirmed saved rows update history.

## `onlyChanges` (type: `boolean`):

Enables comparison and saves only new or changed observations. A fully unchanged repeat saves zero rows. Start with a baseline run before using this filter.

## `snapshotName` (type: `string`):

1-80 letters, numbers, underscores or hyphens. Use the same namespace AND input scope across runs. Use one writer at a time; no removal inference.

## `stateStoreName` (type: `string`):

3-63 letters, numbers or hyphens. Used only when comparisons are requested. Choose a dedicated store for each workflow.

## `maxRunSeconds` (type: `integer`):

Requests, rate waits and history work share this budget, also bounded by the platform deadline minus 25 seconds. SEC requests are serialized at about one per second, with one retry for 502/503/504.

## Actor input object example

```json
{
  "companies": [],
  "filingUrls": [
    "https://www.sec.gov/Archives/edgar/data/797468/000162828026045313/wk-form4_1782342238.xml"
  ],
  "lookbackDays": 365,
  "maxFilingsPerCompany": 12,
  "clusterWindowDays": 30,
  "minimumInsiders": 2,
  "onlyClusters": false,
  "maxResults": 10,
  "comparePrevious": false,
  "onlyChanges": false,
  "snapshotName": "default",
  "stateStoreName": "maydit-insider-purchase-history",
  "maxRunSeconds": 240
}
```

# Actor output Schema

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

Successful public metadata observations only. Failures are never synthetic paid rows.

## `runReport` (type: `string`):

Readable coverage, limits and history outcome from this run.

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

Source coverage, actual counts, limits, failed sources and history outcome.

## `csvExport` (type: `string`):

Download the current run observations for spreadsheets.

# 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 = {
    "filingUrls": [
        "https://www.sec.gov/Archives/edgar/data/797468/000162828026045313/wk-form4_1782342238.xml"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maydit/insider-purchase-cluster-monitor").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 = { "filingUrls": ["https://www.sec.gov/Archives/edgar/data/797468/000162828026045313/wk-form4_1782342238.xml"] }

# Run the Actor and wait for it to finish
run = client.actor("maydit/insider-purchase-cluster-monitor").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 '{
  "filingUrls": [
    "https://www.sec.gov/Archives/edgar/data/797468/000162828026045313/wk-form4_1782342238.xml"
  ]
}' |
apify call maydit/insider-purchase-cluster-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maydit/insider-purchase-cluster-monitor"
        }
    }
}
```

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/1lDc6DDHL1Wcl3k9x/builds/WYJW5Ydvwuyg69B40/openapi.json
