Skip to content

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"

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

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
# 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)
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

  1. Check form type - Use filing.form to determine filing type before processing
  2. Verify XBRL - Check filing.is_xbrl for EntityFiling before extracting structured data
  3. Handle large files - Some filings are very large; consider streaming for attachments
  4. Cache content - Store downloaded content locally to avoid repeated API calls
  5. Respect rate limits - Be mindful of SEC rate limits when processing many filings
  6. 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