How to monitor 8-K filings in real time

8-K filings announce material events the moment they happen, earnings surprises, CEO departures, major contracts. This guide shows you how to subscribe to 8-K filings for a list of tickers, filter to the item numbers you actually care about, and receive a real-time push via webhook or poll on a tight schedule.

Want this data via API instead of reading about it? Get a free API key →

The problem

An 8-K can move a stock before any analyst publishes a note. The window between SEC acceptance and widespread awareness is often measured in seconds, not minutes. Polling EDGAR directly is possible, but their index files update in batches and their per-filing endpoints are rate-limited at 10 requests per second per IP.

The three item numbers that drive most of the alpha-relevant 8-Ks are:

  • Item 5.02, departure or appointment of directors and principal officers (CEO/CFO changes)
  • Item 2.02, results of operations (earnings releases, guidance updates)
  • Item 1.01, entry into a material definitive agreement (major contracts, partnerships, M&A)

Catching those three within seconds of filing is the whole task.

The approach

EdgarKit gives you two paths:

  1. Webhook, register an endpoint and EdgarKit pushes each matching filing to you as it's accepted. Best for production.
  2. Polling, hit the REST API on a schedule. Easier to prototype; fine for workflows where a 1-5 minute lag is acceptable.

This guide covers both. The webhook approach is the right long-term choice.

Step 1: Register a webhook for 8-K filings

Send a POST to /v1/webhooks with the form types and tickers you want to watch. You can also pass item_numbers to restrict to specific 8-K sections.

curl -X POST "https://api.edgarkit.com/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/sec-filings",
    "form_types": ["8-K"],
    "tickers": ["NVDA", "AAPL", "TSLA"],
    "item_numbers": ["5.02", "2.02", "1.01"],
    "secret": "your_webhook_signing_secret"
  }'

The response includes a webhook_id you'll use to update or delete the subscription later:

{
  "webhook_id": "wh_a1b2c3d4e5",
  "status": "active",
  "url": "https://your-server.com/webhooks/sec-filings",
  "form_types": ["8-K"],
  "tickers": ["NVDA", "AAPL", "TSLA"],
  "item_numbers": ["5.02", "2.02", "1.01"],
  "created_at": "2026-06-18T12:00:00Z"
}

Keep the secret value in your environment variables. EdgarKit uses it to sign every payload so you can verify it's actually from us, the webhook verification guide walks through the HMAC check.

Step 2: Understand the webhook payload shape

When a matching 8-K is accepted by the SEC, EdgarKit delivers a POST to your URL within seconds. Here's what the payload looks like:

{
  "event": "filing.new",
  "filing": {
    "id": "8k_0001234567_20260618",
    "form_type": "8-K",
    "issuer_ticker": "NVDA",
    "issuer_name": "NVIDIA Corporation",
    "issuer_cik": "0001045810",
    "filed_at": "2026-06-18T14:32:11Z",
    "period_of_report": "2026-06-18",
    "item_numbers": ["2.02", "9.01"],
    "accession_number": "0001045810-26-000123",
    "primary_document_url": "https://www.sec.gov/Archives/edgar/data/1045810/000104581026000123/nvda8k.htm",
    "filing_index_url": "https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=0001045810"
  },
  "webhook_id": "wh_a1b2c3d4e5",
  "delivered_at": "2026-06-18T14:32:14Z"
}

The item_numbers array contains every item declared in the filing. A single 8-K often covers multiple items (here, 2.02 earnings plus 9.01 financial statements). Your handler can match on whichever items you care about.

Step 3: Poll the API as an alternative

If you're not ready to expose a webhook endpoint, polling is a perfectly workable approach. Hit /v1/filings every 60 seconds with a since timestamp:

curl "https://api.edgarkit.com/v1/filings?form_type=8-K&ticker=NVDA,AAPL,TSLA&item_numbers=5.02,2.02,1.01&since=2026-06-18T14:00:00Z&limit=50" \
  -H "Authorization: Bearer YOUR_API_KEY"

Store the filed_at of the most recent result and use it as since on the next poll to avoid re-processing.

Step 4: Handle a 5.02 executive-change filing

Item 5.02 filings are particularly time-sensitive. Here's a minimal Python handler that checks incoming webhook payloads for 5.02 and logs the alert:

import hmac
import hashlib
import json
import os
from datetime import datetime

SIGNING_SECRET = os.environ["EDGARKIT_WEBHOOK_SECRET"]

def handle_payload(raw_body: bytes, signature_header: str) -> None:
    # Verify signature first (always do this before processing)
    expected = "sha256=" + hmac.new(
        SIGNING_SECRET.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(expected, signature_header):
        raise ValueError("Invalid webhook signature")

    payload = json.loads(raw_body)
    filing = payload["filing"]

    if "5.02" in filing.get("item_numbers", []):
        print(
            f"[{datetime.utcnow().isoformat()}] EXEC CHANGE: "
            f"{filing['issuer_ticker']} filed 8-K item 5.02 "
            f"at {filing['filed_at']}, {filing['primary_document_url']}"
        )

The HMAC verification step is non-optional. Anyone who knows your webhook URL could POST fake payloads without it.

Putting it together

Here's a complete Python polling script that checks every 60 seconds and fires an alert on matching 8-Ks:

import os
import time
import requests
from datetime import datetime, timezone

API_KEY = os.environ["EDGARKIT_API_KEY"]
BASE_URL = "https://api.edgarkit.com/v1/filings"

TICKERS = ["NVDA", "AAPL", "TSLA"]
ITEM_NUMBERS = ["5.02", "2.02", "1.01"]
POLL_INTERVAL = 60  # seconds

def poll(since: str) -> tuple[list[dict], str]:
    params = {
        "form_type": "8-K",
        "ticker": ",".join(TICKERS),
        "item_numbers": ",".join(ITEM_NUMBERS),
        "since": since,
        "limit": 50,
    }
    resp = requests.get(
        BASE_URL,
        params=params,
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=10,
    )
    resp.raise_for_status()
    data = resp.json()
    filings = data["filings"]
    new_since = filings[0]["filed_at"] if filings else since
    return filings, new_since

def main():
    since = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
    print(f"Polling for 8-Ks on {', '.join(TICKERS)} since {since}")

    while True:
        try:
            filings, since = poll(since)
            for f in filings:
                items = ", ".join(f.get("item_numbers", []))
                print(f"[{f['filed_at']}] {f['issuer_ticker']} 8-K, items: {items}")
                print(f"  {f['primary_document_url']}")
        except Exception as e:
            print(f"Poll error: {e}")

        time.sleep(POLL_INTERVAL)

if __name__ == "__main__":
    main()

FAQ

What's the difference between item 2.02 and item 2.01?

Item 2.02 is results of operations, it's the earnings press release wrapper. Item 2.01 is completion of an acquisition or disposition, which is an M&A event. They're easy to mix up; always check the item number, not just the form type.

Can I subscribe to all 8-Ks without filtering by item number?

Yes. Omit the item_numbers field from your webhook registration or polling request. You'll receive every 8-K for your tickers, including boilerplate filings like Item 8.01 (other events) and Item 9.01 (financial statements). Volume goes up significantly for active tickers.

How quickly does an 8-K appear after the company files it?

EdgarKit picks up new filings within roughly 30 seconds of SEC acceptance. The SEC itself typically accepts a filing within a few minutes of submission, so the end-to-end latency from submission to your webhook is usually under 5 minutes.

What happens if my webhook endpoint is down when a filing comes in?

EdgarKit retries failed deliveries with exponential backoff over 24 hours. If your endpoint returns a non-2xx status, the delivery is queued for retry. You can also fetch missed filings retroactively via the polling endpoint using a since timestamp.

Can I filter to a specific executive role in a 5.02 filing?

The item number filter only tells you a 5.02 was filed. To know whether it's a CEO departure versus a board addition, you need to read the filing text itself. Pull primary_document_url and parse the HTML, or check our structured fields in the details object returned with each 8-K (available for common 5.02 patterns).