> ## Documentation Index
> Fetch the complete documentation index at: https://api.unusualwhales.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> API requests use the base URL https://api.unusualwhales.com and require a bearer token in the `Authorization` header (`Authorization: Bearer <API_KEY>`). Create and manage API tokens at https://unusualwhales.com/dashboard/api.
> For live market data inside an AI tool, use the Unusual Whales MCP server at https://unusualwhales.com/public-api/mcp.
> Instructions for agents using Unusual Whales tools: https://unusualwhales.com/skill.md

# SEC insider trades

> Receive insider trade filings (forms 3, 4, 5 and 144) as they are processed, with the transaction rows of a filing that share a type aggregated into one message.

**NOTE:**
This is the documentation for the websocket channel `sec:insider_trades`.
Websocket access for personal use is only available through the [Advanced plan](https://unusualwhales.com/pricing?product=api).

You can find fully-functional examples that stream data from many channels here:

* Python: [https://github.com/unusual-whales/api-examples/tree/main/examples/ws-multi-channel-multi-output](https://github.com/unusual-whales/api-examples/tree/main/examples/ws-multi-channel-multi-output)
* Javascript: [https://github.com/unusual-whales/api-examples/tree/main/examples/ws-multi-channel-multi-output-nodejs](https://github.com/unusual-whales/api-examples/tree/main/examples/ws-multi-channel-multi-output-nodejs)

Connect to the websocket URI:

`wss://api.unusualwhales.com/socket?token=<YOUR_API_TOKEN>`

then `join` the `sec:insider_trades` channel.

Each message carries insider transactions reported on SEC forms 3, 4, 5 and 144, and on
their amendments, in aggregated form. A filing lists its transactions as rows. The rows
that one reporting person files for one ticker with the same filing date and transaction
date are aggregated into one message when they also share the form type, security title,
`security_ad_code`, `transaction_code`, direction (acquired or disposed), and the officer
title and role flags of the reporting person. Rows of different kinds therefore arrive as
several messages.

Three kinds of rows are left out: rows with an `amount` of zero, rows where the reporting
person disclaims ownership, and rows of a filing that a later filing amended.

For the individual rows, call
[`/insider/transactions`](https://api.unusualwhales.com/docs/operations/PublicApi.InsiderController.transactions)
with `group=false`. The `ids` field lists the rows a message aggregates.

### Delivery characteristics

* **Updates are event driven.** The channel stays silent until an insider trade filing is
  processed. There is no heartbeat and no fixed schedule.
* **A message can be sent again.** The aggregates of a reporting person for one ticker,
  filing date and transaction date are rebuilt together, and each rebuild sends them again.
  A newer message replaces every earlier one that shares an entry of `ids`.
* **The share counts can disagree with `amount`.** For a message that aggregates several
  rows, `shares_owned_before` and `shares_owned_after` can come from different rows, so
  `shares_owned_before + amount` can differ from `shares_owned_after`.
* **Form 144 messages carry fewer values.** Their `security_ad_code`, `security_title` and
  `director_indirect` are empty strings, and their `shares_owned_before` and
  `shares_owned_after` are `0`. A form 144 aggregate is not sent when a form 4 aggregate
  exists for the same ticker, reporting person, transaction date, `amount` and
  `transaction_code`.
* **The channel sends nothing when you join.** Messages sent while you are disconnected are
  not replayed.
* **The channel is global only.** It covers every ticker at once. A join on
  `sec:insider_trades:<TICKER>` is answered with an `{"error": "..."}` frame.

Payload format:

```
[
  "sec:insider_trades",
  {
    "ticker": "MSBI",
    "filing_date": "2026-10-01",
    "transaction_date": "2026-09-30",
    "formtype": "4",
    "owner_name": "FRANKLIN TRAVIS",
    "officer_title": "",
    "is_director": true,
    "is_officer": false,
    "is_ten_percent_owner": false,
    "security_ad_code": "DA",
    "transaction_code": "A",
    "shares_owned_before": 11881,
    "amount": 530,
    "shares_owned_after": 12411,
    "price": 32.4316,
    "security_title": "Common Share Equivalent",
    "director_indirect": "D",
    "natureofownership": "",
    "date_excercisable": "",
    "price_excercisable": 22.65,
    "expiration_date": "",
    "ids": [
      "be6cbb3e-8c75-4d4f-a905-51b00026d79a",
      "4221adf0-babe-4727-a3b7-957f03b656c9"
    ],
    "transactions": 2,
    "is_10b5_1": false,
    "reporter_is_public_company": false,
    "reporter_cik": "0002021449"
  }
]
```

### Field reference

Every field is present in every message. A value the filing does not provide is sent as an
empty string, `0` or `false`, never as `null`. A `0` can therefore mean either zero or not
reported. Dates are strings in `YYYY-MM-DD` format.

For `director_indirect`, `natureofownership`, `date_excercisable`, `price_excercisable` and
`expiration_date`, a message that aggregates several rows carries the highest value among
them. For text that is the last value in alphabetical order.

The names `natureofownership`, `date_excercisable` and `price_excercisable` are spelled as
shown.

<div className="field-table">
  | Field | Type | Description |
  | - | - | - |
  | `ticker` | string | Ticker of the company whose securities the insider traded. That company is the issuer of the filing. |
  | `filing_date` | string | Date the filing was published to the SEC. |
  | `transaction_date` | string | Date the transactions were executed. |
  | `formtype` | string | SEC form type. `3` is an initial statement of beneficial ownership, `4` a statement of changes in beneficial ownership, `5` an annual statement, and `144` a notice of a proposed sale of restricted or control securities, which is an intended sale and not a completed one. A `/A` suffix, as in `4/A`, marks an amendment of an earlier filing. |
  | `owner_name` | string | Name of the reporting person, without single-letter parts such as middle initials. |
  | `officer_title` | string | Officer title of the reporting person at the issuer, such as `Chief Executive Officer`. Empty string when there is none. |
  | `is_director` | boolean | Whether the reporting person serves on the issuer's board of directors. |
  | `is_officer` | boolean | Whether the reporting person is an officer of the issuer. |
  | `is_ten_percent_owner` | boolean | Whether the reporting person beneficially owns more than 10% of a class of the issuer's registered equity securities. |
  | `security_ad_code` | string | `NA` for a non-derivative security acquired, `ND` for a non-derivative security disposed of, `DA` for a derivative acquired and `DD` for a derivative disposed of. |
  | `transaction_code` | string | SEC transaction code, such as `P` for a purchase, `S` for a sale or `A` for a grant. |
  | `shares_owned_before` | int | Number of securities owned before the transactions. For an acquisition it is the smallest value among the aggregated rows, otherwise the largest. |
  | `amount` | int | Number of securities traded, summed over the aggregated rows. Positive for an acquisition, negative for a disposal. |
  | `shares_owned_after` | int | Number of securities owned after the transactions. For an acquisition it is the largest value among the aggregated rows, otherwise the smallest. |
  | `price` | float | Price per security, averaged over the aggregated rows and weighted by their `amount`, rounded to 4 decimal places. |
  | `security_title` | string | Name of the security, such as `Common Stock`. |
  | `director_indirect` | string | `D` when the reporting person owns the securities directly, `I` when indirectly. |
  | `natureofownership` | string | Explanation of an indirect ownership, such as `By Trust`. |
  | `date_excercisable` | string | Date a derivative becomes exercisable. |
  | `price_excercisable` | float | Exercise price of a derivative. |
  | `expiration_date` | string | Date a derivative expires. |
  | `ids` | string\[] | IDs of the rows this message aggregates. They are the `id` values that `/insider/transactions` returns with `group=false`. |
  | `transactions` | int | Number of rows this message aggregates. |
  | `is_10b5_1` | boolean | Whether the transactions were made under a Rule 10b5-1 plan, a prearranged plan that lets an insider trade on a preset schedule. |
  | `reporter_is_public_company` | boolean | Whether the reporting person is itself a company with a ticker. |
  | `reporter_cik` | string | SEC CIK of the reporting person. |
</div>

The payload has the fields of the Kafka message
[`InsiderTradeAgg`](/docs/kafka/types/InsiderTradeAgg), published to the topic
[`sec-filings`](/docs/kafka/topics/sec-filings) under the key `insider_trade_agg`.
