// guide

Insider Trading Webhooks: Real-Time SEC Form 4 Alerts

Updated 2026-06-16. By Theodor Nielsen, founder of Form4API.

// answer

To get real-time insider trading alerts, register a webhook pointing at a public HTTPS endpoint. Form4API then sends an HTTP POST within minutes of each new SEC Form 4 on EDGAR. Verify the X-Insider-Signature header — an HMAC-SHA256 of the raw body in the form sha256=<hex> — before trusting the payload, then return a 2xx quickly to acknowledge it.

Why webhooks beat polling

You can poll /v1/transactions on a timer — see the Python tutorial — but polling wastes calls when nothing has filed and adds latency equal to your poll interval. Webhooks invert that: you do work only when a filing actually lands, and you hear about it within minutes of EDGAR publication. For alerting, trade automation, or anything time-sensitive, push beats pull.

Register a subscription

Add a subscription on the dashboard webhooks page: a public HTTPS URL to deliver to, the events you want, and a signing secret (keep it somewhere safe — you will need it to verify deliveries). You can subscribe to new filings, cluster buys, and cluster sells; the X-Event-Type header on each delivery tells you which one fired. See the docs for the full event catalogue.

The request you receive

Each delivery is a POST with a JSON body and three headers you care about:

HeaderWhat it is
X-Insider-Signaturesha256=<hex> — HMAC-SHA256 of the raw body, keyed with your signing secret.
X-Event-TypeWhich event fired (a new transaction, a cluster buy, or a cluster sell).
X-Delivery-IdA unique ID for this delivery — use it to de-duplicate retries (idempotency).

The body is a compact JSON payload referencing the event — for a new-transaction event, the filing ID and accession number; fetch the full parsed transaction from /v1/transactions. Exact fields are in the docs.

Verify the signature (do this first)

Anyone who learns your URL could POST to it, so never act on a delivery you have not verified. Compute an HMAC-SHA256 of the raw request body with your signing secret, hex-encode it lowercase, prefix sha256=, and compare it to X-Insider-Signature in constant time. In Python:

import hmac
import hashlib

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    digest = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    expected = "sha256=" + digest
    # constant-time compare to avoid timing attacks
    return hmac.compare_digest(expected, signature_header or "")

The same check in Node:

const crypto = require("crypto");

function verify(rawBody, signatureHeader, secret) {
  const digest = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const expected = "sha256=" + digest;
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

The one mistake to avoid: hashing a re-serialized body. Frameworks that auto-parse JSON can change key order and whitespace; always hash the exact bytes you received, which is why the receivers below read the raw body explicitly.

Build a receiver

A minimal Flask receiver that reads the raw body, verifies the signature, and acknowledges fast:

import os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["FORM4API_WEBHOOK_SECRET"]

@app.post("/form4-webhook")
def on_event():
    raw = request.get_data()  # raw bytes, before JSON parsing
    if not verify(raw, request.headers.get("X-Insider-Signature"), SECRET):
        abort(401)

    delivery_id = request.headers.get("X-Delivery-Id")
    event_type = request.headers.get("X-Event-Type")
    if already_processed(delivery_id):      # idempotency — see below
        return "", 200

    enqueue_for_processing(event_type, request.get_json())  # do heavy work async
    return "", 200                          # acknowledge quickly

Express is the same shape — mount express.raw() on the route so you get the unparsed Buffer, verify, then res.sendStatus(200).

Retries & idempotency

A delivery is considered successful only if your endpoint returns a 2xx. If it does not, it is retried with a short back-off — roughly +2 minutes after the first failure and +16 minutes after the second — and then marked dead. Two consequences for your receiver:

  • Acknowledge fast. Return 2xx as soon as you have verified and stored the event; do parsing, enrichment, and trading logic asynchronously so a slow handler does not trigger a retry.
  • Be idempotent. A retry re-delivers the same event, so key your processing on X-Delivery-Id and skip anything you have already handled.

Because deliveries are dropped after the retries are exhausted, a long outage means gaps — reconcile by paging /v1/transactions for the window you were down.

Frequently asked questions

How do I get real-time insider trading alerts?

Register a webhook subscription pointing at a public HTTPS endpoint you control, choose the events you care about (new filings, cluster buys, cluster sells), and your endpoint receives an HTTP POST within minutes of the underlying SEC Form 4 being published on EDGAR. This is faster and cheaper than polling a transactions endpoint on a timer, because you only do work when something actually happens. Each delivery is signed so you can confirm it really came from Form4API before acting on it.

How do I verify a Form4API webhook signature?

Every delivery includes an X-Insider-Signature header of the form sha256=<hex>. Compute an HMAC-SHA256 of the raw request body using your subscription’s signing secret, hex-encode it in lowercase, prefix it with "sha256=", and compare it to the header using a constant-time comparison. Critically, sign the raw bytes you received — not a re-serialized version of the parsed JSON — because any change in key order or whitespace will change the hash and the check will fail. Reject the request if the signatures do not match.

What happens if my webhook endpoint is down?

Deliveries that do not return a 2xx status are retried with a short back-off — roughly two minutes after the first failure, then about sixteen minutes after the second. If it still fails, the delivery is marked dead and dropped, so a long outage means missed events. Return a 2xx quickly (do heavy work asynchronously), and if you need to backfill anything you missed during downtime, page the /v1/transactions endpoint for the gap.

Are insider trading webhooks free?

Webhooks are available on the paid plans; the free tier is poll-only. The free tier still covers 15,000 API calls a month with no credit card, which is enough to evaluate the data and build a polling prototype before upgrading to push delivery.

Get API key

500 free requests / day. No credit card.

Get API key

View API docs

curl / JavaScript / Python examples for every endpoint.

View API docs

See pricing

Free, $49, $149, $499 — pick the tier that fits.

See pricing