SEC Comment Letters and Registrant Responses
The SEC's disclosure-review correspondence: the staff's comment letters to a registrant and the registrant's replies. Paired by company and file number they reconstruct every review round the SEC has run - who was questioned, about which filing, and how long the exchange took. The letters are embargoed until the review closes, so each row is dated the day it actually became public, weeks to years after the letter's own date.
edgar_sec_review_letters - the dataset name to pass to the Obscura API.
What one row means
One letter in an **SEC disclosure-review round**: `UPLOAD` is a staff comment letter to a registrant, `CORRESP` is the registrant's reply. 227,777 letters since 2015, and the only public record of the SEC questioning a specific disclosure in a specific filing. # This is the family where `available_date` and the filing date are FAR apart Every letter is EMBARGOED. SEC withholds review correspondence until the review closes, marks the submission `<PRIVATE-TO-PUBLIC>` when it releases it, and stamps the release day on the `<SEC-DOCUMENT>` line - so `occurred_on` (the day the letter was written) and `available_date` (the day anyone could read it) are different columns carrying genuinely different dates. Measured on 468 letters across six 2024 dissemination days: the gap is **never less than 28 days**, median 37, p90 136, max 604 - and on `master.20150514.idx`, 4 of 56 letters were written in 2009, a SIX-YEAR embargo. Dating a row from `FILED AS OF DATE` would therefore leak SEC's private correspondence into a backtest one month to six years early, on every one of the 227,777 rows. `release_lag_days` measures the gap, and is a signal in its own right: it tracks how long the review took to close. See `docs/datasets/edgar_sec_review_letters.md`.
One row per One row per LETTER, keyed by its ACCESSION. The key is the accession and NOT the archive path because EDGAR indexes one letter under one path PER associated registrant, and a review round routinely names many: measured over six 2024 dissemination days, 468 index rows carry only 357 distinct letters, and one T. Rowe Price CORRESP (0001741773-24-002730) is listed under 66 fund CIKs on master.20240719.idx while a Maxeon UPLOAD (0000000000-24-006412) is listed under 11. Keying on the path would store that single letter 66 times. The co-addressed registrants are a child list on the same row (co_filer_ciks), never extra rows. The review ROUND is likewise not the grain: pairing an UPLOAD with the CORRESP that answers it is a heuristic over (cik, sec_file_number, occurred_on) and belongs in a derived view, not baked into a key that could never be fixed..
Point-in-time availability
Every Obscura dataset carries available_date: the calendar day the publisher made the row
available, day-of, with no session rounding. It is the one column a backtest filters on, and it means the
same thing on every dataset in the catalog.
For edgar_sec_review_letters: filing_join: available_date is the EDGAR DISSEMINATION day, taken from the bronze object's own day partition - the day SEC RELEASED the letter, which is 28 days to SIX YEARS after the letter itself. This family is EMBARGOED: SEC publishes review correspondence only after the review closes, marks every released submission <PRIVATE-TO-PUBLIC>, and writes the release day on the <SEC-DOCUMENT> line. MEASURED class A+B: 0000000000-24-007331 was accepted 2024-06-28, is ABSENT from master.20240628.idx and PRESENT in master.20240821.idx (54 days); across 468 letters on six 2024 dissemination days the lag is min 28 / median 37 / p90 136 / max 604 days, and 0000000000-09-028240 was written 2009-05-28 and released 2015-05-14 (2,177 days). Using FILED AS OF DATE as the availability date would leak SEC's PRIVATE correspondence on every row of the dataset; that date is stored as occurred_on and nothing else.
Refresh cadence
Obscura refreshes edgar_sec_review_letters every 6 hours - the most frequent scheduled job that re-collects or re-exports it. This is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when a row became public is recorded per row in available_date.
Schema - 21 columns
The full public column list for edgar_sec_review_letters, with the meaning of every field. The same
schema is served unauthenticated at https://api.obscura.trade/v1/catalog/edgar_sec_review_letters.
| Column | Type | Description |
|---|---|---|
| accession | text · not null | PK. SEC accession number of the letter. `0000000000-YY-NNNNNN` on every `UPLOAD` (SEC generates the accession itself, so there is no filer prefix); a normal filer-agent accession on a `CORRESP`. The key is the accession, not the archive path: EDGAR lists one letter under one path per co-addressed registrant, up to 66 of them. |
| filename | text · not null | The PRIMARY registrant's SEC EDGAR Archives path for this letter (`edgar/data/{cik}/{accession}.txt`), derived from the FIRST registrant block in the payload rather than from whichever index row was walked - so it is the same value on every ingestion path. The join key into `edgar_filings`. |
| available_date | date | Public availability date: the day SEC RELEASED the letter, recovered from the bronze object's dissemination-day partition. **This is 28 days to six years after `occurred_on`, because the family is embargoed** - it is the point-in-time anchor and the only date a backtest may key on. |
| occurred_on | date | The day the letter was WRITTEN and lodged (`FILED AS OF DATE`) - when SEC asked the question, or the registrant answered it. Verified equal to the date printed at the top of the letter on 3/3 sampled `TEXT-EXTRACT` bodies (`May 10, 2024`, `June 4, 2024`, `June 28, 2024`). NEVER an availability date on this table: nobody outside SEC and the registrant could read it on this day. |
| accepted_at | timestamp with time zone | The exact instant SEC accepted the letter into EDGAR (`<ACCEPTANCE-DATETIME>`), US-Eastern wall clock stored under a UTC type. Its date part is `occurred_on`. NULL on a row ingested from the dissemination feed rather than from Archives - the feed's PDS envelope does not carry this stamp, so a NULL here is a fact about the envelope and not a decoder gap. |
| form_type | text · not null | `UPLOAD` (SEC staff → registrant, a comment letter) or `CORRESP` (registrant → SEC staff, the reply). **The DIRECTION of the letter**, and the only thing that distinguishes a question from an answer. |
| was_embargoed | boolean · not null | TRUE when SEC marked the submission `<PRIVATE-TO-PUBLIC>` - the publisher's own statement that this was withheld and then released. TRUE on 7/7 sampled letters across 2009-2024 and both form types. It is the audit trail for why `available_date` differs from `occurred_on` here when they are the same day on almost every other EDGAR table. |
| release_lag_days | integer | Days the letter was withheld: `available_date − occurred_on`. **A measure in its own right** - it is how long SEC's review took to close. Measured min 28 / median 37 / p90 136 / max 604 days over 468 letters on six 2024 dissemination days, and 2,177 days on a 2009 letter released in 2015. Derived, and the derivation is enforced by a CHECK rather than trusted. |
| cik | bigint | Central Index Key of the PRIMARY registrant under review - the first registrant block in the header. Note the header LABEL differs by form: `UPLOAD` writes `FILED FOR:`, `CORRESP` writes `FILER:`. |
| company_name | text | Primary registrant's name as EDGAR conformed it at the time of the letter, e.g. `MariaDB plc`. |
| co_filer_ciks | jsonb · not null | JSON array of `{cik, name, file_number}` for the SECOND and subsequent registrants the letter is addressed to - a corporate group under one review (`0000000000-24-006412`: Maxeon Solar plus 10 subsidiaries) or a fund complex answering as one (`0001741773-24-002730`: 65 T. Rowe Price funds). Empty array on a single-registrant letter, which is the common case. |
| co_filer_count | integer · not null | Number of co-addressed registrants beyond the primary (0 for a plain letter, 10 for the Maxeon group, 65 for the T. Rowe Price complex). A cheap group-review proxy, and the guard against fanning one letter into 66 rows. |
| sec_file_number | text | The primary registrant's SEC file number, from its own header block. Multi-modal, measured: `005-` (tender offer), `001-`/`000-` (Exchange Act reporting), `333-` (Securities Act registration), `022-` (Trust Indenture Act qualification), `811-` (Investment Company Act). The best available pairing key between an `UPLOAD` and the `CORRESP` that answers it - but NOT a reliable one: it is absent entirely on 2 of 3 sampled `CORRESP`s and on the sampled pre-2010 `UPLOAD`s, which is why the pairing is a derived view and not this table's grain. |
| reviewed_accession | text | Accession of the FILING BEING COMMENTED ON (`PUBLIC REFERENCE ACCESSION NUMBER`) - a direct, structural link from the comment letter to the document that drew the comment, with no prose parsing. Present on 4/4 sampled `UPLOAD`s (2009 and 2024) and ABSENT on 3/3 sampled `CORRESP`s, so the letter → reviewed-filing link is one-directional: reach a reply through `(cik, sec_file_number)`, not through this column. |
| sic_code | integer | Primary registrant's SIC code, from the bracketed integer in `STANDARD INDUSTRIAL CLASSIFICATION:` (or the feed envelope's bare `<ASSIGNED-SIC>`). NULL on older letters, whose header states no SIC at all. |
| state_of_incorporation | text | Primary registrant's state or country of incorporation, e.g. `DE`, `L2` (Ireland), `D0` (Bermuda), `A1` (British Columbia). |
| former_name | text | The registrant's MOST RECENT prior name when EDGAR records one (the first `FORMER CONFORMED NAME:` block, which EDGAR writes newest-first). Load-bearing on this family: a letter released years after it was written often names an entity that has since been renamed, and `MariaDB plc` was `Mangomill plc` when the review opened. |
| letter_text | text | The letter as PLAIN TEXT. For an `UPLOAD` this is SEC's own `TEXT-EXTRACT` sibling document, unaltered but for trailing whitespace and runs of blank lines - the primary document is a PDF (18/18 in the sizing sweep) and is deliberately not stored, since `TEXT-EXTRACT` carries the same content at ~1/12 the bytes (3,875 B against 45,137 B on the MariaDB letter). For a `CORRESP` it is the `<TYPE>CORRESP` body with its markup stripped and its entities resolved: measured 327,208 bytes of HTML reducing to ~24,000 characters of prose, so storing the markup would cost 14x for nothing. NULL when SEC attached no readable payload, which is the pre-`TEXT-EXTRACT` era of `UPLOAD` (`0000000000-09-028240` is a `LETTER` PDF and nothing else) - a permanent property of the submission, not a fetch we should retry. |
| text_char_count | integer | Character count of `letter_text`. NULL - deliberately, rather than 0 - when there is no text, so "SEC attached no readable payload" is never confused with "SEC attached an empty one". Derived, and CHECKed. |
| comment_count | integer | Number of NUMBERED items in the letter - lines matching `^\s*\d+\.`, which on an `UPLOAD` are the staff's comments and on a `CORRESP` are the registrant's numbered responses to them. A crude but real severity proxy: 5 on the MariaDB tender-offer letter, 8 on the American Strategic one. **`0` is a real and meaningful value**, not a parse failure: `0000000000-24-006412` is a complete letter whose entire content is "we have not reviewed and will not review your registration statement". NULL if and only if `letter_text` is NULL, which a CHECK enforces as a biconditional - so a no-comment letter can never be confused with a letter whose text SEC never attached. |
| document_types | jsonb · not null | Ordered JSON array of the submission's document types, e.g. `["LETTER","TEXT-EXTRACT"]` on a modern `UPLOAD`, `["LETTER"]` on a pre-2010 one, `["CORRESP","GRAPHIC"]` on a reply with a letterhead image. The manifest is kept; the PDF and the images are not - bronze retains them. |
Access edgar_sec_review_letters
Two delivery paths, one identifier. Both require an Obscura account and an active subscription; the catalog entry and the schema above are public.
import obscura
client = obscura.Client("obs_live_…")
df = client.query(
dataset="edgar_sec_review_letters",
symbols=["NVDA", "AAPL"],
start="2024-01-01",
)
Create a free account Browse all 122 datasets
Frequently asked questions
What is in the edgar_sec_review_letters dataset?
The SEC's disclosure-review correspondence: the staff's comment letters to a registrant and the registrant's replies. Paired by company and file number they reconstruct every review round the SEC has run - who was questioned, about which filing, and how long the exchange took. The letters are embargoed until the review closes, so each row is dated the day it actually became public, weeks to years after the letter's own date. One letter in an **SEC disclosure-review round**: `UPLOAD` is a staff comment letter to a registrant, `CORRESP` is the registrant's reply. 227,777 letters since 2015, and the only public record of the SEC questioning a specific disclosure in a specific filing. # This is the family where `available_date` and the filing date are FAR apart Every letter is EMBARGOED. SEC withholds review correspondence until the review closes, marks the submission `<PRIVATE-TO-PUBLIC>` when it releases it, and stamps the release day on the `<SEC-DOCUMENT>` line - so `occurred_on` (the day the letter was written) and `available_date` (the day anyone could read it) are different columns carrying genuinely different dates. Measured on 468 letters across six 2024 dissemination days: the gap is **never less than 28 days**, median 37, p90 136, max 604 - and on `master.20150514.idx`, 4 of 56 letters were written in 2009, a SIX-YEAR embargo. Dating a row from `FILED AS OF DATE` would therefore leak SEC's private correspondence into a backtest one month to six years early, on every one of the 227,777 rows. `release_lag_days` measures the gap, and is a signal in its own right: it tracks how long the review took to close. See `docs/datasets/edgar_sec_review_letters.md`.
How do I avoid look-ahead bias with edgar_sec_review_letters?
Filter on edgar_sec_review_letters.available_date, the day the publisher made the row public. For this dataset that date is derived as follows - filing_join: available_date is the EDGAR DISSEMINATION day, taken from the bronze object's own day partition - the day SEC RELEASED the letter, which is 28 days to SIX YEARS after the letter itself. This family is EMBARGOED: SEC publishes review correspondence only after the review closes, marks every released submission <PRIVATE-TO-PUBLIC>, and writes the release day on the <SEC-DOCUMENT> line. MEASURED class A+B: 0000000000-24-007331 was accepted 2024-06-28, is ABSENT from master.20240628.idx and PRESENT in master.20240821.idx (54 days); across 468 letters on six 2024 dissemination days the lag is min 28 / median 37 / p90 136 / max 604 days, and 0000000000-09-028240 was written 2009-05-28 and released 2015-05-14 (2,177 days). Using FILED AS OF DATE as the availability date would leak SEC's PRIVATE correspondence on every row of the dataset; that date is stored as occurred_on and nothing else. A query of the form WHERE available_date <= '<as-of date>' never sees a row before it existed.
In what formats can I get edgar_sec_review_letters?
As a Parquet bulk export (POST https://api.obscura.trade/v1/download) or as JSON from the typed query API (POST https://api.obscura.trade/v1/query), both with dataset="edgar_sec_review_letters". The column schema is public at https://api.obscura.trade/v1/catalog/edgar_sec_review_letters.
Can I filter edgar_sec_review_letters by company or symbol?
Yes. edgar_sec_review_letters carries cik, the column the API's symbols filter resolves against.
How often is edgar_sec_review_letters updated?
Obscura refreshes edgar_sec_review_letters on a every 6 hours schedule - that is the most frequent scheduled job that re-collects or re-exports the table. It is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when the publisher makes a row available is described by the availability rule above, and is recorded per row in available_date.