SEC EDGAR API complete developer guide

SEC EDGAR API: Complete Guide to Financial Filings

You find out the SEC publishes financial data for every public company in the US, for free. Revenue, net income, total assets, cash flows, going back over a decade. You open the SEC EDGAR API documentation, craft a URL, hit it with curl, and get back a 2MB JSON blob with 47,000 lines. Now what?

This guide covers the SEC EDGAR API from a developer's perspective: which endpoints to use, how the data is structured, what the response actually means, and the gotchas that will waste your afternoon if nobody warns you first.

What SEC EDGAR Actually Offers Developers

SEC EDGAR API
A set of free, public JSON endpoints at data.sec.gov that serve structured financial data extracted from XBRL-tagged company filings.
XBRL
eXtensible Business Reporting Language. The tagging standard the SEC requires in financial filings, enabling machine-readable financial data.
CIK
Central Index Key. The SEC's unique 10-digit numeric identifier for each filing entity. Must be zero-padded in API URLs.
Company Facts
The primary SEC EDGAR endpoint. Returns every XBRL-tagged financial fact for a single company across all filings in one JSON response.

The SEC maintains a set of XBRL data APIs at data.sec.gov that aggregate financial facts from company filings, 10-K annual reports and 10-Q quarterly reports. These APIs are free, require no API key, and cover every company that files with the SEC: roughly 8,000+ active filers.

The data comes from XBRL tags embedded in each filing. When Apple files its 10-K, every number on the income statement, balance sheet, and cash flow statement is tagged with a standardized identifier like Revenues or NetIncomeLoss. The SEC extracts these tagged values and makes them queryable through three API endpoints.

Key facts before you start:

  • No authentication required - the APIs are fully public
  • Rate limit: 10 requests per second, enforced by IP address
  • User-Agent header is mandatory - format: CompanyName admin@company.com
  • Data format: JSON (no XML option for these endpoints)
  • Coverage: All SEC filers, with XBRL data back to approximately 2009

The Three SEC EDGAR API Endpoints That Matter

The three SEC EDGAR API endpoints: Company Facts, Company Concept, and Frames
SEC EDGAR exposes three XBRL API endpoints, each scoped differently.

Three endpoints cover all the financial data you need. Each one scopes the data differently: by company, by tag, or across all filers. If you need filing history instead of XBRL facts, use the separate data.sec.gov/submissions JSON guide for recent filings, accession numbers, and primary document URLs.

1. Company Facts (The Workhorse)

GET https://data.sec.gov/api/xbrl/companyfacts/CIK{cik10}.json

Returns every XBRL fact ever reported by a company across all filings. One call gives you revenue, net income, assets, liabilities, cash flows, every tagged value, organized by taxonomy and tag name.

Example for Apple (CIK 0000320193):

curl -H "User-Agent: MyApp admin@example.com" \
  https://data.sec.gov/api/xbrl/companyfacts/CIK0000320193.json

This is the endpoint you'll use 90% of the time. A single response contains the complete financial history, so you can extract annual, quarterly, and TTM data from one request per company.

2. Company Concept (Single Tag Lookup)

GET https://data.sec.gov/api/xbrl/companyconcept/CIK{cik10}/us-gaap/{Tag}.json

Returns all reported values for one specific XBRL tag. Useful when you only need a single metric:

curl -H "User-Agent: MyApp admin@example.com" \
  https://data.sec.gov/api/xbrl/companyconcept/CIK0000320193/us-gaap/Revenues.json

3. Frames (Cross-Company Snapshots)

GET https://data.sec.gov/api/xbrl/frames/us-gaap/{Tag}/{Unit}/{Period}.json

Returns a single metric for every company that reported it in a given period. Want every filer's revenue for Q4 2024?

curl -H "User-Agent: MyApp admin@example.com" \
  https://data.sec.gov/api/xbrl/frames/us-gaap/Revenues/USD/CY2024Q4.json

The period format depends on the fact type: use CY#### for annual duration facts, CY####Q# for quarterly duration facts such as revenue, and CY####Q#I for instant facts such as balance sheet values at quarter-end. The Frames endpoint is powerful for screening and cross-company analysis.

Your First API Call: Pulling Apple's Financials

Fetch Apple's company facts and pipe it through a JSON formatter to see the structure:

curl -s -H "User-Agent: MyApp admin@example.com" \
  "https://data.sec.gov/api/xbrl/companyfacts/CIK0000320193.json" \
  | python -m json.tool | head -50

The response structure (simplified):

{
  "cik": 320193,
  "entityName": "Apple Inc.",
  "facts": {
    "us-gaap": {
      "Revenues": {
        "label": "Revenues",
        "description": "Amount of revenue recognized from goods sold...",
        "units": {
          "USD": [
            {
              "end": "2024-09-28",
              "val": 391035000000,
              "accn": "0000320193-24-000123",
              "fy": 2024,
              "fp": "FY",
              "form": "10-K",
              "filed": "2024-11-01"
            },
            {
              "end": "2023-09-30",
              "val": 383285000000,
              "accn": "0000320193-23-000106",
              "fy": 2023,
              "fp": "FY",
              "form": "10-K",
              "filed": "2023-11-03"
            }
          ]
        }
      },
      "NetIncomeLoss": { "..." : "..." },
      "Assets": { "..." : "..." }
    }
  }
}

Each fact entry contains:

  • val - the reported value (whole dollars for USD, not thousands or millions)
  • end - the period end date
  • fy / fp - fiscal year and fiscal period (FY, Q1, Q2, Q3)
  • form - source filing type (10-K for annual, 10-Q for quarterly)
  • filed - the date the filing was submitted to the SEC
  • accn - the accession number, a unique identifier for the filing document

One detail that catches people: val is always in whole units. Apple's revenue of $391 billion shows up as 391035000000, not 391.035. There's no separate "scale" field.

Five Gotchas That Will Cost You Hours

The tag mapping problem: four companies reporting revenue under four different XBRL tags
The same financial concept mapped to four different XBRL tag names across filers.

The SEC EDGAR API is straightforward to call but deceptively hard to use reliably. These five issues trip up every developer who builds on this data.

1. CIKs Must Be Zero-Padded to 10 Digits

The API requires 10-digit CIKs with leading zeros. Apple's CIK is 320193, but the URL needs CIK0000320193. Miss the padding and you get a 404 with no helpful error message.

cik_padded = str(cik).zfill(10)
url = f"https://data.sec.gov/api/xbrl/companyfacts/CIK{cik_padded}.json"

To look up a CIK from a ticker, download https://www.sec.gov/files/company_tickers.json and build a local mapping. There is no ticker-to-CIK lookup endpoint.

2. The Same Metric Uses Different Tag Names

This is the biggest frustration with SEC EDGAR data. "Revenue" might be tagged as:

CompanyXBRL Tag Used
AppleRevenues
MicrosoftRevenueFromContractWithCustomerExcludingAssessedTax
Johnson & JohnsonSalesRevenueNet
AT&TRevenueFromContractWithCustomerIncludingAssessedTax

You need a semantic fallback map for each metric. Revenue alone has 4+ valid tags. Filter 10-K candidates to full-year durations. Keep semantic priority except for two scope proofs: (1) an exact-zero priority FY with another positive full-year revenue candidate (negative revenue is not zero); or (2) only when the nonnegative priority candidate does not reconcile, a reconciling alternative whose Q1-Q3 subtotal exceeds the priority FY. Otherwise keep priority.

3. Quarterly Flow Facts Can Be Year-to-Date

10-Q filings can contain both standalone-quarter and cumulative year-to-date facts for income statement items, while cash-flow items are commonly reported YTD. Classify each fact by its start and end dates instead of assuming every 10-Q value covers one quarter.

Normalize cumulative flows with Q2 = Q2_YTD - Q1 and Q3 = Q3_YTD - Q2_YTD. There is no Q4 10-Q. Derive the fiscal fourth quarter from the annual filing: Q4 = FY_10-K - Q1 - Q2 - Q3.

Balance sheet items are point-in-time snapshots and can be used as reported. This distinction trips up every developer building quarterly analysis for the first time.

4. The User-Agent Header Is Enforced

SEC blocks or throttles requests without a proper User-Agent. The expected format:

User-Agent: CompanyOrAppName ContactEmail@domain.com

Generic agents like Python-urllib/3.11 or curl/7.88.1 will fail silently or return intermittent 403 errors. Set a descriptive agent string and include a real email address.

5. No API Key Means No Higher Rate Limits

The SEC caps all requests at 10 per second by IP address. No registration, no premium tier, no way to get more. For bulk data needs, the SEC publishes a nightly companyfacts.zip archive containing all company facts data. It is currently about 1.4 GB compressed and grows over time. Download the archive once and process locally instead of making thousands of individual API calls.

Use ETag and If-None-Match headers on repeated requests. The SEC supports conditional requests, and a 304 Not Modified response saves bandwidth and counts toward your rate limit budget.

From Raw XBRL to Usable Financial Data

Pulling a response from the SEC EDGAR API takes one curl command. Building a reliable data pipeline on top of it takes weeks.

Here's a minimal Python example that extracts Apple's annual revenue:

import requests

CIK = "0000320193"
URL = f"https://data.sec.gov/api/xbrl/companyfacts/CIK{CIK}.json"
HEADERS = {"User-Agent": "MyApp admin@example.com"}

response = requests.get(URL, headers=HEADERS)
facts = response.json()["facts"]["us-gaap"]

# Merge multiple tags because companies can change tags over time
REVENUE_TAGS = [
    "Revenues",
    "SalesRevenueNet",
    "RevenueFromContractWithCustomerExcludingAssessedTax",
]

annual_by_end = {}
for tag in REVENUE_TAGS:
    usd_facts = facts.get(tag, {}).get("units", {}).get("USD", [])
    for entry in usd_facts:
        if entry.get("form") != "10-K" or entry.get("fp") != "FY":
            continue
        previous = annual_by_end.get(entry["end"])
        if previous is None or entry["filed"] > previous["filed"]:
            annual_by_end[entry["end"]] = entry

for entry in sorted(annual_by_end.values(), key=lambda x: x["end"], reverse=True)[:5]:
    print(f"{entry['end']}: ${entry['val']:,.0f}")

Output:

2025-09-27: $416,161,000,000
2024-09-28: $391,035,000,000
2023-09-30: $383,285,000,000
2022-09-24: $394,328,000,000
2021-09-25: $365,817,000,000

This minimal example merges annual tag history. Production selection keeps semantic priority except for two scope proofs: (1) an exact-zero priority FY with another positive full-year revenue candidate (negative revenue is not zero); or (2) only when the nonnegative priority candidate does not reconcile, a reconciling alternative whose Q1-Q3 subtotal exceeds the priority FY. Otherwise keep priority.

That covers revenue for one company. The full pipeline requires handling 23+ financial fields, 50+ tag variants, three period types, per-field duration classification for quarterly flows, Q4 derivation from the annual filing, derived calculations (free cash flow = CFO minus capex, with capex normalized as a positive spend), and edge cases like companies that switch tags between filings or report in non-USD currencies.

(Fundamentals Hub normalizes all of this automatically, standardized income statements, balance sheets, and cash flows as clean JSON, no XBRL parsing required.)

FAQ

Does the SEC EDGAR API require authentication?

No. The XBRL data APIs at data.sec.gov are public. You need a descriptive User-Agent header with your name and email, but there's no registration, API key, or OAuth flow.

How far back does the financial data go?

Most companies have XBRL-tagged data from 2009 or 2010, when the SEC began mandating interactive data in filings. Large accelerated filers adopted XBRL earliest. Smaller companies and recent IPOs may have shorter histories.

Can I get stock prices from SEC EDGAR?

No. SEC EDGAR provides financial statement data from regulatory filings (10-K, 10-Q), not market data. Filing data updates quarterly or annually. For stock prices, you need a market data provider.

Why do different companies use different XBRL tags for revenue?

The US-GAAP XBRL taxonomy includes several valid tags for revenue depending on the reporting context. Revenues is the broadest. RevenueFromContractWithCustomerExcludingAssessedTax became common after ASC 606 (the revenue recognition standard) took effect in 2018. Each company's accounting team picks the tag that best describes their revenue structure, which creates inconsistency across filers.

What is the companyfacts.zip bulk download?

The SEC publishes a nightly ZIP archive at https://www.sec.gov/Archives/edgar/daily-index/xbrl/companyfacts.zip containing the same data as the Company Facts API for all filers. It is currently about 1.4 GB compressed and grows over time. If you need data for hundreds or thousands of companies, downloading this archive once is more efficient than making individual API calls and respecting the 10 req/sec limit.

Skip the XBRL Parsing

Fundamentals Hub normalizes SEC EDGAR data into clean JSON. Search 8,000+ public companies and pull standardized statements for covered filings via REST API.

Free to use. 100 requests per 30 days.