How to Handle Restatements and Amended Filings in Financial Data
Your history looked stable. Then a prior year's numbers changed underneath you, a chart that was correct last quarter is now wrong for a period 2 years back, and nobody on your team touched that data. The company restated — and your pipeline was never built for it.
In this article, you will learn:
- How financial statement restatements arrive — amended filings, restated comparatives, standard adoptions and reclassifications
- Why a filing-keyed pipeline silently double-counts restated periods
- How to detect restatements early using the 8-K Item 4.02 non-reliance signal
- How to keep exactly one clean figure per period by keying on the period and reading reconciled values — with a free API
Your history looked stable. Then a prior year’s numbers changed underneath you. A chart that was correct last quarter is now wrong for a period 2 years back, and nobody on your team touched that data.
The company did. It restated — and your pipeline was never built to expect a single period to have more than one reported value.
What makes this one genuinely nasty is that it is one of the few data-quality problems that gets worse the longer your history runs and the less often you look at it. It lands precisely in the periods you have stopped watching, and waits.
How restatements arrive
Before you can defend against a restatement, you need to know all the shapes it takes — because it is not just amended filings.
The same period can be reported more than once, and it happens more ways than most pipelines assume. It is worth knowing all of them, because they look different in the filings but produce the identical data problem — one period, 2 values:
- An amended filing — a 10-K/A or 10-Q/A — corrects a filing that was already submitted. It carries a new accession and reports revised figures for a period you already loaded.
- A restated comparative — a later annual or quarterly report that presents a prior period “as restated,” with figures that differ from what that period’s original filing said. No amendment is filed; the correction simply appears in the next regular report.
- A retrospective accounting-standard adoption — when a company adopts a new standard for revenue, leases or credit losses, it often restates prior comparatives onto the new basis. This is not an error, but it changes historical numbers exactly as a restatement does.
- A reclassification — moving an amount between line items or segments. Net income may be unchanged while individual lines shift, which quietly breaks any check or model built on the affected lines.
In every case you now hold 2 values for one period: the original and the revised. That is not a bug in the data; it is the reporting process working. The bug is in a pipeline that assumed one period equals one value.
Big R, little r, and the 8-K that warns you
So you know the 4 shapes a restatement takes. The next question is whether you can see it coming — and the answer depends on which kind it is.
Not all restatements announce themselves the same way, and this distinction is worth learning properly, because it tells you exactly where to point your monitoring. A material error — a “Big R” restatement — requires the company to file an 8-K under Item 4.02, “Non-Reliance on Previously Issued Financial Statements,” and then a formal amendment. An immaterial error — a “little r” restatement — is corrected in the next periodic filing without an 8-K and often without an amendment, so it shows up only as a changed comparative. The practical consequence: if you wait for the 10-K/A, you catch the Big R restatements and silently miss the little r ones, which arrive folded into an ordinary quarterly report.
The 8-K Item 4.02 is the earliest signal you get. It is filed when the company concludes prior statements can no longer be relied upon — before the corrected numbers exist — so watching for it turns a restatement from something you discover months later in a chart into an event you can flag the day it is announced. Because 8-K sections are routed by item key, you look for the Item4_02 key specifically, and you pass it back verbatim — capital I, underscore separator — because a single .lower() silently breaks 8-K item lookups.
Why a filing-keyed load double-counts
You know how restatements arrive and how to spot them. Now the question is what they do to your data once they land — and the answer depends entirely on how you keyed your store.
When you build a store, your instinct will be to key rows on the filing — this 10-K produced these numbers. It feels completely natural. It is also the decision that breaks on your very first restatement. When the amendment, the restated comparative or the standard-adoption restatement lands, you insert a second set of rows for the same period, because they came from a different filing. Now a query for that year returns 2 answers, and depending on how you aggregate you either double-count it, average 2 contradictory figures, or pick one at random. The failure is silent, and it lands in history you are no longer looking at — which is the worst possible place for a silent failure, because you will trust that history in a backtest or a long-run chart precisely because it looked settled.
Key on the period, not the filing
And the fix is one idea, which you can apply in an afternoon: make the period the identity, not the filing. A value belongs to (ticker, statement, frequency, slug, normalizedDate) — not to the filing that reported it — so a revised figure for that period replaces the earlier one instead of joining it. This is the same key that makes a fundamentals database idempotent, covered in building a financial statements database, and restatements are the reason it matters most.
The Financial Statements API applies exactly this principle on the source side: it returns one reconciled value per period, reflecting the most current reported figures, so a restated year comes back as a single number rather than a pair you have to arbitrate:
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://data.businessquant.com"
def series(ticker, slug, view="IS", frequency="Annual", period="all"):
url = (f"{BASE_URL}/statements"
f"?ticker={ticker}&statement={view}"
f"&frequency={frequency}&period={period}&api_key={API_KEY}")
r = requests.get(url).json()
for category in r["data"].values():
for section in category["sections"].values():
if section["metadata"]["slug"] == slug:
# one value per normalizedDate: reconciled, latest reported
return {v["normalizedDate"]: v["reportedValue"]["raw"]
for v in section["values"]}
return {}
rev = series("AAPL", "revenue")
for d in sorted(rev)[-5:]:
print(d, rev[d]) # exactly one figure per year, no duplicates to resolve
Because each normalizedDate maps to a single value, there is nothing to merge. A period that has been restated simply carries its restated figure, and your series stays one-value-per-period.
Detect the amendment so you know to re-sync
Keying on the period solves the storage problem. But a revised value only lands in your store if you actually re-load after the amendment — so detection is the other half of the job.
Poll the SEC Filings API for the amended forms directly — the /A variants — on an interval you control, and pair that with watching for the Item 4.02 8-K that precedes a material restatement. Together they turn a restatement into an event you act on rather than something you find months later in a chart:
def amendments(ticker):
url = (f"{BASE_URL}/secfilings"
f"?ticker={ticker}&formtype=10-K/A,10-Q/A&page_size=20&api_key={API_KEY}")
return requests.get(url).json()["data"]
def non_reliance_flags(ticker):
# 8-Ks whose detected sections include Item4_02 = non-reliance = restatement coming.
url = (f"{BASE_URL}/secfilings"
f"?ticker={ticker}&formtype=8-K&page_size=50&api_key={API_KEY}")
out = []
for f in requests.get(url).json()["data"]:
if "Item4_02" in f.get("items", []): # pass item keys back verbatim
out.append((f["filingdate"], f["accession"]))
return out
for a in amendments("AAPL"):
# reportperiod tells you which period to re-sync; a new accession = a fresh amendment
print(a["filingdate"], a["formtype"], "period:", a["reportperiod"], a["accession"])
print("non-reliance 8-Ks:", non_reliance_flags("AAPL"))
An amendment carries a new accession and often only the documents that changed, so use its reportperiod to re-load just the affected window — then the period key does the overwrite for you. The items[] array on each filing is free routing metadata: it tells you which sections a filing contains, so you flag the non-reliance 8-K without fetching and scanning its body.
As-first-reported vs as-restated, and why backtests care
Now I have to complicate what I just told you, because there is one case where the sensible default is exactly wrong. Reading a period gives you the most current reported value for it. That is right for analysis, screening and current-state reporting — you want today’s best understanding of 2023, not a figure the company has since corrected. But a backtest wants the precise opposite. If you test a strategy over 2023 using the restated 2023 numbers, you have given your model information the market did not have when it would have traded — look-ahead bias, sourced straight from the restatement.
This is why the 3 dates on a filing matter. The reportperiod is the fiscal period the numbers describe; the filingdate is when EDGAR accepted the filing; the publishdatetime is the minute it went public. Using reportperiod as “when the market knew” is itself a look-ahead bug, restatement or not. For a point-in-time backtest you want the value as it stood on the knowledge date, which means reconstructing the as-first-reported figure. You get there by pulling the original filing for that period through the SEC Filings API — the original accession, before the amendment — rather than the reconciled series. Decide which axis your use case needs; the reconciled latest is the right default, and the as-filed original is a distinct, deliberate requirement, not a fallback.
Make your own store restatement-proof
If you persist the data, mirror the same discipline: upsert on the period key so a re-load overwrites rather than appends. Then keeping current after a restatement is just re-loading the recent window and letting the key do the work:
# Re-load a recent window on a schedule; the period key overwrites restated years.
def refresh(ticker, upsert, view="IS", frequency="Annual"):
for date, raw in series(ticker, "revenue", view, frequency, period="5y").items():
upsert(ticker, view, frequency, "revenue", date, raw) # ON CONFLICT DO UPDATE
# A restated 2023 figure replaces the old 2023 row. No second row, no double count.
The 2 ideas together — a source that returns reconciled values, and a store that upserts on the period — mean a restatement is a quiet overwrite instead of a data-integrity incident.
Edge cases to keep in mind
The detect-and-upsert pattern above handles the main path. A few things are worth knowing before you rely on it in production.
Little r restatements have no amendment to watch for. An immaterial correction arrives folded into the next regular filing with no 10-K/A and no Item 4.02 8-K, so polling amendments alone will miss it. Re-loading a recent window on a schedule catches it anyway, because the reconciled series already carries the corrected comparative.
Period articulation can legitimately break around a restatement. If 4 quarters do not sum to the year, a restatement of one period may be the honest cause rather than a parse error. Keep this in mind when you run the checks from validating parsed statements.
Preliminary-to-final is a related reconciliation. The 8-K earnings figure reconciling to the final 10-Q is the same “one period, updated later” pattern on a shorter timescale, described in getting earnings before the 10-Q.
The default series reflects current reported figures. That is what you want for analysis and comparison. If your use case instead needs the original as-first-filed number for a historical audit or a point-in-time backtest, treat that as a distinct requirement and source it from the original filing.
Start Building
So, the short version. Restatements break pipelines that quietly assumed one period equals one value — and they arrive in more ways than you expect, from formal amendments to quiet reclassifications to accounting-standard adoptions that restate everything at once.
Keep the period as the identity, on the source side and in your own store, and a revised figure overwrites the old one instead of doubling it. Read reconciled values per period. Watch the amendments and the non-reliance 8-Ks so you know when to re-sync. Upsert on the period key.
Do those 4 things and your history stays clean on the day a company amends it — which, if you hold a long enough series, is a day that will come.
- Financial Statements API — one reconciled value per period, keyed on normalizedDate.
- SEC Filings API — the amended and original filings, plus the items[] array that flags a non-reliance 8-K.
- Section Extraction API — the 8-K Item 4.02 section as clean HTML, if you want the wording.
Get an API key, pull a full-history series, and confirm every period returns exactly one figure. If it does, you have already avoided the bug this whole page is about.
Frequently asked questions
How do restatements show up in financial data?
Several ways. An amended filing such as a 10-K/A corrects a filing you already loaded, a later report can present a prior period as restated, a company can restate comparatives when adopting a new accounting standard, and a reclassification can move amounts between lines. Each leaves one period with 2 reported values.
What is the difference between a Big R and a little r restatement?
A Big R restatement corrects a material error and requires an 8-K under Item 4.02 declaring non-reliance on the prior statements, followed by a formal amendment. A little r restatement corrects an immaterial error in the next periodic filing with no 8-K and often no amendment, so it appears only as a changed comparative. Watching only for amendments catches the first and misses the second.
How can I detect a restatement early?
Poll for the amended forms, the 10-K/A and 10-Q/A variants, and watch for 8-K filings whose detected sections include Item4_02, the non-reliance item, which is filed before the corrected numbers exist. The items array on each filing flags the non-reliance 8-K without fetching its body.
Why does a naive pipeline double-count restatements?
Because it keys rows on the filing. When an amendment or restated comparative lands, it inserts a second set of rows for the same period, so a query returns 2 answers and aggregation either double-counts the period or picks a value at random. The failure is silent and lands in older history you no longer inspect.
Does the API give original as-filed numbers or restated ones?
It returns the most current reconciled figure for each period, so a restated year carries its restated value and the series stays one value per period, which is the right default for analysis. If you specifically need the original as-first-filed number for a point-in-time backtest or a historical audit, source it from the original filing through the SEC Filings API and treat it as a separate requirement.
Is the Financial Statements API free to use?
Yes. Sign up for a free API key and every period returns a single reconciled value keyed on its normalized date, so restatements do not double-count in your series.