CoolFace
Datasetpublic

ZipLime/sec-8k-events

SEC Form 8-K Corporate Events Every Form 8-K filed since the modern item taxonomy took effect — and, for each one, the second the SEC accepted it, which is not the date printed on it. 1 761 353 filings · 3 676 835 item-level events · 23 August 2004 to today The pipeline lives in recipe/ at the same revision as the data. See PIPELINE.md for the method. The problem this dataset exists to solve Apple filed its June-quarter results on 30 July 2026. Here is the filing… See the full description on the dataset page: https://huggingface.co/datasets/ZipLime/sec-8k-events.

sourceHugging Faceapache-2.0updated 8d agoView on Hugging Face
0likes270downloads
Dataset Card

SEC Form 8-K Corporate Events

Every Form 8-K filed since the modern item taxonomy took effect — and, for each one, the second the SEC accepted it, which is not the date printed on it.

1 761 353 filings · 3 676 835 item-level events · 23 August 2004 to today

The pipeline lives in `recipe/` at the same revision as the data. See PIPELINE.md for the method.

The problem this dataset exists to solve

Apple filed its June-quarter results on 30 July 2026. Here is the filing, as EDGAR records it:

filing_date2026-07-30
accepted_at2026-07-30 16:30:28 Eastern

Half an hour after the close. A dataset keyed on the filing date makes that 8-K visible from midnight — and a backtest reading it trades Apple's earnings during a session that ended before the filing existed. The results were public at 16:30:28 and not one second earlier.

So every row here is dated on knowledge_date, derived from EDGAR's acceptance timestamp, and the calendar filing date is carried alongside for reference and used for nothing:

0000320193-26-000018  AAPL   items 2.02, 9.01
  filing_date                 2026-07-30
  accepted_at                 2026-07-30 20:30:28 UTC   (16:30:28 Eastern)
  knowledge_date              2026-07-30 20:30:28 UTC
  knowledge_time_basis        edgar_acceptance_proxy
  knowledge_estimated         false
  market_session              after_market
  next_regular_session_open   2026-07-31 13:30:00 UTC

The Z that is not always a Z

This is the part of the dataset that took the longest to get right, and it is not in the SEC's documentation.

acceptanceDateTime comes out of the submissions API looking unambiguous:

0000320193-26-000018   acceptanceDateTime   2026-07-30T20:30:28.000Z
0000002178-24-000004   acceptanceDateTime   2024-02-21T16:12:54.000Z

For Apple's filing the Z is true: EDGAR's own page shows it accepted at 16:30:28 Eastern, which is 20:30:28 UTC. For the second filing it is not. EDGAR's dissemination header for that accession reads 20240221161254 — 16:12:54 Eastern, five hours later than the field appears to claim.

Both forms appear in the same year, and in the same filer's history. Reading every value as UTC dates hundreds of thousands of filings four or five hours before they existed, which is exactly the look-ahead this dataset is built to prevent.

So each value is treated as two candidate instants and resolved, most certain step first:

  1. 1.EDGAR's operating hours. The system accepts submissions between 06:00 and 22:00 Eastern. A candidate outside that window is not a filing time, and the other candidate is the answer.
  2. 2.The day the Commission assigned. Same day means the 17:30 cutoff did not bite; a later day means it did. If exactly one candidate agrees, it is taken — and marked estimated, because the relation has exceptions in both directions: measured against EDGAR's own instants for 1 757 567 filings, 8 576 were accepted after 17:30 and kept their day and 634 were accepted before it and were moved anyway.
  3. 3.Neither settles it. The later candidate is taken. A knowledge timestamp is allowed to be late and is never allowed to be early.

acceptance_zone_basis records which step decided each row, so nothing rests on a rule the reader cannot see.

And then it is resolved outright

EDGAR's own filing feed states the offset the archive omits:

xml
<updated>2025-02-04T16:03:11-05:00</updated>

Every filer's history is read from that feed and stored in metadata/acceptance.parquet, one row per accession — 1 937 856 of them. Where it has been read, it wins over every inference above: acceptance_zone_basis becomes edgar_feed_stated_offset and knowledge_estimated is false.

1 757 567 of 1 761 353 filings — 99.8% — carry the instant EDGAR states with its offset. The remaining 3 786 are resolved conservatively and say so:

`acceptance_zone_basis`Filings
edgar_feed_stated_offset1 757 567EDGAR's own instant, offset and all
zone_ambiguous_later_reading1 618Nothing separates the readings; the later one, marked estimated
zone_inferred_from_filing_date1 582The assigned filing date separates them; marked estimated
sec_field_read_as_utc467The Eastern reading falls outside EDGAR's hours
sec_field_read_as_eastern119The UTC reading falls outside EDGAR's hours

This is also what makes the rest of the dataset trustworthy: 804 918 filings are classified `after_market`, where reading the SEC's field naively as UTC would have placed most of them inside the trading session they arrived after.

Acceptance is a proxy, and the column names say so

The SEC timestamps the moment it accepted a submission. It does not publish the moment the document appeared on sec.gov, and in practice that follows acceptance by a small processing interval.

This dataset does not paper over the difference. There is no column called exact_publication_timestamp. There is accepted_at, which is the Commission's own field, and knowledge_time_basis, which names how each row's instant was arrived at:

`knowledge_time_basis`FilingsWhat it means
edgar_acceptance_proxy1 636 127Accepted and disseminated the same day; knowledge_date is the acceptance second
next_business_day_dissemination125 189The SEC held the filing over; dated to 09:30 Eastern on the day it assigned
placeholder_acceptance37The acceptance timestamp is exactly midnight, which is a gap in the record, not a time

knowledge_estimated is true whenever the instant was ruled rather than read — a held-over filing, a midnight placeholder, or a zone this recipe had to infer. That is 128 322 filings, 7.3%, and one predicate filters them out.

How the held-over filings were found

EDGAR disseminates submissions accepted after 17:30 Eastern on the next business day. The obvious implementation is a clock rule, and the obvious implementation is wrong. Measured against EDGAR's own acceptance instants:

Filings
accepted after 17:30, filing date moved124 824
accepted after 17:30, filing date not moved8 576
accepted before 17:30, filing date moved634

A clock rule mis-dates the last two rows — 9 210 filings, 0.52%. So the test is not the clock but the SEC's own two fields disagreeing: when EDGAR holds a submission over, the filing_date it assigns is later than the date of acceptance, and that is the Commission saying which day the filing went out.

The earliest acceptance in the dataset is 20 August 2004, 17:32:07 Eastern — a Friday evening submission, after the cutoff, that the Commission assigned to Monday 23 August, the first day of the modern taxonomy.

One filing is several events

A filing declaring 2.02,5.02,9.01 is three events, not one, and which kind of news arrived is the entire question. items has one row per accession × item code, keyed <accession>:<item_code>, all sharing the filing's one knowledge instant.

ItemEvents
9.011 316 502Financial statements and exhibits
8.01448 364Other events
2.02421 615Results of operations
7.01326 578Regulation FD disclosure
5.02310 531Departure or election of directors and officers
1.01300 252Entry into a material definitive agreement
4.0130 616Change in certifying accountant
3.0127 525Delisting or transfer notice
4.029 342Non-reliance on previously issued financials
1.033 616Bankruptcy or receivership
1.0583Material cybersecurity incident (effective December 2023)

The five bolded families that are hardest to assemble by hand are also published as standalone views: earnings_8k, management_events, auditor_events, restatement_events, cyber_events.

What this dataset does not contain

No sentiment, no severity score, no direction. There is no bullish, no fraud_probability, no restatement_risk_score. An item 1.03 bankruptcy and an item 2.02 results release are both interesting; neither is good or bad news until somebody reads it, and a model's opinion recorded as canonical data is indistinguishable from a fact once it is in a parquet file. The item_priority field is a research bucket — how often a code is worth looking at first — and says nothing about the news.

EX-99.1 is not assumed to be earnings. It is the general-purpose exhibit slot and holds press releases about anything. An exhibit is attached to the earnings_8k view only when the filing itself declares item 2.02.

Pre-2004 filings are not remapped. Before 23 August 2004 item 5 meant Other Events and item 4 a change of accountant; the modern 5.02 is a management change and 4.02 a restatement. The dataset starts at the taxonomy change rather than translating old codes into new ones. The 65 post-2004 rows that still declare a bare 5 or 9 carry item_taxonomy = "legacy_pre_2004", because the code is more specific evidence than the date.

Identity: CIK, not ticker

The filing key is the accession number, which the SEC assigns once and never changes. Identity is the CIK, which is permanent. ticker + date is never a key here: a company can file two 8-Ks in a day, tickers are reused, and a filer may have none at all.

entity_map holds what a company was called when, rather than rewriting a 2009 filing with today's symbol. Of 51 599 filers, 11 332 have a current ticker, covering 800 850 filings (45.5%). The rest are delisted issuers, debt-only filers and entities that never had a listed symbol. Their filings are complete; they simply have no ticker to join on, and inventing one would be worse than the null.

34 539 filings were filed jointly by a parent and its subsidiaries and appear once, not once per registrant, with the others in co_registrant_ciks.

Amendments never overwrite

An 8-K/A is a separate filing with a later knowledge instant. amendment_links joins 44 724 of the 58 308 amendments to what they amend, and records how the link was made; where the evidence was not there, the amendment stands alone rather than being attached to a guess. Standing between the two instants, a backtest sees the original and not the amendment, which is what actually happened.

Market sessions

market_session and next_regular_session_open come from a real exchange calendar — holidays computed from the NYSE rules, half-days and one-off closures enumerated — not from a hard-coded 16:00.

SessionFilings
after_market804 918
regular_session615 445
pre_market336 640
non_trading_day4 350

On the half-days — the afternoon before Independence Day, the day after Thanksgiving, Christmas Eve — the close is 13:00, so a 13:30 filing is after_market, not inside the session.

Point-in-time usage

Everything a strategy may know at an instant:

python
import polars as pl

AS_OF = pl.datetime(2026, 7, 30, 20, 0, 0, time_zone="UTC")   # 16:00 Eastern

known = (
    pl.scan_parquet("data/items/*.parquet")
      .filter(pl.col("knowledge_date") <= AS_OF)
)

At 16:00 Eastern on 30 July, Apple's results 8-K is not in that frame. It becomes visible at 20:30:28 UTC, half an hour later.

An earnings event study, traded at the next open:

python
earnings = (
    pl.scan_parquet("data/earnings_8k/*.parquet")
      .filter(pl.col("entity_id").is_not_null())
      .select("entity_id", "knowledge_date", "next_regular_session_open",
              "market_session", "item_text", "exhibit_text")
      .collect()
)

next_regular_session_open is already the first moment the event could be traded, so nothing has to be recomputed from a calendar.

Only instants the SEC stated, discarding the estimated ones:

python
exact = known.filter(~pl.col("knowledge_estimated"))

What not to do:

python
# Wrong: filing_date makes an after-close filing visible from midnight.
pl.col("filing_date") <= AS_OF.dt.date()

# Wrong: event_date is when the event happened, not when it was disclosed.
pl.col("event_date") <= AS_OF

event_date is the filer's period of report. A 5.02 filing reporting a resignation that took effect three days earlier has an event_date three days before its knowledge_date, and dating on it would hand a backtest the news before the company published it.

Tables

TableRows
filings1 761 353One row per accession: acceptance, knowledge instant, session, URLs
items3 676 835One row per accession × item code, with the item's own text
documents47 112Primary 8-K documents as text (the text window)
exhibits35 491Exhibits, typed as the filer declared them; 29 191 with text
amendment_links44 7248-K/A joined to the filing it amends
entity_map51 599CIK to ticker and former names, with validity dates
earnings_8k421 615Item 2.02 with its EX-99
management_events310 531Item 5.02
auditor_events30 616Item 4.01 with its EX-16
restatement_events9 342Item 4.02
cyber_events83Item 1.05

Which exhibits carry text

Three exhibit families are read, because they are the ones that carry the event: EX-99 (the results release or press release), EX-16 (the outgoing accountant's letter, which is the most informative page in an item 4.01 filing) and EX-10 (the agreement an item 1.01 filing is about).

Every other exhibit a filing carried is still in the table — its type, filename, URL and the item codes it was filed under — with text_status = "not_requested" and no text. An EX-2 merger agreement runs to 292 000 characters at the median and adds nothing the item text does not already say; publishing it would triple the size of this dataset to restate a contract that is one click away.

text_status separates the two kinds of null: not_requested is a family nobody asked for, unavailable is a fetch that failed.

The text window

Metadata covers every filing from 2004 to today, because the SEC publishes it as a single nightly archive: one request for 1.76 million filings.

The current window is 1 January 2026 to today: 47 112 filings read, 35 491 exhibits indexed, 104 030 item sections extracted — 99.3% of the items in the window. 1 038 rows are partial (the SEC declared more items than the headings yielded) and 154 are quarantined.

Document text is different. It costs one request per filing, and reading all 1.76 million of them is sixty hours of the SEC's bandwidth. So text is fetched for an explicit window, recorded in metadata/build.json, and `metadata/coverage.parquet` says year by year and item code by item code which rows have text and which have only metadata — so a null never has to be guessed at.

A filing outside the window has no item_text because nobody fetched it. A filing inside the window with no text had a parser failure and is marked quarantined or partial. Those are different facts and the dataset keeps them apart.

How fast the collector sees a filing

accepted_at is the SEC's instant; first_seen_at is when this project saw the filing on EDGAR's current-filings feed. They are different facts and nothing is dated on the second one. The gap between them is published in metadata/latency.json, so a reader can judge what edgar_acceptance_proxy is worth rather than taking the word "proxy" on trust.

Filings already sitting in the feed when the collector starts are recorded as backlog and excluded from the measurement: they say when the collector started, not how fast EDGAR is.

Provenance and rights

Every row carries source_url and accession_no; nothing here is derived from a third-party redistribution of EDGAR.

The metadata — accession numbers, timestamps, item codes, document indexes — is produced by the Commission and is a US Government work.

The filing text is not. It is written by the issuer, and this dataset does not claim otherwise merely because it arrives through EDGAR. What makes republication proper is narrower: the Exchange Act requires these disclosures to be public, EDGAR exists to disseminate them, the Commission places no restriction on reuse of what it disseminates, and every row names the issuer, the accession and the source URL so that any passage can be traced to its author. See `metadata/source_licenses.yaml`.

The SEC does not endorse, certify or verify this dataset. The collector declares a User-Agent with a contact address and stays inside the Commission's fair-access limit of ten requests a second.

Updates

JobCron (UTC)
sec-8k-events-update0 7 * * 2-6after the SEC rebuilds the nightly archive
sec-8k-events-sweep30 22 * * 1-5through the heaviest filing hour, just after the close

The evening run is the one that matters for event trading: an 8-K accepted at 16:30 Eastern is the commonest shape in this dataset, and waiting for the overnight archive would mean seeing it the next morning instead of the evening it arrived. Text already read is never re-requested — metadata/text_ledger.parquet is the record of which filings the SEC has already been asked for.

Building it yourself

bash
pip install -e ".[publish]"
export SEC_USER_AGENT="Your Project you@example.com"   # the SEC requires this

python -m recipe.cli build --no-text                  # metadata for everything, ~4 minutes
python -m recipe.cli build --text-from 2026-01-01     # and the text for one window
python -m recipe.cli collect --minutes 20             # watch the feed, record first_seen_at
python -m recipe.cli verify                           # the quality gate