In this Article
The SEC EDGAR API lets you retrieve filing metadata and structured financial facts, then follow an accession to its public filing documents. Start with a company identifier, download the relevant records, and validate each value against its reporting context.
Our quick run on October 17, 2026 retrieved Apple’s filing metadata, a public 10-K, and annual revenue and net income. This small snapshot shows a working path and several parsing traps; it is a reproducibility check, not a benchmark or a guarantee of future access.
Key Facts
- Our quick run on October 17, 2026 covered 13 requests.
- Every request using our contact User-Agent returned HTTP 200.
- The run used a direct datacenter connection without a proxy.
- The tested script downloaded one primary 10-K document.
- The CSV contained 19 fiscal-year rows.
- No observed response included X-RateLimit-* or Retry-After headers.
How do you use the SEC EDGAR API with Python?
Resolve the ticker to a CIK, request submissions, select a 10-K, and download its primary document. The tested script below also exports annual revenue and net income.
Use Python with requests installed. Save this as sec_edgar.py, replace the example contact identity with your real name or organization and monitored email, then run python sec_edgar.py.
"""SEC EDGAR API: ticker -> CIK -> filings -> latest 10-K -> annual revenue/net income CSV.
Official, free, no API key. SEC asks for a User-Agent with your name/company and a contact email,
and at most 10 requests per second. Direct connection is the right route; no proxy needed."""
import csv, json, time
from datetime import date
import requests
HEADERS = {"User-Agent": "DataImpulse research [email protected]"} # put YOUR name and email here
REVENUE_TAGS = ["RevenueFromContractWithCustomerExcludingAssessedTax", "Revenues", "SalesRevenueNet"]
def get(url):
for attempt in range(1, 4):
r = requests.get(url, headers=HEADERS, timeout=30)
if r.status_code == 200:
time.sleep(0.2) # stay far below 10 requests/second
return r
print(f"{url} -> HTTP {r.status_code}, retry {attempt}")
if r.status_code == 403:
raise SystemExit("403: check your User-Agent (name + email) and slow down")
time.sleep(5 * attempt)
r.raise_for_status()
def ticker_to_cik(ticker):
data = get("https://www.sec.gov/files/company_tickers.json").json()
for row in data.values():
if row["ticker"] == ticker.upper():
return f'{row["cik_str"]:010d}' # 10 digits, zero-padded
raise ValueError(f"unknown ticker {ticker}")
def latest_10k(cik):
sub = get(f"https://data.sec.gov/submissions/CIK{cik}.json").json()
recent = sub["filings"]["recent"] # parallel lists, newest first
i = recent["form"].index("10-K")
acc = recent["accessionNumber"][i]
url = (f"https://www.sec.gov/Archives/edgar/data/{int(cik)}/"
f"{acc.replace('-', '')}/{recent['primaryDocument'][i]}")
print(f'{sub["name"]}: 10-K {acc} filed {recent["filingDate"][i]}')
return url
def annual_values(us_gaap, tags):
"""Full-year 10-K values keyed by period end date; first tag that has a year wins."""
out = {}
for tag in tags:
for f in us_gaap.get(tag, {}).get("units", {}).get("USD", []):
if f.get("form") != "10-K" or "start" not in f:
continue
days = (date.fromisoformat(f["end"]) - date.fromisoformat(f["start"])).days
if 350 <= days <= 380: # one fiscal year, not a quarter
out.setdefault(f["end"], f["val"]) # keep the first-filed value (later 10-Ks may restate)
return out
def main(ticker="AAPL"):
cik = ticker_to_cik(ticker)
doc_url = latest_10k(cik)
html = get(doc_url).content
with open(f"{ticker}_10k.htm", "wb") as fh:
fh.write(html)
print(f"saved 10-K primary document: {len(html) / 1e6:.1f} MB")
facts = get(f"https://data.sec.gov/api/xbrl/companyfacts/CIK{cik}.json").json()
us_gaap = facts["facts"]["us-gaap"]
revenue = annual_values(us_gaap, REVENUE_TAGS)
net_income = annual_values(us_gaap, ["NetIncomeLoss"])
rows = [{"fiscal_year_end": end, "revenue_usd": revenue.get(end), "net_income_usd": net_income.get(end)}
for end in sorted(set(revenue) | set(net_income))]
with open(f"{ticker}_annual.csv", "w", newline="") as fh:
writer = csv.DictWriter(fh, fieldnames=rows[0].keys())
writer.writeheader()
writer.writerows(rows)
print(f"{len(rows)} fiscal years -> {ticker}_annual.csv")
for row in rows[-3:]:
print(row)
if __name__ == "__main__":
main("AAPL")
The script sets a 30-second timeout and stops immediately on HTTP 403. It assumes a 10-K exists in the recent list and that annual facts exist; another company may violate either assumption.
Use the 4-step filing retrieval model to separate downloading from interpretation:
- Identify CIK: match the ticker and confirm the company name.
- Discover accession: select the form and filing date.
- Retrieve document/facts: save the primary HTML and collect numerical records.
- Validate context: check units, periods, concepts, and filing provenance.

Our measurement sources are sec_edgar.py, its AAPL_annual.csv output, and the saved AAPL_10k.htm. The CSV held 19 fiscal years, ending from 2007-09-29 through 2025-09-27. FY2025 revenue was $416.161 billion and net income was $112.010 billion.
The Linux environment used Python 3.12 and requests; the exact requests version was not recorded. Nine measurement calls used sequential requests with two-second pauses. The final four-request script run used its visible 0.2-second pauses and took about 1.6 seconds. The total was 13 requests, exceeding the initial ten-request plan.
The five successful routes below summarize the 15:43-15:44 UTC measurement window on October 17, 2026. Body sizes are rounded observations, not exact byte counts. Exact per-request timestamps and Content-Type header strings were not recorded; content labels describe the decoded bodies.
| UTC window | Exact URL | HTTP | Content | Body size | Elapsed | Selected fields | Extracted count | Parsing outcome |
|---|---|---|---|---|---|---|---|---|
| 15:43-15:44 | https://www.sec.gov/files/company_tickers.json | 200 | JSON | 781 KB | 175 ms | cik_str, ticker, title | 10,443 tickers | AAPL resolved |
| 15:43-15:44 | https://data.sec.gov/submissions/CIK0000320193.json | 200 | JSON | 160 KB | 62 ms | filings.recent, filings.files | 1,000 recent rows; 1 older file reference | 10-K selected |
| 15:43-15:44 | https://data.sec.gov/api/xbrl/companyfacts/CIK0000320193.json | 200 | JSON | 3.6 MB | 180 ms | facts, units, val | 503 us-gaap concepts; 2 dei concepts | Annual values extracted |
| 15:43-15:44 | https://data.sec.gov/api/xbrl/companyconcept/CIK0000320193/us-gaap/Revenues.json | 200 | JSON | 2.2 KB | 211 ms | units.USD, end, val | 11 facts | Latest period FY2018 |
| 15:43-15:44 | https://www.sec.gov/Archives/edgar/data/320193/000032019325000079/aapl-20250927.htm | 200 | HTML with inline XBRL | 1.5 MB | 131 ms | Primary document | 1 document saved | No HTML fact extraction attempted |
These timings describe one host and one short session. They cannot establish availability, typical latency, or safe concurrency.
Is the SEC EDGAR API free, and do you need a key?
Public SEC EDGAR downloads cost $0 (as of October 2026), and the public data APIs require no authentication or API key. See the SEC’s download guidance and API documentation.
EDGAR is the SEC’s disclosure system. Reading public disclosures is separate from submitting filings or administering filer accounts. The API documentation explicitly separates public data access from filer APIs and token management.
For this workflow, begin with the public URLs and your contact User-Agent. Treat any dashboard purchase or token setup as a separate integration decision. Before entering credentials anywhere, check the hostname and determine whether you are accessing public records, submitting a filing, or buying another company’s service.
Our run incurred no API charge and used no proxy traffic. Those observations do not include ordinary server, storage, or engineering costs.
Which endpoint returns submissions, companyfacts, companyconcept, or frames?
Choose submissions for discovery and XBRL endpoints for numerical facts. The SEC documents a scope limit: aggregated XBRL facts use standard taxonomies and apply to the whole entity (as of October 2026).
These templates follow the official endpoint reference. Replace {cik} with ten digits, including leading zeros.
- Submissions:
https://data.sec.gov/submissions/CIK{cik}.json - Companyfacts:
https://data.sec.gov/api/xbrl/companyfacts/CIK{cik}.json - Companyconcept:
https://data.sec.gov/api/xbrl/companyconcept/CIK{cik}/{taxonomy}/{tag}.json - Frames:
https://data.sec.gov/api/xbrl/frames/{taxonomy}/{tag}/{unit}/{period}.json
| X | Use X when… | Avoid X when… |
|---|---|---|
| Submissions | Finding filings | Extracting financial values |
| Companyfacts | Reading company-level standardized facts | Needing complete narrative/custom dimensions |
| Companyconcept | Selecting one concept | Needing all concepts |
| Frames | Comparing calendar-aligned facts | Requiring identical fiscal periods |
| Bulk archives | Building historical datasets | Fetching one filing |
In our submissions response, filings.recent contained parallel lists. Keep accessionNumber, form, filingDate, reportDate, and primaryDocument aligned by index. Sorting one list independently would silently join unrelated records.
The response also referenced an older submissions file covering 1994-2015. For historical work, follow filings.files; the SEC documents these additional files in its submissions reference. A recent-record lookup alone is insufficient.
Our companyfacts response nested records under taxonomy, concept, and units. Retain start, end, val, accn, form, and filed when building your own table. The example CSV deliberately discards much of that provenance for readability; a production dataset should preserve it.
Frames select facts aligned to requested calendar periods, which can contain different fiscal dates. Review the actual dates before comparing companies.
How do you parse filing HTML and XBRL correctly?
Use filing HTML for narrative and document context, and structured facts for numerical extraction. A correct number still needs the right concept, unit, reporting period, and source accession.
Our clearest trap was Apple’s Revenues concept: it returned successfully but ended in FY2018. A separate request for RevenueFromContractWithCustomerExcludingAssessedTax returned 117 USD facts through Q3 FY2026. Successful HTTP status therefore did not imply current coverage.
The script tries several revenue concepts and accepts durations of 350-380 days. This is a tutorial heuristic, not a universal fiscal-calendar rule. Inspect actual start and end dates; do not select annual facts solely from fy and fp, which describe the filing’s fiscal context.
Its first-filed behavior also needs care. setdefault preserves the first encountered value for each end date, with tag priority taking precedence. The code does not explicitly sort by filing date, so it cannot guarantee earliest-filed selection for arbitrary responses. Later restatements and 10-K/A amendments need a deliberate policy.
For inline XBRL, visible text alone can omit numerical meaning. Transformations, scale, and sign affect the extracted value; context and units must remain attached. These mechanics are explained in XBRL International’s tagging guidance.
For segment dimensions or custom tags, inspect the original filing and taxonomy relationships. Avoid matching labels alone. Before accepting a value, compare its statement position, period, unit, and accession with the report.
For exhibits, begin with the filing’s document list and follow exhibit references, including references to earlier filings. The SEC’s exhibit guidance explains why a listed exhibit may require another document. Saving the primary 10-K is not equivalent to downloading every attachment.
What are the rate limits and automated-access rules?
The published ceiling is 10 requests per second per user across machines (as of October 2026), with an identifying User-Agent. The ceiling comes from SEC developer resources, not our measurements.
Use one coordinated request budget across workers, retries, scheduled jobs, and hosts. Begin conservatively and cache completed downloads. Our guide to API rate limiting explains why separate worker delays can still produce aggregate bursts.
In our run, contact-identified requests succeeded. Two default python-requests User-Agent calls returned 403 HTML: one said “Your Request Originates from an Undeclared Automated Tool” and another said “Request Rate Threshold Exceeded.” This suggests identification mattered, but the small comparison cannot isolate the cause of either block.
No observed 200 or 403 response included X-RateLimit-* or Retry-After headers. Missing headers do not remove the published limit. The JSON responses were gzip-compressed, so decoded body size differed from transferred size.
Stop on blocking, inspect the identity and aggregate traffic, and consult the SEC security policy. For transient failures, use bounded backoff and honor Retry-After when present. The example handles selected HTTP failures, but transport exceptions terminate it. Our Python retry guide covers broader retry design.
No proxy was necessary for our run. For other financial sources, networking choices for financial data may be relevant, but changing IP addresses does not increase the SEC request allowance.
How do you download years of 10-K and 10-Q data?
Build a historical inventory first, then download only the documents or facts your analysis needs. Keep metadata, filing documents, and extracted financial datasets separate.
The SEC’s nightly submissions archive and companyfacts archive contain JSON data, not complete filing-document collections. Their contents and refresh cycle are described in the bulk API documentation.
For documents, use filing histories or EDGAR filing indexes to build accession-based work queues. Store each accession with its CIK, form, filing date, report date, and document URL.
The quarterly Financial Statement Data Sets have a coverage limit: they contain extracted primary-statement information, not every disclosure (as of October 2026). The SEC also warns about extraction errors and incomplete metadata. Consult the linked format documentation before importing a ZIP into your database.
Keep source JSON, HTML, or XML alongside your normalized CSV output. Record download time and a content hash. Deduplicate document jobs by accession and filename, while preserving distinct amended accessions.
Schedule discovery independently from parsing. A completed download should be reusable when extraction logic changes. Retry failed jobs from a manifest rather than restarting an entire history, and periodically reconcile that manifest with the source inventory.
Before expanding coverage, define your company universe, date range, forms, and revision policy. Our data-source planning guide helps frame those choices.
Which Python library fits your task?
Choose by the operation you need: JSON retrieval, document downloading, filing analysis, or XBRL validation. No library removes the need to verify reporting context.
The following project-documented capabilities remain subject to the underlying SEC access limit (as of October 2026). These are third-party projects, not SEC-maintained SDKs.
| Project | Documented role | Selection guidance |
|---|---|---|
| sec-EDGAR-api | JSON wrapper with submissions pagination | Use for endpoint-oriented workflows |
| sec-EDGAR-downloader | Filing downloads with form and date selection | Use for a local document collection |
| EdgarTools | Filing analysis and XBRL interfaces | Evaluate for analyst-facing workflows |
| secedgar | Filing retrieval across CIKs and form types | Evaluate for batch collection |
| Arelle | XBRL processing and validation | Use when taxonomy and context checks matter |
| Commercial sec-api Python client | Client for the separate SEC-API.io service | Evaluate as a separate vendor integration |
Before adoption, test a recent filing, an older filing, an amendment, and an empty result. Record the exact dependency version and inspect release activity. A convenient method name is not evidence that its returned rows match your analytical definition.
Also inspect hidden network activity. Automatic pagination and taxonomy retrieval can add requests beyond the call visible in application code. Include those requests in the same scheduler budget.
Does EDGAR offer a documented full-text search API?
The official documentation reviewed here does not establish a supported full-text API contract. Do not infer one from requests observed in a browser’s network panel.
The SEC search guidance discusses public full-text search. For an automated implementation, anchor dependencies to published documentation and avoid promising endpoint stability or quotas that the SEC has not documented.
For repeatable text research, retrieve a defined filing collection and index the saved documents locally. Preserve accession and document identifiers with every search result so a reviewer can return to the source passage.
For Form 4 work, start with filing discovery and inspect the ownership document rather than forcing transaction records into an annual-financial-facts schema. Keep reporting owner, transaction date, security, and footnotes together during extraction.
If your task also needs market prices, treat that as a separate data-source decision. Our stock market data API guide covers that broader category.
What reuse and licensing risks should you check?
Separate permission to access a source from the rights and responsibilities attached to your intended reuse. Keep source attribution and review the material you plan to republish.
The SEC webmaster FAQ describes public filing content as free to access and reuse. Its website dissemination policy also discusses attribution and restrictions involving logos and trademarks.
Do not turn that guidance into a universal license for every third-party attachment, linked resource, or commercial dataset. Check relevant notices when redistributing substantial documents or combining sources.
For a public-facing dataset, minimize unnecessary personal information, retain provenance, and establish a correction process. Decide who can access raw documents and which fields belong in an export. These practical checks help keep a retrieval project accountable without treating every successful download as blanket redistribution permission.
Frequently Asked Questions
Does SEC EDGAR have a free API requiring no key?
Yes. SEC EDGAR’s public data endpoints can be read without buying a subscription or obtaining an API key. Identify your application and follow the access rules cited above.
How do I retrieve a company’s filings using Python?
Resolve its ticker to a CIK, load submissions, and select the filing by form and date. Keep the accession and primary-document filename together when constructing the download URL.
How do submissions, companyfacts, and companyconcept differ?
Submissions is the discovery step in this workflow. Companyfacts supplies the company’s aggregated concepts, while companyconcept narrows the request to one taxonomy tag. Validate each numerical record before using it.
What rate limit and User-Agent rules apply?
Use the SEC’s published aggregate ceiling cited in the access section, and identify yourself with a truthful contact User-Agent. Coordinate workers and retries through one budget. Stop and investigate a block instead of changing identities.
How can I download multiple years of filings and financial data?
Build an accession inventory, then retrieve documents or use the relevant bulk dataset. Cache completed downloads and preserve amended filings separately. Record which source and extraction policy produced each output.
Why did the Revenues endpoint return old Apple values?
In our snapshot, that concept ended in FY2018 even though the request succeeded. Another revenue concept contained newer records. Check concept coverage and reporting periods before treating the last returned row as current revenue.
Do I need a proxy for SEC EDGAR?
Our identified requests succeeded over a direct datacenter connection. Start directly and keep traffic modest. A proxy cannot grant permission to exceed the SEC’s access rules.
Build the direct workflow first
Start with one company, preserve its filing accession, and validate the exported values before scaling. DataImpulse is optional proxy infrastructure, not a scraping API, managed unblocker, VPN, or filing-data vendor. Our EDGAR snapshot needed no proxy.
Last updated: October 17, 2026.

State/City/Zip/ASN Targeting 



