Repository navigation
Description
Issue Type
- Data Quality Issue (incorrect financial values, missing data)
Environment
EdgarTools Version: 5.61.1 (main at 7a2a157)
Python Version: 3.12
Operating System: macOS 26.6
Bug Description
What you expected to happen:
acceptance_datetime on a filing from Company(...).get_filings() is the moment SEC accepted the filing. It should agree with filing.header.acceptance_datetime and with the "Accepted" time on SEC's index page.
What actually happened:
The value comes from acceptanceDateTime in data.sec.gov/submissions/CIK##########.json, parsed as UTC because of its trailing Z (edgar/dates.py::parse_acceptance_datetime). That field is not reliably UTC. For some files it is 4 hours late.
Apple 10-Q 0000320193-26-000020, read on 2026-10-08:
| Source | Value |
|---|---|
| SEC index page, "Accepted" (ET) | 2026-07-31 06:01:02 |
filing.header.acceptance_datetime (naive ET) |
2026-07-31 06:01:02 |
| True UTC | 2026-07-31 10:01:02 |
submissions JSON / get_filings()[i].acceptance_datetime |
2026-07-31 14:01:02+00:00 |
Tesla's 10-Q 0001628280-26-049270 is correct: 21:02:31 ET, reported as 01:02:31 UTC the next day.
Offset of the JSON value from the header's ET time, measured on 2026-10-08 over 3–8 filings per company:
| Filers | Offset | Meaning |
|---|---|---|
| Tesla, Microsoft, NVIDIA, SVB, Heliogen, Bed Bath & Beyond, Meta Materials | +4 h (EDT) / +5 h (EST) | true UTC |
| Apple, Carvana (every filing sampled, any filer agent) | +8 h | 4 h late |
It also changes over time. On 2026-10-07 the same check gave +4/+5 h (true UTC) for Apple, but 0 h (ET labelled as UTC) for SVB, Heliogen, Bed Bath & Beyond and Meta Materials. So the error is per file and per day. No fixed offset corrects it.
A related problem: EntityFiling.acceptance_datetime is a tz-aware UTC datetime, while filing.header.acceptance_datetime is a naive ET datetime. The same name gives two conventions.
Reproduction
Company/Ticker: AAPL
Relevant Forms: any
Minimal code to reproduce:
from edgar import Company
f = Company("AAPL").get_filings(accession_number="0000320193-26-000020")[0]
f.acceptance_datetime # 2026-07-31 14:01:02+00:00 (SEC: 10:01:02 UTC)
f.header.acceptance_datetime # 2026-07-31 06:01:02 (naive ET, correct)Additional Context
The SGML header (ACCEPTANCE-DATETIME in {accession}-index-headers.html or the .txt submission) is the only source that matched SEC's index page on every filing we checked. That holds for 2015–2026, both DST states, and every filer above.
Possible fixes, for the maintainers to choose:
- document that the submissions-JSON value is unreliable and point to
filing.header.acceptance_datetime; or - read the header when a precise time matters (one extra request per filing).
In our own code we moved to the header for this reason.
Activity
docs(filings): acceptance_datetime from get_filings() is not reliably…
arhancanli commentedon Oct 11, 2026
Some data that may help pin this down. I measured the same thing on 2026-10-11: 450 filings from 25 companies' submissions files (in each file, the 12 newest plus 6 from December to February), each compared with the "Accepted" time on its filing index page.
1. The error is per file, and the same for every filing in a file. Each file was either right for all 18 filings, or off by exactly one Eastern offset for all 18: +4 h for filings accepted in EDT, +5 h in EST. That held across forms (4, 8-K, 424B2, 13G, 10-K, 13F-HR ...), filing agents, times of day and acceptance years. No other offset appeared.
2. The same filing reads differently in two files. Apple's director Form 4s of 2026-02-26 are listed in Apple's file and in each director's own file:
So the value depends on the file it is read from, not on the filing.
3. In this sample the split follows each file's newest filing.
That is consistent with files rebuilt since early October being written with the extra offset. I can't see rebuild times directly (data.sec.gov sends no Last-Modified), so treat it as a hypothesis. One way to confirm: re-measure MSFT after its next filing. I saw no Eastern-time-labelled-UTC files today.
4. One request per file is enough, not one per filing. Because a file is uniform, one index page (or SGML header) calibrates every
acceptanceDateTimein it:k = (api - true_utc) / et_offset, one of -1, 0 or +1;api - k * offset, where the offset is 4 h or 5 h by DST at the true time.I'd still check
kagainst a second filing from the other DST period, and fall back to per-filing headers if the two disagree. I'd also recalibrate whenever the file is re-fetched, since a rebuild can change it.We hit this in our own event-study tool (day 0 depends on whether a filing came before or after the 16:00 close) and now calibrate per file this way: arhancanli/canlicapital#461 (MIT; about 40 lines in
acceptanceTimes). If a calibration like this would be welcome in edgartools, say as an opt-in correction inEntityFilingsor a helper next toparse_acceptance_datetime, I'm happy to open a PR.Reproduction for point 2, standard library only:
shahar-arbor commentedon Oct 13, 2026
Confirming the per-file rule on 2026-10-12: Apple's recent file and its 2015 paginated file (
CIK0000320193-submissions-001.json) are both +8 h from the header (k=+1 in your terms) on 9/9 sampled filings each; Tesla's two files are true UTC on 9/9 each.One exception that matters for the calibration step: on 2026-10-08, about 16 minutes after Apple's Form 4
0001140361-26-039158was accepted (18:30:32 ET), Apple's file listed it at2026-10-08T22:30:32Z(true UTC) while the rest of the file was +8 h. It now reads2026-10-09T02:30:32Z, like the rest. So a filing appended since the file's last rebuild can carry a different k than the file it sits in, and calibrating from the newest filing right after it lands can give the wrong k for the others. Your second-filing check catches this.Also, on 2026-09-30 whole files for MMAT, SVB, Heliogen and Bed Bath read as Eastern wall time labelled
Z(k=-1), so all three states occur.arhancanli commentedon Oct 13, 2026
Thanks, this is the piece I was missing, and it changes the calibration rule.
I just re-read Apple's file:
0001140361-26-039158now reads2026-10-09T02:30:32.000Z, so it matches what you saw after the rebuild.Two consequences for anyone calibrating:
The k=-1 files (Eastern wall time labelled
Z) are new to me; I saw none on 2026-10-11, and I haven't re-checked MMAT, SVB, Heliogen or Bed Bath, so I'm taking those from your measurement. A calibration that only tries k in {0, +1} would silently mis-time those, so the helper should test all three values and refuse to guess when none fits.If it's useful I can fold the mixed-state case into the PR proposal for edgartools: calibrate from older filings, check agreement across at least two, and treat any filing newer than the last disagreement as unverified. Disclosure: I maintain the event-study tool linked above.
MaxHaro commentedon Oct 15, 2026
Thanks @arhancanli and @shahar-arbor for the careful measurements. The per-file finding and the Form 4 exception are very useful.
@arhancanli, do you still plan to open the PR? No rush. I'm happy to leave it with you, help with tests or review, or pick it up if you'd rather hand it over.
@dgunning, I saw #1502 documents the caveat and leaves the value change for 6.0. One question before anyone writes code: would an opt-in correction be welcome in 5.x, with default output unchanged, or would you rather wait for 6.0? If it's welcome, do you prefer a method on
EntityFilingsor a keyword onget_filings()?The sketch I have in mind, default behavior unchanged:
EntityFilings.verify_acceptance()returns a copy with corrected times.filing.header.acceptance_datetimeon request.arhancanli commentedon Oct 15, 2026
Thanks @MaxHaro, and happy to have you on it. I'd rather not promise a date for the PR from my side: I'd like @dgunning's answer on 5.x opt-in versus 6.0 first, since that decides the shape (a method versus a keyword) and whether it is worth writing now. If it is welcome, I'm glad to review, add test cases from my measurements, or hand over the data. If you want to start before that, please don't wait for me.
Your sketch matches what we measured. Three points from the data above that I'd put into the tests:
0001140361-26-039158) was true UTC for a while inside a file whose other rows were +8 h. So "also check the newest filing" is right, but it should be checked against its own header and never used to pick k for the file.k * offsetuses 4 h or 5 h depending on which side the true acceptance time falls on. A fixture with one filing on each side (for example one in July and one in February from the same shifted file) catches an implementation that picks the offset from the shifted value.Zfiles shahar-arbor saw on 2026-09-30), plus a "nothing fits" case that must return None rather than a guess.One caveat on the sample: all of it is from 2026-10-07 to 2026-10-13, and the states have changed between days, so cassettes should record the fetch date.
dgunning commentedon Oct 21, 2026
Thanks @shahar-arbor, @arhancanli and @MaxHaro. Your measurements settled most of this. Here is the direction, plus one new data point.
New data point (2026-10-21): Tesla's file has flipped.
0001628280-26-049270was accepted at 21:02:31 ET (01:02:31 UTC on 07-23), andget_filings()now returns2026-07-23 05:02:31+00:00. That is +4 h, although it was correct when this issue was opened. Apple's 10-Q0000320193-26-000020is still +4 h (14:01:02 instead of 10:01:02 UTC), and its Form 40001140361-26-039158now reads02:30:32Z. So the offset follows each rebuild of a company's file, not just the company.What we won't build: a per-file offset correction. The rule changes with each rebuild, and the newest filings are exceptions to it. A correction that is right today and wrong after the next rebuild is worse than an honestly labelled value, especially for backtests, which need the same answer every run.
What we'd welcome for 6.1 (all additive, so it doesn't need to race the 6.0 cutoff):
filing.accepted_aton everyFiling. Read it from{accession}-index-headers.html, cached permanently because archive files never change. Return a tz-awareAmerica/New_Yorkdatetime, SEC's own clock and the one market sessions use.filings.accepted_at(), which returns the exact values for a wholeFilings. It is rate-limited and shows progress, so the cost is explicit, not hidden in an attribute read inside a loop.reported_acceptance_datetime(and theto_pandas()column), with the caveat in the docstring.EntityFiling.acceptance_datetimefiling.header.acceptance_datetimestays as the raw SGML header value.Cost of the exact value, measured on 2026-10-21 with the cache bypassed. All three sources agreed on the acceptance time for every filing:
.txt(whatfiling.headerdownloads today)-index-headers.html0001104659-26-116198That is one small request per filing. At SEC's 9 requests per second, 1,000 filings take about 2 minutes the first time and are cached after that. It is also far lighter than the current
filing.headerworkaround, which downloads the whole submission.@MaxHaro, the implementation is yours if you'd like it. @arhancanli, your per-file measurements and the Form 4 rebuild case would make good test cases. Please treat a filing appended since the last rebuild as a test of
reported_acceptance_datetime's caveat, not as something to correct. One PR is fine; please include a ground-truth assertion for one filing whoseAcceptedtime you read from its index page.arhancanli commentedon Oct 21, 2026
Thanks @dgunning. The direction is right, and I agree with not shipping a per-file correction. I did build one for our event-study tool; it needs a rule for the mixed state (a filing appended after a rebuild), and that rule would also change after the next rebuild.
I re-checked your two examples just now (2026-10-22), and both match your numbers:
-index-headers.htmlACCEPTANCE-DATETIME-index.htmAccepted (ET)0001628280-26-0492702026-07-23T05:02:31Z202607222102310000320193-26-0000202026-07-31T14:01:02Z20260731060102Both are +4 h against true UTC. Tesla was one of the files I measured as right on 2026-10-11, so the flip fits the rebuild picture.
Two test-case suggestions for the PR:
filing.accepted_atto the-index.htm"Accepted" value converted fromAmerica/New_York, and record the fetch date. The JSON value is the one that moves.reported_acceptance_datetimecan then be tested as "differs fromaccepted_atby the offset of the true time", which catches a helper that derives the offset from the shifted value.@MaxHaro, if it helps, I'll put the 450-filing table (accession, file value, index-page time) into a gist as a fixture source, with the fetch date on every row. Say so here and I'll link it.
lizhuojunx86 commentedon Oct 27, 2026
One more data point on the third state (Eastern wall time labelled
Z), from an earnings-timing audit I ran:acceptanceDateTimevalues with their index pages. 185 were true UTC and 43 were Eastern wall time labelledZ. All 43 came from issuers that are no longer incompany_tickers.json(delisted or acquired), which fits the MMAT, SVB, Heliogen and Bed Bath files reported above for 2026-09-30.So files of delisted issuers move between states too, which is one more reason to read
accepted_atfrom-index-headers.htmlrather than correct the JSON.For anyone timing earnings releases before 6.1 lands: earnings-session (https://github.com/lizhuojunx86/earnings-session, MIT) reads the "Accepted" time from the index page, keeps the JSON value next to it, and records which convention the JSON used for each filing (
utc,et_as_utcormismatch). Disclosure: I maintain it.arhancanli commentedon Oct 27, 2026
Thanks @lizhuojunx86, the re-check of seven files is the useful part: it shows the state follows each rebuild of a file, not the issuer, and a delisted issuer is no more stable than a listed one. That fits dgunning's Tesla and Apple examples above and is one more reason to pin tests to the index-page time rather than to any value read from the JSON.
One question about the 43: when you rechecked on 2026-10-27, were the seven still absent from company_tickers.json, or had any come back? If they were still delisted and flipped anyway, the flip is tied to the rebuild itself rather than to the ticker list, and a test fixture would have to assume that any file can change state between runs.
lizhuojunx86 commentedon Oct 28, 2026
They were still absent. Instead of the seven, I reran all 43 on 2026-10-28 (00:12 UTC): every
acceptanceDateTimenow matches the index-page time as true UTC, and none of the 43 filer CIKs is in that day'scompany_tickers.json. QDEL and VNOM trade again under new CIKs (1906324 and 2074176) after reorganizations, but the CIKs that filed these 8-Ks are still off the list.So all 43 changed convention while staying off the ticker list, which fits the rebuild explanation. For a fixture I'd assert only against the index-page time and let the JSON convention vary between runs.
arhancanli commentedon Oct 28, 2026
Thanks, rerunning all 43 settles it: they flipped together while staying off the ticker list, so the state belongs to the file's last rebuild, not to the issuer's status. It also means a fixture that pins the JSON convention will pass one day and fail the next without any code change, so asserting on the index-page time alone is the stable choice.
One thing that would pin the cause down: does earnings-session keep the response's Last-Modified or ETag next to the
utc/et_as_utclabel? If all 43 changed between your 2026-10-27 and 10-28 fetches, the headers should show a single rebuild window, and that would tell dgunning whether this is one batch job or several.lizhuojunx86 commentedon Oct 28, 2026
No. It caches the JSON body but not the headers, and SEC doesn't send either one for these files: data.sec.gov answers HEAD with 403, and a GET of a submissions file returns Date and request IDs but no Last-Modified or ETag (I checked all 35 filers today). So the only bound I have is my own fetch log: all 43 read as Eastern wall time on 2026-09-17, 7 of 7 read as UTC on 2026-10-27, and 43 of 43 on 10-28. A single rebuild somewhere in those six weeks is the simplest reading, but I can't date it more closely from outside.
arhancanli commentedon Oct 28, 2026
Thanks @lizhuojunx86, checking all 35 filers for Last-Modified and ETag and finding neither closes that route, and the fetch log is a fair bound: a rebuild some time between 2026-09-17 and 10-27, with the 7 of 7 and 43 of 43 reads agreeing.
One thing your log could separate from "a single rebuild": direction. The 185 files that were true UTC in September, have any of them gone the other way and read as Eastern wall time labelled
Zsince? If the 43 only ever moved fromet_as_utctoutcand none of the 185 moved back, it looks like a one-way correction applied to the stale files. If some of the 185 flipped the other way, the files are being regenerated by a process that can produce either convention, and a fixture should expect both on any given day.lizhuojunx86 commentedon Oct 28, 2026
None of the 185 went the other way. I re-read all 228 on 2026-10-28 (12:52 UTC): the 185 that were true UTC in September still are, and the 43 still read as true UTC, so in this sample the change was one-way.
The bigger change is in the files reported above as shifted. At 12:55 UTC I checked the 13 files listed as +1 earlier in this thread (AAPL, NVDA, AMZN, META, JPM, BAC, WMT, BRK, TSM, GME, PLTR, MRNA, Vanguard), plus MSFT, TSLA and GOOGL, against
-index-headers.html. Three filings per file: the newest, the newest accepted before 2026-10-23, and one from February. All 48 read as true UTC, on both sides of DST. The examples from this thread:0000320193-26-0000200001628280-26-0492700001140361-26-039158So all three states seen in this thread have converged on true UTC since 2026-10-22, the last day a +1 file was confirmed here. I can't tell from outside whether this is a fix or one more rebuild, so I'd keep the 6.1 design as planned. If it holds, the caveat in #1502 could name the affected period, since anyone who cached submissions JSON before late October still has the shifted values.
I can share the per-filing table if it's useful for fixtures.
arhancanli commentedon Oct 28, 2026
Thanks @lizhuojunx86, 48 of 48 on both sides of DST, taken from
-index-headers.htmlrather than the JSON, is the strongest evidence in this thread, and one-way across 228 files is a useful result. It also fits the one-time-correction reading better than the "process that produces either convention" one.Agreed on the caveat in #1502 naming the affected period. Anyone who cached submissions JSON before about 2026-10-22 keeps the shifted values, so a cache key that includes a fetch date, or a re-validation against the index headers for filings accepted before that date, is the practical advice.
One question for your fixtures: do your per-filing tables include the pre-2026-10-23 filings whose cached copies would still be shifted, or only the fresh reads? A fixture with both the old (
et_as_utc) and new value for the same accession would let the 6.1 parser assert that it normalises either one to the same UTC instant. The per-filing table would be useful for that.Batch 3j: API misc — EFTS company name, MultiFinancials include_quart…
docs(entity): document the acceptanceDateTime column as SEC's reporte…