# Insider Trading API

> Track SEC Form 4 insider filings: buys, sells, awards, and exercises by officers, directors, and 10%+ shareholders, with cluster buy detection.

Base URL: `https://app.sentisense.ai/api/v1/insider`
Tier: PRO (free preview available)

All API access requires an API key via the `X-SentiSense-API-Key` header. Keys look like `ss_live_...`.

> SDKs: [Python](https://github.com/SentiSenseApp/sentisense) · [Node](https://github.com/SentiSenseApp/sentisense-node)

## Overview

The Insider Trading API provides access to SEC Form 4 filing data: the mandatory disclosures that company insiders (officers, directors, and 10%+ shareholders) must file within 2 business days of any stock transaction. This is the same data that services like OpenInsider, Finviz Elite, and GuruFocus charge $70-200/month for.

**Use cases:**
- Track insider buying and selling across your portfolio or the entire market
- Detect cluster buy signals: when 3+ insiders buy the same stock within a short window (a historically bullish signal)
- Filter by transaction type (open market purchases vs. awards/exercises) for cleaner signals
- Identify 10b5-1 pre-planned trades (less informative) vs. discretionary trades (more informative)
- Build quantitative strategies based on insider flow data
- Cross-reference insider activity with institutional flows (13F) and news sentiment

**Transaction types:** `BUY`, `SELL`, `EXERCISE` (option/derivative exercise), `AWARD` (grant from company), `GIFT`, `OTHER`. This is a simplified rollup of the SEC's one-letter transaction codes, and several codes collapse onto one type. Read `transactionCode` when the distinction matters: `SELL` covers open-market sales (code `S`) alongside dispositions that never reach the market, such as tax withholding at vest (code `F`) and dispositions back to the issuer (code `D`).

**Tax withholding (code `F`):** when a stock grant vests, the issuer withholds a slice of the shares to cover the insider's tax bill. It files on Form 4 as a disposition, so it arrives here as `transactionType: "SELL"`, but no shares are sold into the market and the insider makes no decision. On companies that grant heavily this is the majority of reported "sold" dollars, so treating every `SELL` as a bearish signal will mislead you. Filter on `transactionCode !== "F"` to isolate discretionary selling.

**Insider relationships:** Each filer is classified as `OFFICER`, `DIRECTOR`, `TEN_PCT_OWNER`, or `OTHER`. A person can hold multiple roles simultaneously (e.g., both officer and director): the `officer`, `director`, and `tenPctOwner` boolean fields capture this.

**Access:** All endpoints require a PRO subscription for full data. FREE-tier callers see a limited preview (top 5 results for activity/trades, top 5 for cluster buys) with `isPreview: true` in the response.

Replacing OpenInsider? The documented equivalent, with working calls: [the OpenInsider API guide](/blog/openinsider-api/). Comparing trackers: [the best insider trading trackers in 2026](/blog/best-insider-trading-trackers-2026/).

---

## GET /activity

Returns market-wide insider activity aggregated by ticker, split into top insider **buys** and top insider **sells** by total dollar value. Each entry shows how many insiders traded, total shares, total value, and the most recent insider name.

**The sells rollup excludes tax withholding (code `F`).** Withheld shares never reach the market, so counting them here would rank companies by how generously they grant stock rather than by insider selling. Dispositions to the issuer (code `D`) are still counted. If you need every filed row, including withholding, use `/trades/{ticker}`, which returns the filings untouched.

**Authentication:** PRO required. Free users receive a preview of the top 5 results per direction.

**Parameters:**

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `lookbackDays` | integer | No | 90 | Number of days to look back. Must be between 1 and 365 (inclusive); returns 400 otherwise |

**Example Request:**

```bash
curl -H "X-SentiSense-API-Key: ss_live_YOUR_KEY" \
  "https://app.sentisense.ai/api/v1/insider/activity?lookbackDays=90"
```

```python
from sentisense import SentiSenseClient

client = SentiSenseClient(api_key="ss_live_YOUR_KEY")
activity = client.get_insider_activity(lookback_days=90)
for buy in activity.data["buys"]:
    print(f"{buy['ticker']}: {buy['insiderCount']} insiders bought ${buy['totalValue']:,.0f}")
```

**Response Schema:**

| Field | Type | Description |
|-------|------|-------------|
| `isPreview` | boolean | `true` when response is limited (FREE tier) |
| `previewReason` | string | Why the response is limited: `"PRO_REQUIRED"` or `null` |
| `upgrade` | object | Present only when `isPreview` is `true`. Carries `plan`, `message`, `price`, `url` and `relay`: surface `message` and `url` to your user in one line, then continue with the preview `data` |
| `data` | object | Contains `buys` and `sells` arrays (see below) |
| `data.buys` | array | Stocks with notable insider buying (see summary object below) |
| `data.sells` | array | Stocks with notable insider selling (see summary object below) |

**Activity summary object:**

| Field | Type | Description |
|-------|------|-------------|
| `ticker` | string | Stock ticker symbol |
| `companyName` | string | Company name |
| `tradeCount` | int | Number of individual transactions |
| `insiderCount` | int | Number of distinct insiders who traded |
| `totalShares` | long | Total shares traded |
| `totalValue` | long | Total dollar value of trades |
| `latestDate` | string | Date of most recent transaction |
| `latestInsider` | string | Name of most recent insider |
| `latestTitle` | string | Title/role of most recent insider |

**Example Response:**

```json
{
  "isPreview": false,
  "previewReason": null,
  "data": {
    "buys": [
      {
        "ticker": "JPM",
        "companyName": "JPMorgan Chase & Co",
        "tradeCount": 8,
        "insiderCount": 5,
        "totalShares": 125000,
        "totalValue": 28750000,
        "latestDate": "2026-03-15",
        "latestInsider": "Jamie Dimon",
        "latestTitle": "Chairman & CEO"
      }
    ],
    "sells": [
      {
        "ticker": "TSLA",
        "companyName": "Tesla, Inc.",
        "tradeCount": 3,
        "insiderCount": 2,
        "totalShares": 50000,
        "totalValue": 12500000,
        "latestDate": "2026-03-14",
        "latestInsider": "Vaibhav Taneja",
        "latestTitle": "CFO"
      }
    ]
  }
}
```

FREE tier: same shape with `isPreview: true`, `previewReason: "PRO_REQUIRED"`, and `data` truncated to top 5 per direction.

---

## GET /trades/{ticker}

Returns individual insider transactions for a specific stock, sorted newest-first. Each transaction includes the insider's name, title, transaction type, shares, price, value, security description, and whether it was a pre-planned 10b5-1 trade.

Every filed row is returned as filed, including tax withholding and other non-market dispositions. Unlike `/activity`, nothing is filtered out here, so read `transactionCode` and decide what counts as selling for your use case.

**Authentication:** PRO required. Free users receive a preview of the top 5 transactions.

**Parameters:**

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `ticker` | path | Yes | - | Stock ticker symbol (e.g., `AAPL`) |
| `lookbackDays` | integer | No | 90 | Number of days to look back. Must be between 1 and 365 (inclusive); returns 400 otherwise |

**Example Request:**

```bash
curl -H "X-SentiSense-API-Key: ss_live_YOUR_KEY" \
  "https://app.sentisense.ai/api/v1/insider/trades/AAPL?lookbackDays=90"
```

```python
client = SentiSenseClient(api_key="ss_live_YOUR_KEY")
trades = client.get_insider_trades("AAPL", lookback_days=90)
for t in trades.data:
    print(f"{t['transactionDate']} {t['insiderName']} {t['transactionType']} {t['sharesTransacted']} @ ${t['pricePerShare']}")
```

**Response Schema:**

All tiers return a unified wrapper: `{ isPreview: bool, previewReason: string|null, data: [...] }`. Access trades via `response.data`.

**Trade object fields:**

| Field | Type | Description |
|-------|------|-------------|
| `ticker` | string | Stock ticker symbol |
| `companyName` | string | Company name |
| `insiderName` | string | Name of the insider (e.g., "Tim Cook") |
| `insiderTitle` | string | Role/title (e.g., "Chief Executive Officer") |
| `insiderRelation` | string | `OFFICER`, `DIRECTOR`, `TEN_PCT_OWNER`, or `OTHER` |
| `officer` | boolean | Is the filer an officer? |
| `director` | boolean | Is the filer a director? |
| `tenPctOwner` | boolean | Is the filer a 10%+ shareholder? |
| `transactionDate` | string | Date of the transaction (ISO format) |
| `filedDate` | string | Date filed with SEC |
| `transactionCode` | string | Raw SEC transaction code, as filed: `P` (open market purchase), `S` (open market sale), `A` (award), `M` (exercise), `G` (gift), `F` (shares withheld by the issuer to cover taxes at vest), `D` (disposition to the issuer), etc. This is the authoritative field: use it whenever the distinction between codes matters. |
| `transactionType` | string | Simplified rollup of `transactionCode`: `BUY`, `SELL`, `EXERCISE`, `AWARD`, `GIFT`, `OTHER`. Lossy by design, several codes share one value. `SELL` includes code `F` withholding, which is not a market sale. |
| `securityTitle` | string | Security description (e.g., "Common Stock", "Class C Google Stock Units") |
| `securityBasis` | string \| null | The security the transaction was actually filed in, when that is not the US listing `ticker` names (e.g. `"Common Shares (2330.TW)"` on a TSM row). `null` on the ordinary case, which is nearly every row. |
| `sharesTransacted` | long | Number of shares traded |
| `pricePerShare` | double \| null | Price per share. Null for awards at $0, and null whenever `securityBasis` is set (see below). |
| `totalValue` | long | Total dollar value of the transaction |
| `sharesOwnedAfter` | long | Shares owned after this transaction |
| `directOwnership` | boolean | Direct (true) vs indirect (false) ownership |
| `rule10b51` | boolean | Whether this trade was under a confirmed Rule 10b5-1 pre-planned trading plan (false when parsing was ambiguous) |

**Foreign ordinary shares (`securityBasis`):** a company with a US listing sometimes files a transaction in its home-market ordinary shares rather than in the US-listed security. Those rows carry the filed security title in `securityBasis`, and `pricePerShare` is `null` for them on purpose: the filed price is per ordinary share, so comparing it with the US quote would be wrong (one ADS can represent several ordinary shares). `sharesTransacted` and `totalValue` are as filed and remain usable. `securityBasis` is a recent addition and is `null` on every row filed in the US-listed security, so existing readers are unaffected.

**Example Response:**

```json
{
  "isPreview": false,
  "previewReason": null,
  "data": [
    {
      "ticker": "AAPL",
      "companyName": "Apple Inc",
      "insiderName": "Tim Cook",
      "insiderTitle": "Chief Executive Officer",
      "insiderRelation": "OFFICER",
      "officer": true,
      "director": true,
      "tenPctOwner": false,
      "transactionDate": "2026-03-10",
      "filedDate": "2026-03-12",
      "transactionCode": "S",
      "transactionType": "SELL",
      "securityTitle": "Common Stock",
      "securityBasis": null,
      "sharesTransacted": 50000,
      "pricePerShare": 235.50,
      "totalValue": 11775000,
      "sharesOwnedAfter": 3200000,
      "directOwnership": true,
      "rule10b51": true
    }
  ]
}
```

---

## GET /cluster-buys

Returns cluster buy signals: stocks where **3 or more distinct insiders** purchased shares within the lookback period, a pattern long studied in academic research on insider activity.

**Authentication:** PRO required. Free users receive a preview of the top 5 signals.

**Parameters:**

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `lookbackDays` | integer | No | 90 | Number of days to look back. Must be between 1 and 365 (inclusive); returns 400 otherwise |

**Example Request:**

```bash
curl -H "X-SentiSense-API-Key: ss_live_YOUR_KEY" \
  "https://app.sentisense.ai/api/v1/insider/cluster-buys?lookbackDays=90"
```

```python
client = SentiSenseClient(api_key="ss_live_YOUR_KEY")
clusters = client.get_insider_cluster_buys(lookback_days=90)
for c in clusters.data:
    print(f"{c['ticker']}: {c['insiderCount']} insiders bought ${c['totalValue']:,.0f} worth")
```

**Response Schema:**

All tiers return a unified wrapper: `{ isPreview: bool, previewReason: string|null, data: [...] }`. Access clusters via `response.data`.

**Cluster buy object fields:**

| Field | Type | Description |
|-------|------|-------------|
| `ticker` | string | Stock ticker symbol |
| `companyName` | string | Company name |
| `insiderCount` | int | Number of distinct insiders who bought (always >= 3) |
| `tradeCount` | int | Total number of buy transactions |
| `totalShares` | long | Total shares purchased across all insiders |
| `totalValue` | long | Total dollar value of all purchases |
| `firstBuyDate` | string | Earliest buy date in the cluster |
| `lastBuyDate` | string | Most recent buy date in the cluster |

**Example Response:**

```json
{
  "isPreview": false,
  "previewReason": null,
  "data": [
    {
      "ticker": "BAC",
      "companyName": "Bank of America Corp",
      "insiderCount": 5,
      "tradeCount": 8,
      "totalShares": 250000,
      "totalValue": 10500000,
      "firstBuyDate": "2026-02-01",
      "lastBuyDate": "2026-03-12"
    },
    {
      "ticker": "WFC",
      "companyName": "Wells Fargo & Co",
      "insiderCount": 4,
      "tradeCount": 6,
      "totalShares": 180000,
      "totalValue": 12600000,
      "firstBuyDate": "2026-01-15",
      "lastBuyDate": "2026-03-08"
    }
  ]
}
```
