Every 10-K and 10-Q filed with the SEC carries financial statements in XBRL, the machine-readable tagging layer. In theory that means every company's numbers are structured and comparable. In practice, filers name, arrange, and extend their XBRL tags differently, and no two companies' statements line up without a lot of manual cleanup.
3spread solves that. Every statement, regardless of filer or archetype, maps to one consistent grid: the same sections, the same line-item keys, the same sign conventions. This guide pulls a company's annual income statement, extracts the key figures, and shows how to get the same statement as it was known at a past date for backtesting.
Everything below runs on the free Community tier: every dataset, 10,000 requests per day (600 per minute), no credit card.
Setup
pip install py3spread
export THREESPREAD_API_KEY="sk_live_..."Grab a key at 3spread.com/auth/signup. The client reads
THREESPREAD_API_KEY from the environment, so you never put it in the code.
Pull the latest annual income statement
from decimal import Decimal
from py3spread import Client
TICKER = "WMT"
with Client() as client:
page = client.financials.statements(
ticker=TICKER,
version="latest",
statement_type="inc",
period_length=12,
limit=1,
)
stmt = page["data"][0]
grid = stmt["statement_json"]
entity = grid["header"]["entity"]
period = grid["header"]["period_end"]
print(f"{entity} — Income statement, period ending {period}")
print(f"QA score: {stmt['score_composite']}")
print()
for section_name, section in grid["sections"].items():
for line_key, line in section.items():
if line["value"] is None:
continue
val = Decimal(str(line["value"]))
print(f" {line_key:<40} {val:>20,.0f}")The response is the standardized grid. Each section (revenue, cost_and_expenses,
non_operating, net_income, per_share) contains line items keyed by a stable
category name, each with a value, a source (reported, backfilled, or absent),
and the members object that traces each value back to the exact XBRL facts the
filer reported. You never have to touch raw XBRL.
Extract the numbers you actually want
The grid is a nested dict, so pulling specific lines is just key access:
revenue = Decimal(str(
grid["sections"]["revenue"]["total_revenue"]["value"]
))
operating_income = Decimal(str(
grid["sections"]["cost_and_expenses"]["operating_income"]["value"]
))
net_income = Decimal(str(
grid["sections"]["net_income"]["net_income_to_parent"]["value"]
))
margin = (operating_income / revenue * 100) if revenue else 0
print(f"Revenue: ${revenue:>16,.0f}")
print(f"Operating income: ${operating_income:>16,.0f}")
print(f"Net income: ${net_income:>16,.0f}")
print(f"Operating margin: {margin:>15.1f}%")The line keys are stable across filers. total_revenue, operating_income,
net_income_to_parent, eps_diluted mean the same thing whether the company is
Walmart, JPMorgan, or an insurance underwriter. The spine (commercial_industrial,
interest_spread, etc.) determines which sections and line items appear, but
within a spine the keys are consistent.
Get the statement as it was known on a past date
Every fiscal period has multiple versions: the original filing, any amendment,
and the comparative column of each later filing that restates it. The version
parameter controls which one you get.
# What was known as of January 1, 2024? Avoids look-ahead bias.
pit_page = client.financials.statements(
ticker=TICKER,
version="as_of:2024-01-01",
statement_type="inc",
period_length=12,
limit=1,
)
if pit_page["data"]:
pit_stmt = pit_page["data"][0]
pit_grid = pit_stmt["statement_json"]
pit_revenue = Decimal(str(
pit_grid["sections"]["revenue"]["total_revenue"]["value"]
))
print(f"\nAs of 2024-01-01, latest known revenue: ${pit_revenue:,.0f}")
print(f" (period ending {pit_grid['header']['period_end']})")version="latest" gives you the most recent version, including restatements.
version="original" gives you exactly what the company first filed. And
version="as_of:YYYY-MM-DD" gives you the newest version that was public on
that date, which is what you want for backtests: it prevents future restatements
from leaking into historical analysis.
Where to take it
Swap the single ticker for a watchlist and pull the same line items across all of
them in a loop. Because the keys are consistent, the code does not change per
company. Pull statement_type="bs" for the balance sheet or statement_type="cf"
for the cash flow statement, same parameters, same grid structure.
The metrics endpoint gives you the same line items as a time series, so instead
of pulling one statement at a time you can chart a company's revenue or net income
across every period in one call.
The dataset
Full endpoint reference, response schemas, and every parameter are on the financials dataset page. The interactive API reference has the complete specification.
The py3spread client (v0.3.0) wraps all 11 financials endpoints with typed method signatures and automatic pagination. See the client documentation and the financials example script for statements, metrics, ratios, factors, and percentile ranks in one file.