Working with SEC Filings — Access Content, Attachments, and Structured Data
This guide shows you how to get, view, and extract data from individual SEC filings using the Filing class.
Getting a Filing
From a Company
Get filings for a specific company by ticker or CIK:
from edgar import Company
company = Company("AAPL")
filings = company.get_filings(form="10-K")
filing = filings.latest()
print(filing.company) # "Apple Inc."
print(filing.form) # "10-K"
print(filing.filing_date) # "2023-11-03"
From a Filing Search
Search across all filings and pick one:
from edgar import get_filings
# Get all Q1 2024 10-K filings
filings = get_filings(2024, 1, form="10-K")
# Get the first one
filing = filings[0]
# Or get the latest
filing = filings.latest()
By Accession Number
If you know the accession number:
from edgar import get_by_accession_number
filing = get_by_accession_number("0000320193-23-000106")
print(filing.company) # "Apple Inc."
Direct Construction
Create a Filing object directly (rarely needed):
from edgar import Filing
filing = Filing(
form='10-Q',
filing_date='2024-06-30',
company='Tesla Inc.',
cik=1318605,
accession_no='0001628280-24-028839'
)
Filing Properties
Basic Information
print(f"Company: {filing.company}")
print(f"CIK: {filing.cik}")
print(f"Form: {filing.form}")
print(f"Filing Date: {filing.filing_date}")
print(f"Period: {filing.period_of_report}")
print(f"Accession: {filing.accession_no}")
Output:
Company: Apple Inc.
CIK: 320193
Form: 10-K
Filing Date: 2023-11-03
Period: 2023-09-30
Accession: 0000320193-23-000106
Enhanced Properties (EntityFiling)
When you get a filing from a company, you get an EntityFiling with additional properties:
company = Company("AAPL")
filing = company.get_filings(form="10-K").latest()
# EntityFiling-specific properties
print(f"Acceptance Time: {filing.acceptance_datetime}")
print(f"File Number: {filing.file_number}")
print(f"Size: {filing.size} bytes")
print(f"Primary Doc: {filing.primary_document}")
print(f"Is XBRL: {filing.is_xbrl}")
print(f"Is Inline XBRL: {filing.is_inline_xbrl}")
acceptance_datetime comes from SEC's submissions JSON, which labels it UTC but for some filers is hours off, by an amount that varies by filer and by day. When the exact time matters, for example in point-in-time backtests, use filing.header.acceptance_datetime. It reads the filing's own SGML header, matches the "Accepted" time on SEC's index page, and is naive Eastern time. It costs one extra request per filing.
Viewing Filings
Open in Browser
Open the filing in your default web browser:
filing.open() # Opens primary document
Open the SEC filing homepage (shows all documents):
filing.open_homepage() # Opens index page with all files
View in Console
Display the filing directly in your terminal or Jupyter notebook:
filing.view()
This uses Rich formatting to display the filing as close to the original as possible.
Serve Locally
Serve the filing on a local HTTP server:
filing.serve(port=8080)
# Opens http://localhost:8080 in browser
Accessing Filing Content
Get HTML
html = filing.html()
if html:
print(f"HTML length: {len(html)} characters")
Get Plain Text
text = filing.text()
print(text[:500]) # First 500 characters
Images contribute nothing by default. To mark where they were — useful when a chart carries content the surrounding prose does not repeat:
text = filing.text(include_images=True)
# ... [Image: nvidialogoa10.jpg] ...
Get Markdown
md = filing.markdown()
# Save to file
with open("filing.md", "w") as f:
f.write(md)
Images are rendered as Markdown image links with absolute SEC archive URLs, so charts that exist only as images — a 10-K's stock performance graph, for example — survive into the export and stay viewable.
include_page_breaks is deprecated
filing.markdown(include_page_breaks=True, start_page_number=1) inserts
{N}---- delimiters, but page-break rendering exists only in the legacy
renderer, so the flag routes the whole document through it — and that
renderer drops every image and formats tables differently. It will be
removed in 6.0. Prefer filing.markdown().
Get XML
For filings that contain XML (like ownership forms):
xml = filing.xml()
if xml:
import xml.etree.ElementTree as ET
root = ET.fromstring(xml)
# Parse XML data
Get Full Submission
Get the complete SEC text submission file including SGML headers:
full_text = filing.full_text_submission()
print(full_text[:1000])
Working with Attachments
SEC filings often include multiple documents beyond the primary filing.
List All Attachments
attachments = filing.attachments
print(f"Total attachments: {len(attachments)}")
for att in attachments:
print(f"{att.sequence}: {att.description}")
print(f" Type: {att.document_type}")
print(f" File: {att.document}")
Get Primary Document
primary = filing.document
print(f"Primary: {primary.description}")
Access Specific Attachment
By index:
first_att = filing.attachments[0]
By document name:
att = filing.attachments["ex-10_1.htm"]
Download Attachments
Download a specific attachment:
attachment = filing.attachments[0]
attachment.download("./downloads/")
Download all attachments:
filing.attachments.download("./downloads/")
Work with Exhibits
Exhibits are a subset of attachments:
exhibits = filing.exhibits
for exhibit in exhibits:
print(f"Exhibit {exhibit.exhibit_number}: {exhibit.description}")
# Download specific exhibit
if exhibit.exhibit_number == "10.1":
exhibit.download("./exhibits/")
Extracting Structured Data
Get XBRL Data
For filings with XBRL (10-K, 10-Q, etc.):
xbrl = filing.xbrl()
if xbrl:
# Access financial statements
statements = xbrl.statements
income = statements.income_statement()
balance = statements.balance_sheet()
cashflow = statements.cash_flow_statement()
print(income)
Preview the Data Object Type
Before calling obj(), you can check what type of object a filing will return:
filing.obj_type # e.g. 'TenK', 'ThirteenF', 'Form4', None
This is useful for filtering filings to only those with structured data objects.
Get Form-Specific Object
Get a structured object based on the form type:
# For 10-K filing
obj = filing.obj()
print(type(obj)) # <class 'edgar.company_reports.TenK'>
# Access financials from TenK object
if obj.financials:
income = obj.financials.income_statement
balance = obj.financials.balance_sheet
print(income)
Important: The financials property exists on form-specific objects (TenK, TenQ), not on the base Filing class.
Incorrect:
# This will fail - Filing doesn't have financials
financials = filing.financials # AttributeError
Correct:
# Get form-specific object first
tenk = filing.obj()
if tenk.financials:
financials = tenk.financials
Form-Specific Objects
Different forms return different object types:
| Form | Object Type | Key Features |
|---|---|---|
| 10-K | TenK | financials, income_statement, balance_sheet, auditor, subsidiaries, reports |
| 10-Q | TenQ | financials, income_statement, balance_sheet, reports |
| 8-K | EightK | items (material events), reports |
| 20-F | TwentyF | financials for foreign issuers, reports |
| 4 | Form4 | insider transactions |
| 13F-HR | ThirteenF | institutional holdings |
| SC 13D/G | Schedule13 | beneficial ownership |
| DEF 14A | ProxyStatement | proxy voting matters |
Example - 8-K:
filing = company.get_filings(form="8-K").latest()
eightk = filing.obj()
print(f"Items: {eightk.items}") # Material events reported
Example - Form 4:
filing = company.get_filings(form="4").latest()
form4 = filing.obj()
# Access insider transaction data
html = form4.to_html()
Searching Within a Filing
Simple Text Search
results = filing.search("artificial intelligence")
print(f"Found {len(results)} mentions")
for result in results[:3]:
print(result[:200]) # First 200 chars of each match
Regex Search
# Search for email addresses
emails = filing.search(
r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
regex=True
)
print(f"Found {len(emails)} email addresses")
Search for Financial Terms
# Search for revenue mentions
revenue_mentions = filing.search("revenue")
# Search for risk factors
risk_mentions = filing.search("risk factor")
# Case-sensitive regex
critical_terms = filing.search(r'\b(material weakness|restatement)\b', regex=True)
Parsing Document Sections
Get Available Sections
sections = filing.sections()
for section in sections:
print(section)
Parse for Structured Search
doc = filing.parse()
# Use parsed document for advanced operations
Saving and Loading
Save Filing
# Save to directory (auto-generates filename)
filing.save("./data/filings/")
# Save to specific file
filing.save("./data/apple_10k_2023.pkl")
Load Filing
from edgar import Filing
filing = Filing.load("./data/apple_10k_2023.pkl")
print(filing.company)
Export to Dictionary
data = filing.to_dict()
print(data.keys())
Export Summary
import pandas as pd
summary_df = filing.summary()
print(summary_df)
Common Use Cases
Extract Revenue from Latest 10-K
from edgar import Company
company = Company("MSFT")
filing = company.get_filings(form="10-K").latest()
tenk = filing.obj()
if tenk.financials:
income = tenk.financials.income_statement
print(income)
Download All Exhibits
filing = get_by_accession_number("0001234567-24-000001")
for exhibit in filing.exhibits:
print(f"Downloading {exhibit.exhibit_number}: {exhibit.description}")
exhibit.download(f"./exhibits/{exhibit.document}")
Search Across Recent 8-K Filings
from edgar import get_filings
filings = get_filings(2024, 1, form="8-K").head(50)
for filing in filings:
results = filing.search("earnings")
if results:
print(f"{filing.company} ({filing.filing_date}): {len(results)} mentions")
Convert Filing to Markdown for LLM Analysis
company = Company("NVDA")
filing = company.get_filings(form="10-K").latest()
# Export to markdown — tables, links and images all preserved
md = filing.markdown()
# Save for processing
with open("nvidia_10k_for_analysis.md", "w") as f:
f.write(md)
print(f"Saved {len(md)} characters to markdown file")
Batch Download Filings
from edgar import get_filings
filings = get_filings(2023, 4, form="10-K").head(100)
for filing in filings:
try:
filing.download(data_directory="./raw_filings/")
print(f"Downloaded: {filing.company}")
except Exception as e:
print(f"Error downloading {filing.company}: {e}")
Error Handling
Always check for None before using optional data:
from edgar import get_by_accession_number
try:
filing = get_by_accession_number("0000320193-23-000106")
# Check HTML availability
html = filing.html()
if html is None:
print("HTML content not available")
else:
print(f"HTML: {len(html)} characters")
# Check XBRL availability
xbrl = filing.xbrl()
if xbrl is None:
print("No XBRL data available")
else:
print("XBRL data available")
# Check structured object
obj = filing.obj()
if obj:
# Process object
pass
except Exception as e:
print(f"Error processing filing: {e}")
Best Practices
- Check form type - Use
filing.formto determine filing type before processing - Verify XBRL - Check
filing.is_xbrlfor EntityFiling before extracting structured data - Handle large files - Some filings are very large; consider streaming for attachments
- Cache content - Store downloaded content locally to avoid repeated API calls
- Respect rate limits - Be mindful of SEC rate limits when processing many filings
- Use obj() for structure - Prefer
filing.obj()over HTML parsing for structured data
Performance Tips
Efficient pattern:
# Get form-specific object once
obj = filing.obj()
# Check before using
if obj and hasattr(obj, 'financials') and obj.financials:
income = obj.financials.income_statement
# Process income statement
Cache expensive operations:
# Download once, use multiple times
text = filing.text()
# Search multiple times without re-downloading
results1 = filing.search("revenue")
results2 = filing.search("profit")
See Also
- Filing API Reference - Complete Filing class documentation
- Filings API Reference - Working with filing collections
- Extract Financial Statements - Getting financial data from XBRL
- Filing Attachments Guide - Working with documents and exhibits
- Filtering Filings - Finding specific filings
- Current Filings - Access today's filings