# SEC Form 4 Insider Trades by Ticker: Shares, Price, Holdings (`yadroo/sec-form4-insider-trades`) Actor

Corporate insider buys and sells parsed out of SEC EDGAR Form 4 filings, one row per transaction: insider name and role, transaction code, date, shares, price per share, dollar value, shares held afterwards, option and RSU detail and Rule 10b5-1 flag. By ticker, issuer CIK or insider CIK.

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

## Pricing

from $2.10 / 1,000 insider transaction rows

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 Insider Trades by Ticker: Shares, Price, Holdings

Corporate insider buys and sells parsed out of SEC EDGAR Form 4 filings, one row per transaction: insider name and role, transaction code, date, shares, price per share, dollar value, shares held afterwards, option and RSU detail and Rule 10b5-1 flag. By ticker, issuer CIK or insider CIK.

A Form 4 is the report an officer, director or 10% holder of a US-listed company must file within two business days of buying or selling that company's stock. This actor reads those filings straight from the SEC's own EDGAR system — the full-text search index finds the submissions, and each submission's ownership XML is parsed into rows — so one row is one reported line: who traded, on what date, under which transaction code, how many shares, at what price, and how many shares they held afterwards. No API key, no account, no proxy and no browser; dates are UTC, and numbers come out as numbers.

> **Need it tailored to your workflow?** Missing a field, a filter, another source or a different output format? [Open an issue](https://apify.com/yadroo/sec-form4-insider-trades/issues) and describe what you need — we read every request and adapt the actor.

### Use cases

- **Insider-buying screen across the whole market**: run `mode: "market"` with `transactionCodes: ["P"]` for a month and get every open-market purchase anybody filed — the only Form 4 line that is a cash decision rather than compensation.
- **Watchlist monitoring on a schedule**: `sinceHours: 24` plus `onlyNew: true` on a daily schedule turns the actor into an insider-trade feed that never repeats a transaction it has already delivered.
- **One company's insider history**: `mode: "issuer"` with a ticker and a date window gives every officer, director and 10% holder who traded that stock, with the filing link next to each row.
- **Follow one person or fund across companies**: `mode: "insider"` with a reporting-owner CIK returns that filer's trades at every issuer they report for — board members of several companies, family trusts, venture funds.
- **Compensation research**: `transactionCodes: ["A", "M", "F"]` with `includeDerivative: true` returns awards, option and RSU exercises and the shares handed back for tax, including the derivative table with strike price, underlying security and expiry.
- **Only the trades that matter**: `minTransactionValueUsd: 1000000` drops the routine award and tax-withholding lines, so a watchlist of twenty names returns a handful of rows instead of hundreds.
- **Answering an agent's question**: "did anyone at NVDA buy recently?" is one small JSON input, and `fields` cuts the payload to the four columns the agent needs.

### Input

No field is required. `issuer` mode needs at least one entry in `tickers`, `insider` mode at least one entry in `insiderCiks`; `market` mode needs neither. The input form is prefilled with a working `issuer` example (NVDA and AAPL over nine months) — those are prefill values, not defaults, so an empty field stays empty.

| Field | Type | Default | Allowed values / notes |
|---|---|---|---|
| `mode` | string (enum) | `issuer` | `issuer`, `insider` or `market` — see [Modes](#modes). Strict: the platform refuses any other value before the run starts. |
| `tickers` | string\[] | empty | `issuer` mode, at most 25 per run. Exchange symbols (`NVDA`, `BRK-B` or `BRK.B`) or CIK numbers with or without leading zeros (`1045810`, `0001045810`). See [Company symbols](#company-symbols). |
| `insiderCiks` | string\[] | empty | `insider` mode, at most 25 per run. EDGAR CIK numbers only — see [Finding a reporting-owner CIK](#finding-a-reporting-owner-cik). |
| `since` | string | empty | Earliest **filing** date, `YYYY-MM-DD` (UTC). Empty together with `until` = the last 90 days. |
| `until` | string | empty | Latest filing date, `YYYY-MM-DD` (UTC), that day included. Empty = today. |
| `sinceHours` | number | empty | 1–8760. A moving window for schedules; while it is set it replaces `since`. |
| `onlyNew` | boolean | `false` | Remember delivered transactions between runs and write only unseen ones, keyed by accession number plus the position of the line inside the filing. The memory itself is kept per mode and per company/insider list, so two watchlists do not shadow each other and the same watchlist in two tasks shares one memory. |
| `transactionCodes` | string\[] | empty (every code) | One-letter SEC codes, or plain words like `buy`, `sell`, `grant`, `exercise`. See [Transaction codes](#transaction-codes). |
| `includeDerivative` | boolean | `true` | Keep the option, RSU and warrant table as well, marked `isDerivative: true`. |
| `includeHoldings` | boolean | `false` | Also write the lines that restate a position without a trade, as `rowType: "holding"`. |
| `includeAmendments` | boolean | `true` | Keep amended filings (`4/A`). Every row says which it came from in `formType` and `isAmendment`. |
| `minTransactionValueUsd` | number | empty | Keep only lines worth at least this much (`shares` × `pricePerShare` as reported). A price filed as `0` makes the value `0`, and a line filed without a price has no value at all — any floor above zero drops both. |
| `maxFilingsPerEntity` | number | `25` | 1–500, newest first. The per-company or per-insider cap; one filing is one request. In `market` mode there is no entity: the run keeps reading filings until `maxItems` rows are found, its own submission budget is used up (it grows with `maxItems`, at most 1,500 per run) or its time budget runs out — the status message says which. |
| `maxItems` | number | `50` | 1–2000. Hard cap on the rows this run writes, and therefore on what it costs. |
| `fields` | string\[] | empty (all) | Keep only these columns, **in this order**. `rowType`, `ticker`, `issuerName`, `insiderName`, `transactionDate`, `transactionCode`, `shares`, `url` and `fetchedAt` are always kept — they come first unless you list them, and a column you do list stays where you put it. Unknown names are reported in the status message. |

### Reference

#### Modes

| `mode` | Question it answers | What the run does |
|---|---|---|
| `issuer` | Who traded inside the companies I name? | Searches Form 4 submissions by issuer CIK, resolved from your tickers. |
| `insider` | Where did this person or fund trade? | Searches by reporting-owner CIK, so one filer is followed across every issuer. |
| `market` | Everything filed in this window | No company filter at all; with `transactionCodes: ["P"]` this is the open-market-buying screen. |

An off-list value never reaches the run: `mode` is an enum, so the platform refuses anything but `issuer`, `insider` or `market` with HTTP 400 before the run starts.

#### Transaction codes

`transactionCodes` is free text, so case and spaces do not matter and the plain words in the last column are accepted too. Anything else is refused with the closest codes named in the error — a near miss is never silently swapped for another code. Leave the list empty to keep every code.

| Code | Meaning | Also accepted as |
|---|---|---|
| `P` | Open-market or private purchase — cash out of the insider's pocket | `buy`, `buys`, `purchase`, `bought` |
| `S` | Open-market or private sale | `sell`, `sells`, `sale`, `sold` |
| `A` | Grant, award or other acquisition from the issuer | `grant`, `award`, `rsu` |
| `M` | Exercise or conversion of a derivative security held | `exercise` |
| `F` | Shares handed to the issuer to pay tax or an exercise price | `tax`, `withholding` |
| `D` | Disposition back to the issuer | `disposition` |
| `G` | Bona fide gift | `gift` |
| `C` | Conversion of a derivative security | `conversion` |
| `X` | Exercise of an in-the-money or at-the-money derivative security | — |
| `O` | Exercise of an out-of-the-money derivative security | — |
| `E` | Expiration of a short derivative position | — |
| `H` | Expiration or cancellation of a long derivative position | — |
| `L` | Small acquisition under Rule 16a-6 | — |
| `W` | Acquisition or disposition by will or the laws of descent | — |
| `Z` | Deposit into or withdrawal from a voting trust | — |
| `I` | Discretionary transaction inside an employee plan | — |
| `J` | Other acquisition or disposition | `other` |
| `K` | Transaction in an equity swap or similar instrument | — |
| `U` | Disposition in a tender of shares in a change-of-control transaction | — |

Screening tip: `P` and `S` are decisions about the stock. `A` and `F` are routine compensation and tax withholding, and they make up most Form 4 lines.

#### Company symbols

`tickers` is free text matched against the SEC's own ticker-to-CIK file (10,434 symbols on 2026-10-06). Accepted: any case (`nvda`), surrounding spaces, and both house styles of a class suffix (`BRK-B` and `BRK.B` mean the same company). A symbol the SEC file does not know is refused with the closest known symbols named in the error, never swapped for another company — so `NVDAA` fails with *did you mean "NVDA"?* instead of returning a different issuer. Funds, foreign private issuers, holding vehicles and insiders themselves often have no ticker at all: give their CIK number in the same field instead (any value made only of digits is read as a CIK).

#### Direct and indirect ownership

`ownershipType` is `direct` when the shares are held in the insider's own name, and `indirect` when they are held through a trust, a partnership, a fund or a family member; in that case `natureOfOwnership` carries the filer's own words ("By Trust", "By 401(k) Plan"). The two are reported as separate lines, so one filing often holds both.

#### Relationship flags

Every row carries the reporting owner's relationship to the company exactly as the filing states it: `isDirector`, `isOfficer`, `isTenPercentOwner`, `isOther`, with the officer's own job title in `officerTitle` and a readable join of the flags in `insiderRole` ("Director, Officer"). A filer may hold several of them at once.

#### Finding a reporting-owner CIK

A reporting owner — a person, a trust, a fund — has its own EDGAR CIK, and that number is what identifies them across every company they file for. Run `issuer` mode for a company you care about and read the `insiderCik` column of the rows; then put that number into `insiderCiks` with `mode: "insider"`. Names are not accepted there on purpose: two officers can share a name, and the wrong CIK is the wrong person.

### Examples

**One ticker, one quarter — the core job**

```json
{ "mode": "issuer", "tickers": ["NVDA"], "since": "2026-01-01", "until": "2026-09-30", "maxFilingsPerEntity": 10, "maxItems": 20 }
```

Every officer, director and 10% holder who traded NVDA in the window, with shares, price, dollar value and holdings afterwards.

**Insider buying across the whole market**

```json
{ "mode": "market", "transactionCodes": ["P"], "since": "2026-09-01", "until": "2026-09-30", "includeDerivative": false, "maxItems": 20 }
```

Only code `P` — insiders who paid cash for stock anywhere in the US market that month. Grants and tax withholding are left out.

**Executive sales across a peer group, with only the columns you need**

```json
{ "mode": "issuer", "tickers": ["AAPL", "MSFT"], "transactionCodes": ["S"], "includeDerivative": false, "since": "2026-01-01", "until": "2026-09-30", "maxFilingsPerEntity": 15, "maxItems": 20,
  "fields": ["insiderName", "transactionDate", "shares", "pricePerShare", "transactionValueUsd"] }
```

Share sales side by side for two companies. The always-kept columns come first, then your five in exactly that order.

**Option exercises and RSU grants, derivative table included**

```json
{ "mode": "issuer", "tickers": ["MSFT", "ORCL"], "transactionCodes": ["M", "A"], "includeDerivative": true, "since": "2026-01-01", "until": "2026-09-30", "maxItems": 20 }
```

The compensation side: `strikePrice`, `exerciseDate`, `expirationDate`, `underlyingSecurityTitle` and `underlyingShares` are filled on the derivative rows.

**Follow one insider across every company**

```json
{ "mode": "insider", "insiderCiks": ["0001696841"], "since": "2026-01-01", "until": "2026-09-30", "maxItems": 20 }
```

Every Form 4 that reporting owner filed, whichever issuer it was for.

**Only million-dollar sales**

```json
{ "mode": "issuer", "tickers": ["NVDA", "META", "AVGO"], "transactionCodes": ["S"], "minTransactionValueUsd": 1000000, "includeDerivative": false, "since": "2026-01-01", "until": "2026-09-30", "maxItems": 20 }
```

Value is `shares` × the price in the filing, so the small routine lines disappear.

**A daily feed that never repeats itself**

```json
{ "mode": "market", "sinceHours": 24, "onlyNew": true, "includeDerivative": false, "maxItems": 200 }
```

Put this on a schedule: every run writes only transactions no earlier run with the same mode and the same company or insider list has delivered.

### Output

One row per reported line of a Form 4. A real row from a cloud run (footnotes shortened here):

```json
{
  "rowType": "transaction",
  "ticker": "NVDA",
  "issuerName": "NVIDIA CORP",
  "issuerCik": "0001045810",
  "insiderName": "Teter Timothy S.",
  "insiderCik": "0001696841",
  "insiderRole": "Officer",
  "officerTitle": "EVP, General Counsel and Sec",
  "isDirector": false,
  "isOfficer": true,
  "isTenPercentOwner": false,
  "isOther": false,
  "coFilers": [],
  "securityTitle": "Common Stock",
  "transactionDate": "2026-09-21",
  "transactionCode": "S",
  "transactionCodeLabel": "Open-market or private sale",
  "acquiredDisposed": "D",
  "direction": "sell",
  "shares": 12483,
  "pricePerShare": 222.1932,
  "transactionValueUsd": 2773637.72,
  "sharesOwnedAfter": 2705637,
  "ownershipType": "indirect",
  "natureOfOwnership": "By Trust",
  "isDerivative": false,
  "strikePrice": null,
  "exerciseDate": null,
  "expirationDate": null,
  "underlyingSecurityTitle": null,
  "underlyingShares": null,
  "formType": "4",
  "isAmendment": false,
  "periodOfReport": "2026-09-21",
  "filedAt": "2026-09-23",
  "rule10b51Plan": true,
  "equitySwapInvolved": false,
  "footnotes": ["The reported transaction was effected pursuant to a Rule 10b5-1 trading plan adopted by the Reporting Person on May 22, 2026.", "Represents weighted average sales price. The shares were sold at prices ranging from $221.59 to $222.58. …"],
  "remarks": null,
  "accessionNumber": "0001696841-26-000014",
  "lineIndex": 0,
  "url": "https://www.sec.gov/Archives/edgar/data/1045810/000169684126000014/0001696841-26-000014-index.htm",
  "xmlUrl": "https://www.sec.gov/Archives/edgar/data/1045810/000169684126000014/wk-form4_1790196985.xml",
  "fetchedAt": "2026-10-06T23:05:44.918Z"
}
```

| Field | Type | Meaning |
|---|---|---|
| `rowType` | string | `transaction` (a reported trade) or `holding` (a restated position, off by default) |
| `ticker` | string | Trading symbol as the filing itself states it; empty for issuers without one |
| `issuerName`, `issuerCik` | string | The company and its EDGAR CIK |
| `insiderName`, `insiderCik` | string | The reporting owner and their EDGAR CIK — the number to use in `insiderCiks` |
| `insiderRole` | string | Readable join of the relationship flags, e.g. `Director, Officer` |
| `officerTitle` | string | The officer's job title as filed |
| `isDirector`, `isOfficer`, `isTenPercentOwner`, `isOther` | boolean | The relationship the filing reports |
| `coFilers` | object\[] | The other reporting owners when several filed one Form 4 together (`name`, `cik`); empty otherwise |
| `securityTitle` | string | What was traded, e.g. `Common Stock`, `Restricted Stock Unit` |
| `transactionDate` | string | Trade date, `YYYY-MM-DD` (UTC); empty on a holding line |
| `transactionCode` | string | The SEC's one-letter code — see [Transaction codes](#transaction-codes) |
| `transactionCodeLabel` | string | That code in plain words |
| `acquiredDisposed` | string | `A` (shares came in) or `D` (shares went out) |
| `direction` | string | `buy` for code `P`, `sell` for code `S`, `other` for every other code |
| `shares` | number | Shares in this line; empty on a holding line, which reports a position instead |
| `pricePerShare` | number | Price as filed — often a weighted average with a footnote; gifts and most awards are filed as `0` or with no price at all |
| `transactionValueUsd` | number | `shares` × `pricePerShare` — `0` when the price is filed as `0`, empty when the filing reports no price |
| `sharesOwnedAfter` | number | Shares of that security the owner held after the line |
| `ownershipType` | string | `direct` or `indirect` |
| `natureOfOwnership` | string | The filer's words for an indirect holding, e.g. `By Trust` |
| `isDerivative` | boolean | `true` for a line from the option / RSU / warrant table |
| `strikePrice` | number | Conversion or exercise price (derivative lines) |
| `exerciseDate`, `expirationDate` | string | Dates from the derivative table; empty when the filer put them in a footnote instead |
| `underlyingSecurityTitle`, `underlyingShares` | string, number | What the derivative converts into, and how many shares |
| `formType` | string | `4` or `4/A` |
| `isAmendment` | boolean | `true` for a `4/A` correction |
| `periodOfReport` | string | Period of report from the filing header |
| `filedAt` | string | Filing date (UTC) — what `since` / `until` / `sinceHours` filter on |
| `rule10b51Plan` | boolean | The filing's `aff10b5One` flag: the trade was made under a Rule 10b5-1 plan; empty when the filer did not state it |
| `equitySwapInvolved` | boolean | As coded in the filing |
| `footnotes` | string\[] | The footnote texts this very line points at |
| `remarks` | string | The filing's remarks block |
| `accessionNumber` | string | EDGAR submission id |
| `lineIndex` | number | Position of this line inside the filing; with `accessionNumber` it is the row's identity and the `onlyNew` key |
| `url` | string | The filing page on EDGAR |
| `xmlUrl` | string | The ownership XML this row was parsed from |
| `fetchedAt` | string | When the actor read the filing, ISO 8601 UTC |

Dataset views: **Insider transactions** (the buy/sell table), **Options, RSUs and warrants** (the derivative columns), **Filing and ownership detail** (accession, form, amendment flag, CIKs, 10b5-1, XML link).

### Use it from code / agents

```bash
curl -X POST "https://api.apify.com/v2/acts/yadroo~sec-form4-insider-trades/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"issuer","tickers":["NVDA"],"since":"2026-01-01","until":"2026-09-30","maxItems":20}'
```

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/sec-form4-insider-trades').call({
    mode: 'market',
    transactionCodes: ['P'],
    sinceHours: 24,
    maxItems: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/sec-form4-insider-trades").call(run_input={
    "mode": "insider",
    "insiderCiks": ["0001696841"],
    "since": "2026-01-01",
    "maxItems": 50,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

MCP: add `https://mcp.apify.com` to Claude / Cursor / any MCP client and call the `yadroo/sec-form4-insider-trades` tool with the same JSON input. For an agent, `fields` is the cheap trick: `{"mode":"issuer","tickers":["NVDA"],"transactionCodes":["P","S"],"fields":["insiderName","transactionDate","direction","shares","transactionValueUsd"],"maxItems":10}` returns rows small enough to put straight into a prompt.

### Pricing

Pay per event: **$0.001 per run start + $0.003 per dataset row** at the free tier. Volume tiers lower the row price automatically: $0.0027 (Bronze), $0.0024 (Silver), $0.0021 (Gold and above); the start event stays the same.

Worked examples at the free tier, start event included:

- 20 rows (one ticker, one quarter): $0.001 + 20 × $0.003 = **$0.061**
- 50 rows (the prefilled input, two tickers over nine months): $0.001 + 50 × $0.003 = **$0.151**
- 500 rows (a wide market-wide month): $0.001 + 500 × $0.003 = **$1.501**
- A run that finds nothing — no Form 4 in the window, or nothing matching your filters: **$0.001**, the start event alone

Every row written to the dataset is billed, and nothing else is: this actor writes no placeholder or "not found" marker rows, so an empty result costs only the start event. `maxItems` is the hard ceiling on both rows and cost. If you set Apify's **Maximum cost per run**, the run stops as soon as the next row would exceed it, keeps every row you have already paid for, and says so in its status message ("Stopped at your spending limit: N rows delivered").

### Limits & FAQ

**How fast is it?** Measured in the Apify cloud on 2026-10-06, run `UcHAF4hE16yrwCg6O`: 354 requests over 350 submissions in 74 seconds — a sustained **297 requests per minute** (4.95 per second), no throttling, 500 rows delivered, so about **420 rows a minute** market-wide. One search page covers up to 100 submissions and every submission is then one request, which is why the row rate depends on your filters: the prefilled two-ticker watchlist returns 50 rows in 10 seconds, while the market-wide open-market-buying screen manages about **60 rows a minute**, because only about one Form 4 line in fourteen is a code-`P` purchase (20 rows out of 86 submissions read, measured the same day). The widest input the schema allows — 2,000 rows market-wide — reads at most 1,500 submissions, roughly five minutes of requests, and so fits the 900-second default run timeout. With a narrow code or value filter a market-wide run usually uses up that submission budget before the row cap; its status message then says how many rows it saved and that a narrower time window goes further.

**The SEC's rate limit.** sec.gov's fair-access policy allows an automated client no more than 10 requests per second across all its machines and throttles the address temporarily above that; the block clears about ten minutes after the traffic drops. This actor paces itself with a single token bucket at 5 requests per second — half the ceiling — across every sec.gov host, retries included, and it sends the descriptive User-Agent the SEC asks for. If the address it runs on is throttled anyway, the run ends with a clear message asking you to start again in a few minutes rather than hammering the source.

**Does a run ever stop early?** Yes, and it says so. The run stops before its own timeout, keeps everything read so far and ends successfully with "Stopped before the run timeout: N rows saved"; the same happens at your maximum cost per run. To continue, narrow `until` to the day before the oldest row you got.

**How far back does it go?** Electronic Form 4 filing became mandatory at the end of June 2003, so mid-2003 to today is the honest range; a window ending before that returns nothing and the status says so. EDGAR's full-text search also answers at most 10,000 hits per query — a market-wide year has far more than that, so slice wide windows by month or quarter. The status message tells you when a run hit that cap.

**`since` / `until` filter the filing date, not the trade date.** An insider has two business days to report, so a window that ends on a Friday usually still misses that week's last trades, and `transactionDate` is normally 0–2 business days behind `filedAt`.

**Amendments.** `forms=4` on EDGAR matches the root form, so amendments (`4/A`) arrive together with first reports and `includeAmendments: true` keeps them. An amendment carries the numbers that now stand, and the original filing stays in the data too — a correction never silently overwrites history. Each row says which it was in `formType` and `isAmendment`.

**Prices and share counts are the filer's own.** `pricePerShare` is frequently a weighted average with a footnote explaining the price range. Gifts and most awards have no real price, and filers report that in two ways: a price of `0` — then `transactionValueUsd` is `0` as well — or an empty price, and then the row carries no `transactionValueUsd` at all. Both kinds disappear under any `minTransactionValueUsd` floor above zero, the `0` rows because their value is below it and the priceless rows because they have no value to compare. Nothing is restated, split-adjusted or recalculated here: you get the numbers as filed, plus `shares × pricePerShare`.

**One filing can be one row or fifteen.** A Form 4 may report several lines, and when several owners file one form together (a fund and its managers) it stays one row per line, with the other filers in `coFilers`. Holding lines — positions restated without a trade — are off by default because they answer a different question; switch on `includeHoldings` to get them as `rowType: "holding"`.

**What about personal data?** Form 4 exists to publish the insider's name, role and share count, and those are the fields returned. The ownership XML also carries the filer's mailing address block: it is deliberately not read, not emitted and not in any dataset view. No e-mail addresses, no phone numbers.

**Empty result or error?** An unusable input fails immediately with a message naming what to change (an unknown ticker with the closest known symbol, a CIK where a name was typed, a reversed date window). A valid input that simply has no filings, or none matching your filters, succeeds with zero rows and a status message explaining which it was.

**Questions or problems:** open an issue on the Issues tab of this actor.

***

Made by **Yadroo**. Sibling actors: [sec-edgar-filings](https://apify.com/yadroo/sec-edgar-filings) (the filing list this one starts from), [sec-company-financials](https://apify.com/yadroo/sec-company-financials) (XBRL facts per company), [federal-register-documents](https://apify.com/yadroo/federal-register-documents), [cisa-kev-vulnerabilities](https://apify.com/yadroo/cisa-kev-vulnerabilities), [openfda-records](https://apify.com/yadroo/openfda-records).

# Actor input Schema

## `mode` (type: `string`):

Three different questions, one per run. `issuer` is the usual one: name tickers and get every officer, director and 10% holder who bought or sold that stock. `insider` follows one person or fund across all the companies they report on, which is how you watch a serial buyer or a venture fund's board seats. `market` ignores companies altogether and returns everything filed in the window - with `transactionCodes: ["P"]` that is the classic open-market-buying screen across the whole US market.

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

Exchange tickers or issuer CIKs, used by `issuer` mode, at most 25 per run. Free text: `NVDA`, `BRK-B` or `BRK.B` (either spelling), or a CIK with or without leading zeros (`1045810`, `0001045810`). Tickers are resolved against the SEC's own ticker-to-CIK file, so case and spacing do not matter; an unknown symbol is refused with the closest known ones named in the error, never silently swapped for another company. Which issuers have no ticker and must be given as a CIK: see README, Reference.

## `insiderCiks` (type: `array`):

Used by `insider` mode, at most 25 per run. A reporting owner - a person, a trust or a fund - has its own EDGAR CIK, and that number is what identifies them across every company they file for. You get the number from the `insiderCik` column of any `issuer` run, which is the normal way to use this actor: find the person in a company run, then follow them. Names are not accepted here, because two officers can share a name and the wrong CIK is a wrong person.

## `since` (type: `string`):

Earliest filing date to look at, as `YYYY-MM-DD` (UTC). This is the date the Form 4 reached EDGAR, not the trade date - an insider has two business days to report, so a window that ends on a Friday usually still misses that week's last trades. Leave both dates empty and the run covers the last 90 days.

## `until` (type: `string`):

Latest filing date to look at, `YYYY-MM-DD` (UTC), the day itself included. Electronic Form 4 filings begin in June 2003; an earlier date returns nothing and says so in the status message instead of failing.

## `sinceHours` (type: `number`):

A moving window for a schedule: `24` for yesterday's filings, `168` for the past week. While it is set it replaces `Filed on or after`, so a scheduled run always asks for the fresh end of the record instead of re-reading the whole year. The SEC accepts Form 4 submissions on business days until 22:00 ET, so an overnight run on a Sunday legitimately finds nothing and ends with a status that says so.

## `onlyNew` (type: `boolean`):

Write only transactions no earlier run has delivered, remembered by accession number plus the position of the line inside the filing. Made for a schedule: with `sinceHours` it turns the actor into an insider-trade feed that never repeats itself. The memory is kept per mode and per list of companies or insiders named, so two watchlists keep separate memories, while the same list in two tasks shares one. An amended Form 4 (`4/A`) is a new accession number, so it arrives as a new row.

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

The SEC's one-letter codes for the kind of transaction; free text, and an empty list keeps every code. Plain words are accepted too - `buy` reads as `P` (purchase), `sell` as `S` (sale), `grant` as `A` (award), `exercise` as `M`, `tax` as `F` - and so is lower case; anything else is refused with the closest codes named, never guessed. All 19 codes and their words are in README, Reference. Screening tip: `P` alone is the signal most people want, because `A` and `F` are routine compensation.

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

Form 4 has two tables: ordinary shares and derivatives - stock options, restricted stock units, warrants, phantom units. Keep this on and both become rows, with the derivative ones carrying `strikePrice`, `exerciseDate`, `expirationDate` and the underlying security; they are marked `isDerivative: true` so you can split them later. Switch it off for a clean picture of actual share movements, which is what a buy/sell screen usually wants, since an option grant moves no stock.

## `includeHoldings` (type: `boolean`):

A Form 4 may also restate positions that did not move - unvested RSUs, shares in a family trust. Those lines carry no date, code or price, only a share count, and arrive as rows with `rowType: "holding"`. Off by default because they double the row count of some filings while answering a different question; switch them on when you need the full reported position rather than the trades.

## `includeAmendments` (type: `boolean`):

An amendment corrects an earlier Form 4 - a mistyped share count, an omitted holding. Keeping them on is the safer default for research: the amendment carries the numbers that now stand. Every row says which it came from in `formType` and `isAmendment`, and the original filing stays in the data too, so a correction never silently overwrites history. Switch this off for a feed where only first reports matter.

## `minTransactionValueUsd` (type: `number`):

Drops the small routine lines: `1000000` keeps only million-dollar trades, `50000` filters out odd-lot grants and tax withholding. The value compared is `shares` times `pricePerShare` as reported in the filing. Gifts, most grants and many derivative awards are filed with a price of `0` or with no price at all, so their value is `0` or missing and any floor above zero drops them - leave this empty when you are studying grants or gifts.

## `maxFilingsPerEntity` (type: `number`):

Newest first. Keeps one heavily traded company in a watchlist of twenty from eating the whole run, and bounds the request count: one filing is one request to EDGAR. A large-cap company files a few hundred Form 4s a year, so 25 covers roughly the last month for the busiest of them.

## `maxItems` (type: `number`):

Hard cap on the rows this run writes, and therefore on what it costs. One filing yields one to about ten rows, so 50 rows is roughly 15 filings. The run stops as soon as the cap is reached and the status message names how many rows were saved and from how many filings; to read the rest, set `Filed on or before` to the day before the oldest `filedAt` you received and run again.

## `fields` (type: `array`):

Keep only the named columns, in the order given - for example `ticker, insiderName, transactionDate, shares, pricePerShare`. Empty = every column. `rowType`, `ticker`, `issuerName`, `insiderName`, `transactionDate`, `transactionCode`, `shares`, `url` and `fetchedAt` are always present; name one of them here and it moves to the position you gave it. Unknown names are reported in the status message instead of being dropped in silence.

## Actor input object example

```json
{
  "mode": "issuer",
  "tickers": [
    "NVDA",
    "AAPL"
  ],
  "insiderCiks": [
    "0001696841"
  ],
  "since": "2026-01-01",
  "until": "2026-09-30",
  "onlyNew": false,
  "includeDerivative": true,
  "includeHoldings": false,
  "includeAmendments": true,
  "maxFilingsPerEntity": 25,
  "maxItems": 50
}
```

# 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 = {
    "tickers": [
        "NVDA",
        "AAPL"
    ],
    "insiderCiks": [
        "0001696841"
    ],
    "since": "2026-01-01",
    "until": "2026-09-30"
};

// Run the Actor and wait for it to finish
const run = await client.actor("yadroo/sec-form4-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 = {
    "tickers": [
        "NVDA",
        "AAPL",
    ],
    "insiderCiks": ["0001696841"],
    "since": "2026-01-01",
    "until": "2026-09-30",
}

# Run the Actor and wait for it to finish
run = client.actor("yadroo/sec-form4-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 '{
  "tickers": [
    "NVDA",
    "AAPL"
  ],
  "insiderCiks": [
    "0001696841"
  ],
  "since": "2026-01-01",
  "until": "2026-09-30"
}' |
apify call yadroo/sec-form4-insider-trades --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yadroo/sec-form4-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/rJtgIy7pWK6cHEmTU/builds/htowtcyE1oPXcqoMj/openapi.json
