Skip to content

XBRL Footnotes

EdgarTools now supports parsing and accessing footnotes from XBRL documents. Footnotes provide additional context and explanations for specific facts in financial statements.

Overview

In XBRL documents, footnotes are linked to facts through a structured relationship: - Facts have unique id attributes (e.g., id="ID_123") - Footnotes contain explanatory text and have their own identifiers - FootnoteArcs connect facts to footnotes using XLink references

EdgarTools automatically extracts these relationships, making footnotes easily accessible alongside the financial data.

Basic Usage

Accessing Footnotes

from edgar.xbrl.parser import XBRLParser

# Parse an XBRL instance document
parser = XBRLParser()
with open("instance.xml") as f:
    content = f.read()
parser.parse_instance_content(content)

# Access footnotes
print(f"Found {len(parser.footnotes)} footnotes")

# Iterate through footnotes
for footnote_id, footnote in parser.footnotes.items():
    print(f"Footnote {footnote_id}: {footnote.text}")
    print(f"Related to facts: {footnote.related_fact_ids}")

Finding Facts with Footnotes

# Find facts that have footnotes
facts_with_footnotes = [
    fact for fact in parser.facts.values() 
    if fact.footnotes
]

print(f"Found {len(facts_with_footnotes)} facts with footnotes")

# Show fact details
for fact in facts_with_footnotes[:5]:
    print(f"Fact: {fact.element_id} (ID: {fact.fact_id})")
    print(f"Value: {fact.value}")
    print(f"Footnotes: {', '.join(fact.footnotes)}")
    print()

Using the XBRL Class

The XBRL class provides convenient methods for working with footnotes:

from edgar.xbrl import XBRL

# Initialize and parse
xbrl = XBRL()
xbrl.parser.parse_instance_content(content)

# Access footnotes property
footnotes = xbrl.footnotes
print(f"Document has {len(footnotes)} footnotes")

# Get footnotes for a specific fact ID
fact_footnotes = xbrl.get_footnotes_for_fact("ID_123")
for footnote in fact_footnotes:
    print(f"Footnote: {footnote.text}")

# Get all facts that have footnotes
facts_with_footnotes = xbrl.get_facts_with_footnotes()

Data Models

Fact Model

The Fact model has been enhanced with footnote support:

class Fact(BaseModel):
    element_id: str
    context_ref: str
    value: str
    unit_ref: Optional[str] = None
    decimals: Optional[Union[int, str]] = None
    numeric_value: Optional[float] = None
    footnotes: List[str] = Field(default_factory=list)  # Footnote IDs
    instance_id: Optional[int] = None
    fact_id: Optional[str] = None  # Original XML id attribute

Footnote Model

class Footnote(BaseModel):
    footnote_id: str
    text: str
    lang: Optional[str] = "en-US"
    role: Optional[str] = None
    related_fact_ids: List[str] = Field(default_factory=list)

Real-World Example

Here's a complete example using a filing with footnotes:

from edgar import Filing
from pathlib import Path

# Get a filing and parse its XBRL
filing = Filing(form='10-K', cik=1234567, accession_no='0001234567-23-000001')
xbrl = filing.xbrl()

# Check if the document has footnotes
if xbrl.footnotes:
    print(f"Document contains {len(xbrl.footnotes)} footnotes")

    # Show footnote details
    for footnote_id, footnote in list(xbrl.footnotes.items())[:3]:
        print(f"\nFootnote ID: {footnote_id}")
        print(f"Text: {footnote.text[:100]}...")
        print(f"Language: {footnote.lang}")
        print(f"Linked to {len(footnote.related_fact_ids)} facts")

    # Find facts with footnotes in the balance sheet
    balance_sheet = xbrl.get_statement("BalanceSheet")
    if balance_sheet:
        for item in balance_sheet.get_all_line_items():
            facts = item.get("facts", [])
            for fact in facts:
                if fact.footnotes:
                    print(f"\n{item['label']} has footnotes:")
                    for fn_id in fact.footnotes:
                        if fn_id in xbrl.footnotes:
                            print(f"  • {xbrl.footnotes[fn_id].text[:80]}...")
else:
    print("No footnotes found in this document")

Advanced Usage

Filtering Footnotes by Content

# Find footnotes containing specific keywords
debt_footnotes = [
    (fn_id, footnote) for fn_id, footnote in parser.footnotes.items()
    if any(keyword in footnote.text.lower() for keyword in ['debt', 'loan', 'credit'])
]

print(f"Found {len(debt_footnotes)} footnotes related to debt")

Creating a Footnote Report

from rich.console import Console
from rich.table import Table

console = Console()

# Create a footnote summary table
table = Table(title="Footnote Summary", show_header=True)
table.add_column("ID", style="cyan")
table.add_column("Preview", style="white", width=60)
table.add_column("Facts", style="yellow", justify="right")

for fn_id, footnote in parser.footnotes.items():
    preview = footnote.text[:60] + "..." if len(footnote.text) > 60 else footnote.text
    fact_count = str(len(footnote.related_fact_ids))

    table.add_row(fn_id, preview, fact_count)

console.print(table)

Cross-Referencing with Financial Statements

# Find footnotes that reference specific financial statement items
def find_footnotes_for_concept(xbrl, concept_name):
    """Find footnotes related to a specific accounting concept."""
    related_footnotes = []

    # Find facts matching the concept
    for fact_key, fact in xbrl.parser.facts.items():
        if concept_name.lower() in fact.element_id.lower():
            if fact.footnotes:
                for fn_id in fact.footnotes:
                    if fn_id in xbrl.footnotes:
                        related_footnotes.append((fact, xbrl.footnotes[fn_id]))

    return related_footnotes

# Example: Find footnotes about revenue
revenue_footnotes = find_footnotes_for_concept(xbrl, "Revenue")
for fact, footnote in revenue_footnotes:
    print(f"Revenue fact {fact.fact_id}: {footnote.text}")

XBRL Technical Details

Footnote Structure in XBRL

XBRL footnotes follow the XBRL 2.1 specification:

<!-- Footnote definition -->
<link:footnote id="fn-1" xlink:label="fn-1" 
               xlink:role="http://www.xbrl.org/2003/role/footnote" 
               xml:lang="en-US">
    <xhtml:div>
        <xhtml:span>Explanatory text about the fact.</xhtml:span>
    </xhtml:div>
</link:footnote>

<!-- Footnote arc linking fact to footnote -->
<link:footnoteArc xlink:arcrole="http://www.xbrl.org/2003/arcrole/fact-footnote" 
                  xlink:from="fact-id-123" 
                  xlink:to="fn-1" 
                  xlink:type="arc"/>

Supported Footnote Formats

EdgarTools handles: - Standard footnotes with id attributes - XLink footnotes with xlink:label attributes
- XHTML content within footnotes (automatically extracts text) - Multiple languages via xml:lang attributes - Custom roles via xlink:role attributes

Namespace Handling

The parser correctly handles all standard XBRL namespaces: - http://www.xbrl.org/2003/linkbase (link) - http://www.w3.org/1999/xlink (xlink) - http://www.w3.org/1999/xhtml (xhtml) - http://www.w3.org/XML/1998/namespace (xml)

Performance Considerations

  • Footnote extraction adds minimal overhead to XBRL parsing
  • Footnotes are parsed lazily during instance document processing
  • Both fact-to-footnote and footnote-to-fact lookups are O(1) operations
  • Large documents with many footnotes are handled efficiently

Error Handling

The parser gracefully handles common footnote issues:

# Parser warnings for missing footnote references
# Warning: "Footnote arc references undefined footnote: footnote_123"

# Missing footnote definitions are logged but don't cause parsing to fail
# Malformed XHTML content is handled with fallback text extraction

Migration from Manual Parsing

If you were previously parsing footnotes manually:

# Before (manual parsing)
import xml.etree.ElementTree as ET

def extract_footnotes_manually(xml_content):
    root = ET.fromstring(xml_content)
    footnotes = {}
    # ... complex manual parsing logic ...
    return footnotes

# After (using EdgarTools)
from edgar.xbrl.parser import XBRLParser

parser = XBRLParser()
parser.parse_instance_content(xml_content)
footnotes = parser.footnotes  # Ready to use!

API Reference

XBRLParser.footnotes

  • Type: Dict[str, Footnote]
  • Description: Dictionary mapping footnote IDs to Footnote objects

XBRL.footnotes

  • Type: Dict[str, Footnote]
  • Description: Property providing access to parser footnotes

XBRL.get_footnotes_for_fact(fact_id: str)

  • Parameters: fact_id - The ID of the fact to get footnotes for
  • Returns: List[Footnote] - List of associated footnotes
  • Description: Retrieves all footnotes linked to a specific fact

XBRL.get_facts_with_footnotes()

  • Returns: Dict[str, Fact] - Dictionary of facts that have footnotes
  • Description: Returns all facts that reference footnotes

Fact.footnotes

  • Type: List[str]
  • Description: List of footnote IDs that reference this fact

Fact.fact_id

  • Type: Optional[str]
  • Description: Original id attribute from the XML element

This feature was implemented to support the XBRL 2.1 specification for footnotes and is compatible with all standard SEC XBRL filings.