Financial Literature

US Securities and Exchange Commission

SEC EDGAR APIs — Fundamentals as filed, every filing's index, and insider trades — from the regulator, not a reseller

When a number has to be the one in the filing — revenue, net income, shares outstanding — or you want the list of everything a company has filed. companyfacts gives the whole XBRL history of one company in one call; frames gives one metric across every filer for one period.

fundamentalsfilingsUnited States
Asset classesfundamentals · filings
Frequencyas filed → annual
History2009 → today
Limit · costNo key · 10 requests/second, declared User-Agent required
KeyNo key — No key, but every request must carry a User-Agent with a name and contact email or EDGAR blocks it.
Formats · pull withjson · xbrl · html — python
LicenceUS government work, public domain; filings are public records.
RedistributeYes, with attribution
Best for“What did the company actually report?” · “Who is selling their own stock? (Form 4)”
When to use it

Reach for it when…

When a number has to be the one in the filing — revenue, net income, shares outstanding — or you want the list of everything a company has filed. companyfacts gives the whole XBRL history of one company in one call; frames gives one metric across every filer for one period.

Not for: Prices, ratios or screens (a data vendor computes those), non-US companies (EDINET for Japan, OpenDART for Korea), anything before XBRL (2009).

How to read it

Units, revisions, traps

Units. XBRL values in the filing's units (USD, shares, pure). Each fact carries the period it covers — a 10-K revenue fact is a fiscal year, a 10-Q fact is a quarter or year-to-date.

Revisions. Restated numbers appear as new facts with a later filing date; the original fact stays. Filter on 'form' and 'fy' to avoid double-counting.

  • The same concept appears under several tags (Revenues, RevenueFromContractWithCustomerExcludingAssessedTax); pick per company.
  • Duration facts overlap: year-to-date Q3 includes Q1 and Q2 — use the 'frame' field (CY2024Q3) to get clean quarters.
  • CIKs are ten digits, zero-padded, in the URL.

Classic mistake: Summing the four 10-Q revenue facts and the 10-K fact and getting two years' worth.

How to use it in your own work

Three recipes

Each recipe: Python · Excel · Sheets, with how to read the result. Python recipes run under pandas; recipe 1 is re-run by the weekly check where the source allows it.

How to use it in your own work

Copy the snippet, change the series id, read the result the way the footer says. Replace YOUR_…_KEY with your own key where one is needed.

Recipe 1A company's revenue history, as filed

# Apple: annual revenue from the XBRL facts (CIK 320193). Declare who you are or EDGAR refuses.
import requests, pandas as pd
H = {"User-Agent": "Your Name [email protected]"}
j = requests.get("https://data.sec.gov/api/xbrl/companyfacts/CIK0000320193.json", headers=H, timeout=60).json()
facts = j["facts"]["us-gaap"]["RevenueFromContractWithCustomerExcludingAssessedTax"]["units"]["USD"]
df = pd.DataFrame(facts)
fy = df[(df.form == "10-K") & (df.fp == "FY") & df.frame.notna() & ~df.frame.str.contains("Q", na=False)]
print(fy[["fy", "end", "val", "frame"]].drop_duplicates("frame").tail(6))
How to read the resultThe 'frame' field (CY2024) is the clean fiscal-year tag; without it, year-to-date and restated facts double-count.

Recipe 2One metric across every filer (frames)

# every company that reported Revenues for calendar 2024
import requests, pandas as pd
H = {"User-Agent": "Your Name [email protected]"}
j = requests.get("https://data.sec.gov/api/xbrl/frames/us-gaap/Revenues/USD/CY2024.json", headers=H, timeout=60).json()
df = pd.DataFrame(j["data"]).sort_values("val", ascending=False)
print(df[["entityName", "val", "end"]].head(10))
How to read the resultCompanies tag revenue under different concepts; a filer missing here may be under RevenueFromContractWithCustomerExcludingAssessedTax.

Recipe 3Insider trades: the Form 4 list

# every Form 4 a company filed, newest first, from the submissions index
import requests, pandas as pd
H = {"User-Agent": "Your Name [email protected]"}
j = requests.get("https://data.sec.gov/submissions/CIK0000320193.json", headers=H, timeout=60).json()
f = pd.DataFrame(j["filings"]["recent"])
form4 = f[f.form == "4"][["filingDate", "accessionNumber", "primaryDocument"]]
print(form4.head(10))
# open one: https://www.sec.gov/Archives/edgar/data/320193/<accessionNumber without dashes>/<primaryDocument>
How to read the resultA sale under a 10b5-1 plan (marked on the form) was scheduled months earlier — it says nothing about today's view.

Series → question map

The ids we use from SEC EDGAR APIs, each with the question it answers. The catalog's compare view reads the concept tags behind these rows.

SeriesAnswersRegionConcept
companyfactsWhat did the company report, by year and quarter?United Statesfundamentals
framesOne metric across all filers for one periodUnited Statesfundamentals
submissionsWhat has the company filed?United Statesfilings
Form 4Are insiders buying or selling?United Statesinsider

Compare with

Same question, different source: OpenDART, EODHD, Twelve Data. The compare view lines up coverage, frequency, history and access side by side and lists what the combination makes possible.

Open compare: SEC EDGAR APIs · OpenDART · EODHD →

Questions readers ask

Why does EDGAR return 403?

No User-Agent. Send 'Name email' in the header and stay under ten requests a second.

Is EDGAR data real-time?

Filings appear within minutes of acceptance; the XBRL APIs update as the filing is processed, usually the same day.

Where do I find a company's CIK?

www.sec.gov/cgi-bin/browse-edgar?company=name, or the company_tickers.json file at www.sec.gov/files/company_tickers.json.

Educational only — we explain, we never advise · snippet licence: public domain · corrections to [email protected], fixed within a day and logged in the changelog.