Endpoints
/v1/stream
https://secfeedapi.com/v1/streamServer-Sent Events: each new filing the moment EDGAR publishes it.
| Parameter | Type | Description | Example |
|---|---|---|---|
formTypes | string | Comma separated form types to keep, matched exactly as EDGAR labels them (for example 4, 8-K, SC 13D). Omit to receive every form. | 8-K,4 |
ciks | string | Comma separated CIKs without leading zeros. Matches the CIK of the filer listed in the event, so add the issuer and the insider CIKs if you want both sides of a Form 4. | 320193,1045810 |
- Response is text/event-stream. The server sends ': connected' first, then 'event: filing' messages whose data line is one JSON object (same fields as /v1/stream/latest).
- A ': keep-alive' comment arrives every 15 seconds when nothing matches, so idle proxies do not drop the connection.
- Each new filing is delivered once per open connection while it is open. Nothing is replayed after a disconnect: on reconnect, call /v1/stream/latest to catch up on what you missed, then reopen the stream.
- Opening the stream counts as one API call, however long it stays open.
/v1/stream/latest
https://secfeedapi.com/v1/stream/latestThe latest filings seen by the stream, each with seenAt and latencyMs.
| Parameter | Type | Description | Example |
|---|---|---|---|
size | integer | Number of most recent filings to return, newest first. Maximum 500. Default 50. | 20 |
- Reads the stream log, so it shows exactly what the stream delivered, with seenAt and latencyMs on each row.
- Use it to backfill gaps after a reconnect: compare accessionNo values with what you already stored.
/v1/stream/latency
https://secfeedapi.com/v1/stream/latencyMeasured delivery latency (p50, p90, p99) from EDGAR acceptance to SecFeed, last N hours.
| Parameter | Type | Description | Example |
|---|---|---|---|
hours | integer | Look-back window in hours. Default 24. | 24 |
- Latency is measured from EDGAR's acceptance timestamp to the moment SecFeed saw the filing. EDGAR timestamps have one-second precision, so each value can read up to 1000 ms high.
- Returns {"filings": 0} when no filings were logged in the window.
/v1/webhooks
https://secfeedapi.com/v1/webhooksPush new filings to your HTTPS endpoint.
| Parameter | Type | Description | Example |
|---|---|---|---|
urlrequired | string | JSON body field. HTTPS URL that receives the POST. Must resolve to a public address; private, loopback and link-local hosts are rejected. | https://example.com/secfeed-hook |
formTypes | array of strings | JSON body field. Only deliver these form types. Empty or missing means all forms. | ['8-K', '4'] |
ciks | array of strings or integers | JSON body field. Only deliver filings from these filer CIKs. Empty or missing means all filers. | ['320193'] |
- Returns {"id", "url"} for the new webhook.
- Each delivery is a POST with Content-Type application/json and User-Agent SecFeed-Webhook/1.0. The body is {"filings": [...]}, a batch of the filings found in one poll that match your filters, each with the same fields as /v1/stream/latest.
- Deliveries are sent once with a 10 second timeout and are not retried. Respond quickly with any 2xx and do your processing afterwards; use /v1/stream/latest to reconcile anything you missed.
- The target host is checked again at send time, so a URL that later resolves to a private address stops receiving deliveries.
Request examples
Every response below is real output from the API, trimmed to the first records.
The two most recent filings the stream delivered
Each row is one filing as the stream emitted it. latencyMs is the delay between EDGAR acceptance (filedAt) and the moment SecFeed saw it (seenAt).
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream/latest?size=2"
import os, requests
r = requests.get(
"https://secfeedapi.com/v1/stream/latest",
params={'size': '2'},
headers={"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"},
timeout=60,
)
r.raise_for_status()
print(r.json())[
{
"accessionNo": "0001918704-26-031257",
"formType": "424B2",
"companyName": "BofA Finance LLC",
"cik": "1682472",
"role": "Filer",
"filedAt": "2026-10-13T11:27:56-04:00",
"linkToFilingDetails": "https://www.sec.gov/Archives/edgar/data/1682472/000191870426031257/0001918704-26-031257-index.htm",
"seenAt": "2026-10-13T15:28:22.263+00:00",
"latencyMs": 26263
},
{
"accessionNo": "0001839882-26-050586",
"formType": "424B2",
"companyName": "Morgan Stanley Finance LLC",
"cik": "1666268",
"role": "Filer",
"filedAt": "2026-10-13T11:28:01-04:00",
"linkToFilingDetails": "https://www.sec.gov/Archives/edgar/data/1666268/000183988226050586/0001839882-26-050586-index.htm",
"seenAt": "2026-10-13T15:28:20.379+00:00",
"latencyMs": 19379
}
]Measured delivery latency over the last 24 hours
Percentiles in milliseconds over every filing logged in the window. p99 includes filings EDGAR published in a burst after a delay on its side.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream/latency?hours=24"
import os, requests
r = requests.get(
"https://secfeedapi.com/v1/stream/latency",
params={'hours': '24'},
headers={"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"},
timeout=60,
)
r.raise_for_status()
print(r.json()){
"filings": 1245,
"p50Ms": 25406,
"p90Ms": 37258,
"p99Ms": 450189,
"minMs": 12045,
"note": "EDGAR timestamps have 1-second precision, so each value can be up to 1000 ms high"
}A larger catch-up window after a reconnect
Pull the most recent filings, skip any accessionNo you already processed, then reopen /v1/stream.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream/latest?size=5"
import os, requests
r = requests.get(
"https://secfeedapi.com/v1/stream/latest",
params={'size': '5'},
headers={"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"},
timeout=60,
)
r.raise_for_status()
print(r.json())[
{
"accessionNo": "0000905148-26-004445",
"formType": "D/A",
"companyName": "Thrivent White Rose Endurance Fund IV, L.P.",
"cik": "2089010",
"role": "Filer",
"filedAt": "2026-10-13T11:28:13-04:00",
"linkToFilingDetails": "https://www.sec.gov/Archives/edgar/data/2089010/000090514826004445/0000905148-26-004445-index.htm",
"seenAt": "2026-10-13T15:28:44.090+00:00",
"latencyMs": 31090
},
{
"accessionNo": "0001918704-26-031257",
"formType": "424B2",
"companyName": "BofA Finance LLC",
"cik": "1682472",
"role": "Filer",
"filedAt": "2026-10-13T11:27:56-04:00",
"linkToFilingDetails": "https://www.sec.gov/Archives/edgar/data/1682472/000191870426031257/0001918704-26-031257-index.htm",
"seenAt": "2026-10-13T15:28:22.263+00:00",
"latencyMs": 26263
}
]Response fields
| Field | Type | Meaning |
|---|---|---|
[].accessionNo | string | EDGAR accession number, the unique id of the filing. Use it to deduplicate. |
[].formType | string | Form type as EDGAR lists it in the live feed, for example 4, 8-K, 424B2, D. |
[].companyName | string | Name of the filer this event was emitted for. |
[].cik | string | CIK of that filer, without leading zeros. |
[].role | string | The filer's role in the filing as EDGAR lists it, for example Filer, Issuer or Reporting. |
[].filedAt | string | EDGAR acceptance time, ISO 8601 with the Eastern Time offset. |
[].linkToFilingDetails | string | URL of the filing index page on sec.gov. |
[].seenAt | string | When SecFeed picked the filing up, ISO 8601 in UTC with milliseconds. |
[].latencyMs | integer | Milliseconds from filedAt to seenAt. |
filings | integer | /v1/stream/latency: number of filings in the window. |
p50Ms | integer | /v1/stream/latency: median latency in milliseconds. |
p90Ms | integer | /v1/stream/latency: 90th percentile latency. |
p99Ms | integer | /v1/stream/latency: 99th percentile latency. |
minMs | integer | /v1/stream/latency: fastest delivery in the window. |
note | string | /v1/stream/latency: reminder about EDGAR's one-second timestamp precision. |
SSE message types
| Line | Meaning |
|---|---|
| : connected | Sent once when the connection opens. |
| : keep-alive | Comment sent every 15 seconds while no matching filing arrives. Ignore it. |
| event: filing | A new filing. The next line is data: followed by one JSON object. |
How to
How do I receive only Form 4 and 8-K filings?
Pass formTypes as a comma separated list. Matching is exact, so list amendments such as 4/A separately if you want them.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream?formTypes=4,8-K"
How do I follow a single company?
Filter by its CIK. A Form 4 is listed once for the issuer and once for each reporting owner, and the stream emits each accession once, so include every CIK you care about.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream?ciks=1045810"
What happens to filings that arrive while I am disconnected?
The stream does not replay them. After reconnecting, call /v1/stream/latest with a size large enough to cover the gap and skip accession numbers you already have.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream/latest?size=200"
How fast is delivery in practice?
Check /v1/stream/latency for live percentiles over any window. Values are measured per filing from EDGAR's acceptance time.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream/latency?hours=6"
How do I consume the stream from Python?
Open the URL with streaming enabled, read line by line, and parse every line that starts with data: as JSON. Lines starting with a colon are comments.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream?formTypes=8-K"
How do I get filings pushed to my server instead of holding a connection open?
Register a webhook with POST /v1/webhooks and a JSON body containing your HTTPS url plus optional formTypes and ciks. Matching filings arrive as POST requests with a filings array.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/webhooks"
Why was my webhook URL rejected?
The url must start with https:// and resolve to a public address. Local, private network and link-local hosts are refused to protect the service.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/webhooks"
Are webhook deliveries retried?
No. Each batch is posted once with a 10 second timeout. Acknowledge fast and reconcile periodically with /v1/stream/latest.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream/latest?size=50"
Does a long stream connection use many API calls?
No. Opening the connection is one call; filings delivered over it are not counted separately.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream?formTypes=4"
Why does a filing's filedAt look older than the moment I received it?
filedAt is EDGAR's acceptance time. EDGAR sometimes publishes to its live feed a few seconds after acceptance, which shows up in latencyMs.
curl -H "Authorization: Bearer $SECFEED_KEY" \ "https://secfeedapi.com/v1/stream/latest?size=10"
Errors
- POST /v1/webhooks returns 400 with "body needs an https url" when url is missing or not https.
- POST /v1/webhooks returns 400 with "webhook url must point to a public host" for private, loopback or link-local targets.
See also the error codes every endpoint shares.
Try it with your own key
500 free calls a month.