Blog / Developer guides
Developer guides

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.

By the SecFeed teamOctober 16, 20265 min read
Key takeaways

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 URLExampleWhat it is
CIK320193The filer's SEC identifier, no leading zeros in this path form
Accession, no dashes000032019326000020Folder name
Accession, with dashes0000320193-26-000020Used in the index file name

The SEC's fair access rules

The SEC's fair access policy asks every automated client to:

If EDGAR starts returning errors or empty responses to a script that was working moments ago, slow down and add a delay between requests before assuming something else is wrong. A missing or generic User-Agent, or too high a request rate, are the two most common causes.

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.

Get a free API key →

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.

Get a free API key →

For information only, not investment advice. Data from public SEC filings; verify important figures on SEC.gov. See our AI Disclosure.