Run the example
Save the script as submissions_example.py. For a live request set SEC_USER_AGENT to your organization and a monitored contact address, then run python3 submissions_example.py. There is no SEC API key to configure.
For offline reproduction, save a submissions response and pass its path: python3 submissions_example.py company.json. The publication check used the real Apple submissions response from 2026-11-14. It successfully parsed the saved response and selected filing rows; the linked primary documents were not downloaded.
"""python3 submissions_example.py SNAPSHOT.json; optional live request with SEC_USER_AGENT."""
import json
import os
import re
import sys
from urllib.parse import quote
from urllib.request import Request, urlopen
def filing_rows(payload):
recent = payload["filings"]["recent"]
fields = ("accessionNumber", "form", "filingDate", "primaryDocument")
n = len(recent["accessionNumber"])
if any(len(recent[name]) != n for name in fields):
raise ValueError("Unequal SEC column lengths")
cik = str(int(payload["cik"]))
for i in range(n):
row = {name: recent[name][i] for name in fields}
accession = row["accessionNumber"]
if not re.fullmatch(r"\d{10}-\d{2}-\d{6}", accession):
raise ValueError("Invalid accession")
row["url"] = f"https://www.sec.gov/Archives/edgar/data/{cik}/{accession.replace('-', '')}/{quote(row['primaryDocument'], safe='')}"
yield row
if __name__ == "__main__":
if len(sys.argv) > 1:
with open(sys.argv[1]) as handle:
payload = json.load(handle)
mode = "saved SEC response"
else:
agent = os.environ["SEC_USER_AGENT"] # Organization + monitored contact address.
request = Request("https://data.sec.gov/submissions/CIK0000320193.json", headers={"User-Agent": agent})
with urlopen(request, timeout=30) as response:
payload = json.load(response)
mode = "live SEC response"
rows = list(filing_rows(payload))
forms = [r for r in rows if r["form"] in {"10-K", "10-Q", "8-K"}]
print(json.dumps({"mode": mode, "company": payload["name"], "parsed_rows": len(rows), "selected_examples": forms[:3], "older_history_files": payload["filings"].get("files", []), "limits": "Lists metadata only; linked documents were not fetched or parsed by this example."}, indent=2))
Executed against a saved HTTP 200 SEC response on 2026-11-14. The script uses only the Python standard library. A new live request may return different filings.
Examples from the saved response
These rows were selected from the saved SEC response, not typed as hypothetical filings. They are a snapshot of filing metadata. The links are deterministically constructed from that metadata and have not been independently fetched in this example.
| 8-K | 2026-11-09 | 0001140361-26-042798 | SEC document |
| 10-K | 2026-11-03 | 0000320193-26-000026 | SEC document |
| 8-K | 2026-11-02 | 0000320193-26-000024 | SEC document |
No matching records. Try a broader search.
Avoid turning this into an uncontrolled crawler
The SEC developer guidance caps access at ten requests per second across a user’s machines. That is a ceiling, not a suggested polling target. Cache a company response, coordinate workers and back off when the service returns rate-limit or blocking responses.
For older history, follow the filenames under filings.files; filings.recent alone is not the full lifetime archive. For a market-wide rebuild use the bulk archives rather than iterating every company at high frequency.
Keep this boundary explicit
The script returns primary filing metadata and a URL. It does not parse an 8-K item, extract financial tables, identify every exhibit or judge the meaning of a disclosure. Those are separate document-processing tasks with their own evidence checks.
Source notes
- SEC EDGAR API documentation ↗
Official submissions, XBRL, bulk data, update schedule and access requirements.
- SEC Developer Resources ↗
Official archive, index and RSS options; fair-access request limit.
The Synoptic workflow
Explore the SEC event stream
For new-filing monitoring, verify the stream and your account access.
Explore the SEC event stream