Convert SEC Filings to PDF With Python
The examples below use the sec-api Python package to convert any SEC filing or exhibit into a PDF. The original formatting is preserved, including images and tables, and inline XBRL tags are stripped to keep the file size down. To download a file as filed, without converting it, see the Download API Python example.
Installation
Install the package with pip:
pip install sec-apiDownload a 10-K as PDF
get_pdf takes the sec.gov URL of the filing and returns the PDF as bytes, so the file is written in binary mode. The example converts Apple's annual report.
from sec_api import PdfGeneratorApi
pdfGeneratorApi = PdfGeneratorApi("YOUR_API_KEY")
url = "https://www.sec.gov/Archives/edgar/data/320193/000032019323000106/aapl-20230930.htm"
pdf = pdfGeneratorApi.get_pdf(url)
with open("apple-10-k.pdf", "wb") as f:
f.write(pdf)Download a prospectus as PDF
Prospectuses filed on Forms 424B2, 424B3, 424B4 and S-1 run to hundreds of pages, with the offering terms, financial statements and risk factors laid out in tables. The example converts a Rule 424(b)(4) prospectus of roughly four megabytes.
from sec_api import PdfGeneratorApi
pdfGeneratorApi = PdfGeneratorApi("YOUR_API_KEY")
# Form 424B4, a Rule 424(b)(4) prospectus
url = "https://www.sec.gov/Archives/edgar/data/2109823/000121390026101599/ea0286599-08.htm"
pdf = pdfGeneratorApi.get_pdf(url)
with open("prospectus.pdf", "wb") as f:
f.write(pdf)Download a proxy statement as PDF
Proxy statements on Form DEF 14A are image heavy: logos, performance graphs, director photographs and the proxy voting card. Every image the filing references is rendered into the PDF, which is also why a DEF 14A takes longer to convert than a press release.
from sec_api import PdfGeneratorApi
pdfGeneratorApi = PdfGeneratorApi("YOUR_API_KEY")
# Form DEF 14A, Apple's 2026 proxy statement
url = "https://www.sec.gov/Archives/edgar/data/320193/000130817926000008/aapl014016-def14a.htm"
pdf = pdfGeneratorApi.get_pdf(url)
with open("apple-def-14a.pdf", "wb") as f:
f.write(pdf)Convert an exhibit to PDF
Exhibits work the same way. The example converts the Exhibit 99.1 press release attached to Apple's Q3 2026 earnings 8-K. Exhibit URLs are listed in the documentFormatFiles array returned by the Filing Search API.
from sec_api import PdfGeneratorApi
pdfGeneratorApi = PdfGeneratorApi("YOUR_API_KEY")
# Exhibit 99.1, the earnings press release attached to an 8-K
url = "https://www.sec.gov/Archives/edgar/data/320193/000032019326000018/a8-kex991q3202606272026.htm"
pdf = pdfGeneratorApi.get_pdf(url)
with open("earnings-press-release.pdf", "wb") as f:
f.write(pdf)Convert the filings of a company
The PDF Generator pairs with the Filing Search API: search returns the URLs, the generator converts them. The example saves the last five Apple 8-K filings as PDFs.
from sec_api import QueryApi, PdfGeneratorApi
queryApi = QueryApi("YOUR_API_KEY")
pdfGeneratorApi = PdfGeneratorApi("YOUR_API_KEY")
query = {
"query": 'ticker:AAPL AND formType:"8-K"',
"from": "0",
"size": "5",
"sort": [{"filedAt": {"order": "desc"}}],
}
filings = queryApi.get_filings(query)["filings"]
for filing in filings:
url = filing["linkToFilingDetails"]
name = filing["accessionNo"] + ".pdf"
with open(name, "wb") as f:
f.write(pdfGeneratorApi.get_pdf(url))
print("saved", name)Handling the 202 response
A filing published moments ago may not be converted yet. The API then returns HTTP 202 rather than the PDF, meaning the conversion has been enqueued. Retry the same request after 5 seconds. Retrying 3 to 4 times usually returns the final PDF. See the FAQ for the retry ceiling and when to report a failure.
import time
from sec_api import PdfGeneratorApi
pdfGeneratorApi = PdfGeneratorApi("YOUR_API_KEY")
def get_pdf_with_retry(url, attempts=4, delay=5):
for attempt in range(attempts):
pdf = pdfGeneratorApi.get_pdf(url)
# a converted filing starts with the PDF magic number
if pdf[:4] == b"%PDF":
return pdf
time.sleep(delay)
raise RuntimeError("PDF not ready after " + str(attempts) + " attempts")
pdf = get_pdf_with_retry(
"https://www.sec.gov/Archives/edgar/data/320193/000032019323000106/aapl-20230930.htm"
)
with open("apple-10-k.pdf", "wb") as f:
f.write(pdf)Good to know
- The URL must point at the file itself under
sec.gov/Archives. An inline XBRL viewer URL contains/ix?doc=, which has to be removed first. - Every image the filing references is rendered into the PDF, which is why proxy statements take longer to convert than press releases.
- Forms 3, 4 and 5 are filed as XML. Passing the
xslF345X02URL renders them through their stylesheet, so the PDF matches what a reader sees on the EDGAR viewer rather than showing raw XML. - Each filing is converted once and then stored, so the second request for the same URL returns the PDF immediately.