How to Download SEC Filings With Python, Politely
How to find and download SEC filings and exhibits with Python, respect EDGAR's fair access rules, and pull documents through an API instead of scraping.
- Every EDGAR filing is a free, public file under a predictable Archives URL built from the CIK and accession number.
- The SEC requires a declared User-Agent with a name and contact email, and asks that automated access stay under 10 requests per second.
- A filing's index page lists every document and exhibit it contains, so you fetch the index first, then the files you actually need.
- An API can hand you the document list and the file itself without you managing rate limits or parsing the index page yourself.
Every filing on EDGAR is a plain file sitting at a predictable URL, and the SEC publishes them for free. Downloading them with Python is simple in principle, but doing it without slowing down for everyone else, including yourself when EDGAR temporarily blocks a script that is firing requests too fast, takes a bit more care.
This guide covers where filings live, the access rules the SEC asks you to follow, how to download one with plain Python, and when it is easier to use an API instead.
Where EDGAR filings actually live
Every filing has an accession number, a unique ID in the form 0000320193-26-000020. Combined with the filer's CIK, it defines the filing's folder on EDGAR:
https://www.sec.gov/Archives/edgar/data/<cik>/<accession-no-dashes>/<accession-no>-index.htm
The index page lists every document in that filing: the main form, exhibits, and any XBRL files. Each one is a separate file at its own URL inside the same folder.
| Part of the URL | Example | What it is |
|---|---|---|
| CIK | 320193 | The filer's SEC identifier, no leading zeros in this path form |
| Accession, no dashes | 000032019326000020 | Folder name |
| Accession, with dashes | 0000320193-26-000020 | Used in the index file name |
The SEC's fair access rules
The SEC's fair access policy asks every automated client to:
- Declare a User-Agent header that identifies who is making the request, typically a company or person name plus a contact email, for example
MyResearch tool admin@example.com. - Stay under 10 requests per second across all of your traffic to sec.gov.
Get this data as clean JSON
SecFeed turns SEC filings into structured data with one API call: GET /v1/download?url=https://www.sec.gov/Archives/edgar/data/320193/000032019326000020/0000320193-26-000020-index.htm. 500 free calls a month.
Finding what to download
Before you can download a filing, you need its CIK and accession number. You can get both from EDGAR's own company search and filing index pages, or from a filing search API. For example, searching by ticker and form type:
curl "https://secfeedapi.com/v1/filings?query=ticker:AAPL AND formType:\"10-K\"&size=1" \
-H "Authorization: Bearer YOUR_API_KEY"
The response includes the CIK and accession number for the filing, which is everything you need to build the Archives URL above, or to fetch the full document list.
Downloading with plain Python
Using only the standard library, a polite downloader sets a real User-Agent, adds a small delay between requests, and retries on transient errors rather than hammering the server:
import time
import urllib.request
HEADERS = {"User-Agent": "MyResearch tool admin@example.com"}
def fetch(url, min_gap=0.2):
time.sleep(min_gap)
req = urllib.request.Request(url, headers=HEADERS)
with urllib.request.urlopen(req, timeout=30) as r:
return r.read()
index = fetch("https://www.sec.gov/Archives/edgar/data/320193/000032019326000020/0000320193-26-000020-index.htm")
From the index page you can parse out the document file names, then fetch each one the same way, keeping the same delay between calls. For anything beyond a handful of filings, write the raw bytes to disk as you go, so a script that stops partway through does not have to start over.
Downloading through an API instead
If you are downloading many filings, or do not want to parse index pages and manage rate limiting yourself, SecFeed wraps the same Archives files behind two endpoints: /v1/files lists every document in a filing, and /v1/download fetches one of them.
from secfeed import Client
sf = Client("YOUR_API_KEY")
doc_list = sf.files(cik=320193, accession_no="0000320193-26-000020")
pdf_bytes = sf.download("https://www.sec.gov/Archives/edgar/data/320193/000032019326000020/0000320193-26-000020-index.htm")
with open("apple-10k.htm", "wb") as f:
f.write(pdf_bytes)
This does not remove the need to be a reasonable API consumer, but it moves the rate limiting, retries and caching to the server instead of your script.
Handling exhibits and multiple documents
A single filing can contain dozens of files: the main form, signed exhibits, press releases attached as Exhibit 99, and machine readable XBRL data. The index page (or /v1/files) is the only reliable way to know what exists in a given filing, since file names are not fully standardized across filers. Download the index first, decide which documents you actually need, and skip the rest rather than pulling every file in every filing.
Frequently asked questions
Do I need an account or API key to download from EDGAR directly?
No. EDGAR itself is free and does not require registration. You only need an API key if you use a third party service that wraps EDGAR, such as SecFeed, for search, parsing or rate limit handling.
What happens if I exceed the SEC's request rate?
EDGAR may start rejecting or throttling your requests. The fair access policy asks for a declared User-Agent and a rate under 10 requests per second specifically so that automated scripts do not get blocked and do not slow down the service for others.
Can I download every filing for a company in bulk?
Yes, by iterating over its filing history and downloading each accession's files with the same polite rate limiting. For very large pulls, check whether a bulk dataset already covers what you need rather than re-downloading one filing at a time.
Why does the index page matter if I just want the main document?
Because file names inside a filing are not standardized across filers, the index page is the only consistent way to identify which file is the primary document versus an exhibit.
Is the data the same whether I download directly or through an API?
Yes. An API that wraps EDGAR, like SecFeed, serves the same files the SEC published. The value it adds is parsing, search, rate limit handling and caching, not different source data.
Get this data as clean JSON
SecFeed turns SEC filings into structured data with one API call: GET /v1/download?url=https://www.sec.gov/Archives/edgar/data/320193/000032019326000020/0000320193-26-000020-index.htm. 500 free calls a month.
For information only, not investment advice. Data from public SEC filings; verify important figures on SEC.gov. See our AI Disclosure.