SEC EDGAR API Guide
SEC EDGAR API: public data, EDGAR Next and MCP examples
The SEC already provides free JSON APIs for filing history and company XBRL facts. If you know the CIK and need an official filing or a supported financial concept, start there. No API key is required for those public endpoints.
The harder work begins when a question crosses filings: resolving a deal alias, parsing its loan records, matching reporting periods, or tracing a calculated field to its source. CMD+RVL's MCP services address those steps. The examples below show what they return and where their coverage stops.
Updated November 12, 2026. Filing dates, financial periods and retrieval dates are kept separate throughout.
What has changed on EDGAR?
EDGAR Next added APIs for filers. Submission and account-management APIs support sending filings and managing filer access. They have their own authorization and token requirements. They are separate from the public data.sec.gov research endpoints. Use the SEC's filer API guide and development toolkit when building a filing workflow.
The 2026 releases changed schemas and operations. Release 26.1, dated March 16, added support for 2026 taxonomies and new degraded-service statuses in the EDGAR Operational Status API. Release 26.3, dated September 14, added FND-2026q3 concepts for unit investment trusts and retired specified older taxonomies. Parsers should follow the supported taxonomies and retain the source filing's context.
Announced changes are not yet released features. The SEC's November 6 preview of Release 26.4 describes planned December 7 changes, including electronic SBS Dispute Notices. As of this article's update, these are plans subject to revision.
These developments improve filing and disclosure infrastructure. They do not turn CompanyFacts into a loan-level ABS database.
Choose the public data route
| Need | Starting point | Check before using it |
|---|---|---|
| Company financial concepts | CompanyFacts or CompanyConcept | Taxonomy, unit, start/end dates, form and accession |
| A filer's document history | Submissions JSON | Recent records plus the additional historical files |
| A calendar-aligned comparison | XBRL Frames | Underlying companies can have different fiscal periods |
| Large-scale XBRL or filing-history retrieval | Nightly bulk ZIP archives | Snapshot timing and local storage requirements |
| Original filings and exhibits | EDGAR Archives | Document type, amendments and the relevant exhibit |
| Text within filings | EDGAR Full Text Search | A text match does not establish a numeric field's meaning |
The SEC documents public APIs and nightly bulk downloads, including CompanyFacts ZIP and Submissions ZIP. Use Full Text Search for document discovery and the archive-access guide for filing paths and indexes.
CompanyFacts is limited to supported non-custom concepts applying to the whole entity. Custom tags, segment detail, ABS-EE loan records and fund N-PORT holdings require other source documents or datasets. The endpoint's name is broader than its actual coverage.
Python: retrieve a fact with its period and accession
This example uses the Python 3 standard library. Set SEC_USER_AGENT to your organization or project name and a monitored contact email before running it. The request helper pauses between calls and stops on access errors; it does not retry a blocked request indefinitely.
import jsonimport osimport timefrom urllib.error import HTTPErrorfrom urllib.parse import quotefrom urllib.request import Request, urlopenUSER_AGENT = os.environ["SEC_USER_AGENT"]BASE = "https://data.sec.gov"def sec_json(path):# A single worker; coordinate the rate across all your workers.time.sleep(0.25)request = Request(BASE + path, headers={"User-Agent": USER_AGENT})try:with urlopen(request, timeout=30) as response:return json.load(response)except HTTPError as error:if error.code in (403, 429):retry_after = error.headers.get("Retry-After", "not supplied")raise RuntimeError(f"SEC access response {error.code}; stop and review access. "f"Retry-After: {retry_after}") from errorraisecik = 320193cik10 = str(cik).zfill(10)facts = sec_json(f"/api/xbrl/companyfacts/CIK{cik10}.json")# Assets is an instant concept. Select a balance-sheet date explicitly.observations = facts["facts"]["us-gaap"]["Assets"]["units"]["USD"]candidates = [row for row in observationsif row["end"] == "2023-09-30" and row["form"] == "10-K"]if not candidates:raise ValueError("No matching Assets observation for this date and form")# Latest-filed observation for that date, which can include later comparisons.chosen = max(candidates, key=lambda row: (row["filed"], row["accn"]))print(json.dumps({"company": facts["entityName"],"concept": "us-gaap/Assets","unit": "USD","end": chosen["end"],"value": chosen["val"],"filed": chosen["filed"],"accession": chosen["accn"],}, indent=2))
This selects the latest-filed 10-K observation for a specified balance-sheet date. For an “as known on” analysis, also restrict filed to your information cutoff. For revenue or another duration concept, inspect both start and end. Filtering only on fy and fp can mix current and comparative periods reported in the same annual filing.
Find the corresponding filing documents
Continue in the same Python session:
submissions = sec_json(f"/submissions/CIK{cik10}.json")recent = submissions["filings"]["recent"]rows = zip(recent["accessionNumber"], recent["filingDate"],recent["form"], recent["primaryDocument"],)for accession, filed, form, document in list(rows)[:5]:archive = accession.replace("-", "")url = (f"https://www.sec.gov/Archives/edgar/data/{int(cik)}/"f"{archive}/{quote(document, safe='/')}")print(filed, form, accession, url)# These URLs locate older history; they are not fetched by this loop.for history in submissions["filings"].get("files", []):print("Older history:", f"{BASE}/submissions/{history['name']}")
The top-level response has a recent-history window. An older observation's accession may require one of the additional history files or the archive index. Those history files expose the filing arrays directly. Do not assume the recent list is the filer's full history.
Rate limits, 403, 429 and browser access
The SEC's developer guidance limits each user to no more than 10 requests per second across machines. A delay in one worker is insufficient if several workers share the workload. Cache downloaded files, use the bulk archives for broad collection, and reduce traffic when access errors appear.
For 403 Forbidden, check your identifying User-Agent and the access policy. A header alone cannot resolve every IP or automated-access restriction. For 429 Too Many Requests, stop the burst, honor Retry-After where supplied, and resume at a lower coordinated rate. Server errors need bounded retries and backoff, too.
data.sec.gov does not support browser CORS requests. Run retrieval in a backend or local script. Public availability does not mean an unrestricted browser API or an exemption from fair-access rules.
Where CMD+RVL MCPs help
MCP lets an assistant call tools with defined inputs and outputs. The advantage here is the work behind the tools: a filing index, entity registry, parsed observations and source relationships. You can use the SEC directly for original documents while using these services for a covered analysis.
| Service | Useful task | Work it handles | Boundary |
|---|---|---|---|
| EDGAR MCP | Resolve a deal name and find its filings | Registered aliases, CIK mappings and indexed filing lookup | Index coverage is not the complete SEC archive |
| Reveal MCP | Find the relevant table and its meaning | Catalog identity, descriptions and relationships | Metadata does not establish the underlying values |
| Data MCP | Read a property observation or compare periods | Parsed tables, schema inspection and guarded SQL execution | Dates, units, joins and result completeness still need review |
| Evidence MCP | Trace how a field or table was produced | Recorded dependencies and producing-code pointers | Table lineage alone does not verify an individual value |
| Receipts MCP | Check a supported claim against filing evidence | Defined field comparisons and a sourced verdict | Requires a claim; scope and as-of dates are explicit |
EDGAR, Reveal, Data and Evidence require arranged access and credentials. Receipts has a free connection at https://receipts.cmdrvl.com/mcp; start with quickstart and coverage. See the MCP catalog and setup guides for current availability. Tool namespaces can vary by client and provisioned connection.
Example 1: resolve a deal, then retrieve its filings
These are MCP tool calls, not HTTP paths. Run the second call only if the resolver returns decision: resolved, using the returned entity_ref. Ask for clarification if it returns candidates.
edgar.com.entity.resolve({"query":"BMARK 2020-B16","type":"deals"})edgar.com.entity.filings({"entity_ref":"cmdrvl:DEAL-BMARK-2020-B16","form_type":"ABS-EE","limit":1})
Checked November 12, 2026, the alias resolved to Benchmark 2020-B16 Mortgage Trust, CIK 1797288. Its latest indexed ABS-EE was 0001888524-26-020262, filed October 30. A limit of one is a bounded lookup, not a complete filing history.
The value is getting from the name used in conversation to a specific filer and source document. Zero index results under a filter still need investigation; they do not establish that the SEC has no such filing.
Example 2: retrieve reported property DSCR with the right dates
CompanyFacts cannot answer a CMBS property-level DSCR question. In the connected Data service, first discover and describe the table:
edgar_data.list_tables({"match":"property period","limit":6})edgar_data.describe_table({"table":"PROPERTY_MART.PROPERTY_PERIOD_FACT"})
After verifying the columns and query guidance, pass this SQL to edgar_data.run_query:
SELECT PROPERTYNAME, ASSETNUMBER, REPORTING_PERIOD,MOSTRECENTFINANCIALSSTARTDATE, MOSTRECENTFINANCIALSENDDATE,MOSTRECENTDEBTSERVICECOVERAGENETOPERATINGINCOMEPERCENTAGE,FILING_ID, FILING_DATEFROM EDGAR_DB.PROPERTY_MART.PROPERTY_PERIOD_FACTWHERE CIK = '1797288'AND ASSETNUMBER = '7'AND PROPERTYNAME = '1633 Broadway'AND REPORTING_PERIOD >= '2026-09-01'AND HAS_LOANORDER BY REPORTING_PERIOD DESC, FILING_DATE DESC, FILING_ID DESCLIMIT 1
The November 12 check returned 2.8684x, with financial dates January 1 to June 30, 2026, tape reporting-period end October 13, and filing date October 30. The source is the ABS-EE accession above. The long column name contains “PERCENTAGE,” but this reported DSCR is a ratio multiple; multiplying it by 100 would misstate the result.
This is one property observation. It does not establish the whole loan's refinancing capacity, a current valuation or a bond's payment. The DSCR article shows how to use the distinction.
For metadata and source tracing, these read-only calls were also checked:
cmdrvl.com.metadata.lookup({"reference":"PROPERTY_MART.PROPERTY_PERIOD_FACT","limit":3})evidence.trace_lineage({"entity_key":"PROPERTY_MART.PROPERTY_PERIOD_FACT","direction":"upstream","depth":1})
The lookup returned the table's catalog identity. The trace returned upstream table relationships and producing-code pointers. A one-hop trace is partial; it does not itself prove the DSCR calculation. Inspect the relevant source code and observations when checking that claim.
Example 3: check a supplied claim
Receipts checks claims rather than supplying arbitrary loan values. After calling coverage, this call checked a 4.11% current-period note-rate claim for WFCM 2020-C58, loan 25:
verify_loan_field({"deal":"WFCM 2020-C58","loan_id":"25","field":"note_rate","claimed_value":4.11})
The November 12 check returned CONFIRM, as of October 2026, citing ABS-EE accession 0001888524-26-019266 and 10-D accession 0001888524-26-019265. It checked the reported note rate, not an origination rate, pool average or forecast.
Read the live coverage response before another call. Current-only service does not offer arbitrary historical queries, and bond-class payments are outside this example's coverage. UNVERIFIED means the covered claim could not be confirmed; OUT_OF_COVERAGE means the requested subject or field is not held. Neither means the claim is false.
Prefer a question to a tool call?
Ask Cairn for the property data and its sources
Copy the question into your Cairn conversation. For public snapshots you can retrieve without an MCP connection, start with the DealCharts facts API. For interpretation, give your assistant the returned data and ask it to state its assumptions.