XBRL API Reference
The XBRL module provides comprehensive parsing and processing of XBRL (eXtensible Business Reporting Language) data from SEC filings. It includes support for statement standardization, multi-period analysis, and advanced querying capabilities.
Module Overview
The XBRL module is organized into several key components:
- Core Classes:
XBRL,XBRLSfor parsing and managing XBRL documents - Statement Processing:
Statements,Statementfor working with financial statements - Facts Querying:
FactsView,FactQueryfor querying XBRL facts - Multi-Period Analysis:
StitchedStatements,StitchedStatementfor comparative analysis - Standardization:
StandardConceptfor normalizing company-specific concepts - Rendering:
RenderedStatementfor formatted output
Core Classes
XBRL
The main class for parsing and working with XBRL documents from SEC filings.
from edgar.xbrl import XBRL
class XBRL:
"""Main XBRL parser integrating all components of the XBRL parsing system."""
Factory Methods
from_filing()
@classmethod
def from_filing(cls, filing: Filing) -> XBRL
Parameters:
- filing: SEC filing object containing XBRL data
Returns: XBRL instance
Example:
from edgar import Company
from edgar.xbrl import XBRL
company = Company("AAPL")
filing = company.latest("10-K")
xbrl = XBRL.from_filing(filing)
from_directory()
@classmethod
def from_directory(cls, directory: str) -> XBRL
Parameters:
- directory: Path to directory containing XBRL files
Returns: XBRL instance
from_files()
@classmethod
def from_files(cls, files: List[str]) -> XBRL
Parameters:
- files: List of file paths to XBRL documents
Returns: XBRL instance
Core Properties
statements
@property
def statements(self) -> Statements
Returns: Statements object for accessing individual statements
Example:
# Access different statement types
balance_sheet = xbrl.statements.balance_sheet()
income_statement = xbrl.statements.income_statement()
cash_flow = xbrl.statements.cash_flow_statement()
facts
@property
def facts(self) -> FactsView
Returns: FactsView object for querying facts
Example:
# Query facts by concept
revenue_facts = xbrl.facts.by_concept("Revenue")
# Convert to DataFrame for analysis
facts_df = xbrl.facts.to_dataframe()
Statement Methods
get_statement()
def get_statement(self, statement_type: str) -> Optional[Statement]
Parameters:
- statement_type: Statement type ("BalanceSheet", "IncomeStatement", "CashFlowStatement", etc.)
Returns: Statement object or None if not found
render_statement()
def render_statement(self, statement_type: str, **kwargs) -> RenderedStatement
Parameters:
- statement_type: Statement type to render
- **kwargs: Additional rendering options
Returns: RenderedStatement object
Example:
# Render balance sheet
rendered = xbrl.render_statement("BalanceSheet")
print(rendered)
# Render with custom options
rendered = xbrl.render_statement("IncomeStatement",
show_percentages=True,
max_rows=50)
Linkbase Access
calculation_linkbase()
def calculation_linkbase(self, include_abstract: bool = False) -> pd.DataFrame
Parameters:
- include_abstract: When False (default), abstract concepts are excluded. Set True to include structural/grouping concepts.
Returns: pd.DataFrame with columns:
| Column | Type | Description |
|---|---|---|
concept |
str | Local concept name (e.g., Revenues) |
concept_taxonomy |
str | Prefix before the first _ (us-gaap, dei, jpm, ...) |
parent_concept |
str | Local name of the calc parent |
parent_taxonomy |
str | Parent's taxonomy prefix |
weight |
float | Signed calc weight (+1.0, -1.0) — do not flatten the sign |
role_uri |
str | Full extended-link role URI |
role_short |
str | Human-readable role title from the schema |
menucat |
str | None | SEC report tier when available (S=Statement, D=Details, N=Notes, T=Tables, P=Policies, C=Cover) |
is_abstract |
bool | True for structural/grouping concepts |
label |
str | Standard label from the label linkbase |
Returns an empty DataFrame with the documented columns when the filing has no calculation linkbase. Root nodes (those without a parent) are always excluded — they carry no arc.
Example:
from edgar import Company
filing = Company("JPM").latest("10-K")
calc = filing.xbrl().calculation_linkbase()
# Extension concepts and their us-gaap parents
extensions = calc[
(calc.concept_taxonomy == 'jpm') &
(calc.parent_taxonomy == 'us-gaap')
]
# Children of NoninterestIncome — bank revenue disaggregation
noninterest = calc[calc.parent_concept == 'NoninterestIncome']
See the Calculation Linkbase guide for the longer walkthrough.
Data Conversion
to_pandas()
def to_pandas(self) -> pd.DataFrame
Returns: DataFrame with all facts and their attributes
Example:
# Convert to DataFrame for analysis
df = xbrl.to_pandas()
print(df.columns) # ['concept', 'value', 'period', 'label', ...]
# Filter for specific concepts
revenue_df = df[df['concept'].str.contains('Revenue', case=False)]
XBRLS
Container class for managing multiple XBRL documents for multi-period analysis.
from edgar.xbrl import XBRLS
class XBRLS:
"""Container for multiple XBRL objects enabling multi-period analysis."""
Factory Methods
from_filings()
@classmethod
def from_filings(cls, filings: List[Filing]) -> XBRLS
Parameters:
- filings: List of Filing objects
Returns: XBRLS instance
Example:
from edgar import Company
from edgar.xbrl import XBRLS
company = Company("AAPL")
filings = company.get_filings(form="10-K").head(3) # Get 3 years
xbrls = XBRLS.from_filings(filings)
Properties
statements
@property
def statements(self) -> StitchedStatements
Returns: StitchedStatements object
Example:
# Get multi-period statements
income_stmt = xbrls.statements.income_statement()
balance_sheet = xbrls.statements.balance_sheet()
# Render multi-period view
print(income_stmt.render())
Statement Classes
Statements
High-level interface for accessing financial statements from a single XBRL document.
class Statements:
"""High-level interface to all statements in an XBRL document."""
Statement Access Methods
balance_sheet()
def balance_sheet(self) -> Optional[Statement]
Returns: Statement object or None
income_statement()
def income_statement(self) -> Optional[Statement]
Returns: Statement object or None
cash_flow_statement()
def cash_flow_statement(self) -> Optional[Statement]
Returns: Statement object or None
statement_of_equity()
def statement_of_equity(self) -> Optional[Statement]
Returns: Statement object or None
comprehensive_income()
def comprehensive_income(self) -> Optional[Statement]
Returns: Statement object or None
Example:
statements = xbrl.statements
# Access different statement types
if statements.balance_sheet():
bs = statements.balance_sheet()
print(f"Total Assets: {bs.get_concept_value('Assets')}")
if statements.income_statement():
is_stmt = statements.income_statement()
print(f"Revenue: {is_stmt.get_concept_value('Revenue')}")
Statement
Individual financial statement with analysis and rendering capabilities.
class Statement:
"""A single financial statement extracted from XBRL data."""
Core Methods
render()
def render(self, **kwargs) -> RenderedStatement
Parameters:
- **kwargs: Rendering options (show_percentages, max_rows, etc.)
Returns: RenderedStatement object
to_dataframe()
def to_dataframe(
self,
include_dimensions: bool = True,
include_unit: bool = False,
include_point_in_time: bool = False,
presentation: bool = False
) -> pd.DataFrame
Parameters:
- include_dimensions: Include dimensional breakdowns (default: True)
- include_unit: Include unit column (USD, shares, etc.) (default: False)
- include_point_in_time: Include point-in-time column for instant facts (default: False)
- presentation: Apply HTML-matching transformations using preferred_sign (default: False)
- False (default): Raw instance values from XML
- True: Transform values to match SEC filing HTML display
Returns: DataFrame with the following columns:
- Core columns: concept, label, period columns (dates)
- Metadata columns (always included):
- balance — debit or credit (from XBRL taxonomy)
- weight — calculation tree weight (+1 or -1)
- preferred_sign — how the value should be displayed (from presentation linkbase)
- level — nesting depth in the presentation tree (0=root, 1=section header, 2=line item, etc.)
- abstract — True if this row is a section header, not a data row
- parent_concept — calculation tree parent (the metric concept this rolls up to for summation math)
- parent_abstract_concept — presentation tree parent (the section header this appears under for display hierarchy)
- Optional columns: dimension, unit, point_in_time
Value Modes:
- Raw mode (default): Preserves values exactly as reported in instance document
- Presentation mode (presentation=True): Applies transformations to match SEC HTML rendering
- Cash Flow: outflows with preferred_sign=-1 shown as negative
- Income Statement: applies preferred_sign transformations
Example:
statement = xbrl.statements.income_statement()
# Raw values (default)
df_raw = statement.to_dataframe()
# Returns actual XML values + metadata columns
# Presentation mode (matches SEC HTML)
df_presentation = statement.to_dataframe(presentation=True)
# Returns transformed values matching 10-K HTML display
# Check metadata
print(df_raw[['concept', 'balance', 'weight', 'preferred_sign']].head())
# Hierarchy columns — understand parent-child relationships
print(df_raw[['label', 'level', 'parent_concept', 'parent_abstract_concept']].head(10))
See Also: - Issue #463 - XBRL value transformations and metadata columns - Issue #514 - Parent concept hierarchy columns - Revenue Segment Hierarchy Guide
Returns: DataFrame with statement data
extension_arcs()
def extension_arcs(self, include_values: bool = False) -> List[ExtensionArc]
These concepts do not appear in render() output today because the rendered statement is driven by the presentation tree. Calling extension_arcs() does not change render() behavior — it's an opt-in additive view.
Parameters:
- include_values: When True, look up instance facts for each extension concept and emit one ExtensionArc per (concept, context). When False (default), return one ExtensionArc per concept with value/period_key/context_ref left as None.
Returns: List[ExtensionArc]. Empty list when the statement has no calculation tree, when its role cannot be resolved, or when no extension concepts are dropped from the presentation tree.
ExtensionArc fields:
| Field | Type | Description |
|---|---|---|
concept |
str | Local name (e.g., NetBorrowingsFromSubsidiaries) |
concept_taxonomy |
str | Filer prefix (e.g., jpm) |
parent_concept |
str | Local name of the calc parent |
parent_taxonomy |
str | Parent's taxonomy prefix (typically us-gaap) |
weight |
float | Signed calc weight |
label |
str | Standard label or '' |
role_uri |
str | Full role URI of the statement |
element_id |
str | Original underscore-form ID (e.g., jpm_FooBar) |
value |
float | None | Instance value (only with include_values=True) |
period_key |
str | None | Period key (e.g., duration_2023-01-01_2023-12-31) |
context_ref |
str | None | XBRL context reference |
Example:
cash_flow = filing.xbrl().statements.cash_flow_statement()
# Structural mode — which extensions are silently dropped from render()?
for arc in cash_flow.extension_arcs():
print(f"{arc.concept_taxonomy}:{arc.concept} "
f"-> {arc.parent_taxonomy}:{arc.parent_concept} "
f"w={arc.weight:+.1f}")
# With values — emits one arc per period that has a fact
for arc in cash_flow.extension_arcs(include_values=True):
print(f"{arc.concept} {arc.period_key} {arc.value:,.0f}")
See the Calculation Linkbase guide for the longer walkthrough and rationale.
get_concept_value()
def get_concept_value(self, concept: str) -> Optional[Any]
Parameters:
- concept: Concept name to look up
Returns: Concept value or None
Example:
statement = xbrl.statements.income_statement()
# Render the statement
rendered = statement.render()
print(rendered)
# Convert to DataFrame
df = statement.to_dataframe()
# Get specific values
revenue = statement.get_concept_value("Revenue")
net_income = statement.get_concept_value("NetIncomeLoss")
Facts Querying
FactsView
Provides a view over all XBRL facts with analysis and querying methods.
class FactsView:
"""View over all facts with analysis methods."""
Query Methods
by_concept()
def by_concept(self, pattern: str, exact: bool = False) -> FactQuery
Parameters:
- pattern: Pattern to match against concept names
- exact: If True, require exact match; otherwise, use regex
Returns: FactQuery object for further filtering
by_label()
def by_label(self, pattern: str, exact: bool = False) -> FactQuery
Parameters:
- pattern: Pattern to match against labels
- exact: If True, require exact match; otherwise, use regex
Returns: FactQuery object for further filtering
by_value()
def by_value(self, min_value: float = None, max_value: float = None) -> FactQuery
Parameters:
- min_value: Minimum value threshold
- max_value: Maximum value threshold
Returns: FactQuery object for further filtering
by_period()
def by_period(self, start_date: str = None, end_date: str = None) -> FactQuery
Parameters:
- start_date: Start date (YYYY-MM-DD format)
- end_date: End date (YYYY-MM-DD format)
Returns: FactQuery object for further filtering
Analysis Methods
pivot_by_period()
def pivot_by_period(self, concepts: List[str] = None) -> pd.DataFrame
Parameters:
- concepts: List of concepts to include (default: all)
Returns: DataFrame with concepts as rows and periods as columns
time_series()
def time_series(self, concept: str) -> pd.Series
Parameters:
- concept: Concept name
Returns: pandas Series with time series data
Data Conversion
to_dataframe()
def to_dataframe(self) -> pd.DataFrame
Returns: DataFrame with all facts and metadata
Example:
facts = xbrl.facts
# Query by concept
revenue_query = facts.by_concept("Revenue")
revenue_facts = revenue_query.execute()
# Query by label and value
large_expenses = facts.by_label("expense").by_value(min_value=1000000)
expense_facts = large_expenses.to_dataframe()
# Time series analysis
revenue_ts = facts.time_series("Revenue")
print(revenue_ts.head())
# Pivot analysis
pivot_df = facts.pivot_by_period(["Revenue", "NetIncomeLoss"])
FactQuery
Fluent query builder for filtering and manipulating XBRL facts.
class FactQuery:
"""A query builder for XBRL facts with fluent interface."""
Filtering Methods
All filtering methods return self for method chaining.
by_concept()
def by_concept(self, pattern: str, exact: bool = False) -> FactQuery
by_label()
def by_label(self, pattern: str, exact: bool = False) -> FactQuery
by_value()
def by_value(self, min_value: float = None, max_value: float = None) -> FactQuery
by_period()
def by_period(self, start_date: str = None, end_date: str = None) -> FactQuery
by_statement()
def by_statement(self, statement_type: str) -> FactQuery
Parameters:
- statement_type: Statement type to filter by
Returns: FactQuery object for method chaining
Execution Methods
execute()
def execute(self) -> List[Dict]
Returns: List of fact dictionaries
to_dataframe()
def to_dataframe(self) -> pd.DataFrame
Returns: DataFrame with query results
first()
def first(self) -> Optional[Dict]
Returns: First fact dictionary or None
count()
def count(self) -> int
Returns: Number of matching facts
Example:
# Chain multiple filters
query = (xbrl.facts
.by_concept("Revenue")
.by_period(start_date="2023-01-01")
.by_value(min_value=1000000))
# Execute in different ways
facts_list = query.execute()
facts_df = query.to_dataframe()
first_fact = query.first()
count = query.count()
Multi-Period Analysis
StitchedStatements
Interface for accessing multi-period statements that combine data across multiple XBRL documents.
class StitchedStatements:
"""Interface for multi-period statements."""
Statement Access Methods
Similar to Statements but returns StitchedStatement objects. All methods accept these common parameters:
max_periods(int): Maximum number of periods to include (default: 8)standard(bool): Whether to use standardized concept labels (default: True)use_optimal_periods(bool): Whether to use entity info for optimal period selection (default: True)show_date_range(bool): Whether to show full date ranges for duration periods (default: False)include_dimensions(bool): Whether to include dimensional segment data (default: False, True for equity/comprehensive income)view(str): Controls dimensional filtering —"standard","detailed", or"summary". Overridesinclude_dimensionswhen provided.
balance_sheet()
def balance_sheet(self, view=None, **kwargs) -> Optional[StitchedStatement]
income_statement()
def income_statement(self, view=None, **kwargs) -> Optional[StitchedStatement]
cashflow_statement()
def cashflow_statement(self, view=None, **kwargs) -> Optional[StitchedStatement]
statement_of_equity()
def statement_of_equity(self, view=None, **kwargs) -> Optional[StitchedStatement]
comprehensive_income()
def comprehensive_income(self, view=None, **kwargs) -> Optional[StitchedStatement]
Example:
# Multi-period analysis
stitched_statements = xbrls.statements
income_stmt = stitched_statements.income_statement()
# Shows multiple years of data
print(income_stmt.render())
# Include dimensional breakdowns (e.g., cost by segment)
income_detailed = stitched_statements.income_statement(view="detailed")
df = income_detailed.to_dataframe()
StitchedStatement
Individual statement showing multi-period data with comparative analysis.
class StitchedStatement:
"""Individual stitched statement showing multi-period data."""
Constructor Parameters:
- xbrls: XBRLS object containing stitched data
- statement_type (str): Type of statement ('BalanceSheet', 'IncomeStatement', etc.)
- max_periods (int): Maximum number of periods (default: 8)
- standard (bool): Use standardized labels (default: True)
- include_dimensions (bool): Include dimensional data (default: False)
- view (str): "standard", "detailed", or "summary". Overrides include_dimensions.
Analysis Methods
render()
def render(self, show_date_range: bool = False) -> Table
to_dataframe()
def to_dataframe(self) -> pd.DataFrame
Standardization
StandardConcept
Represents a standardized concept that normalizes company-specific terminology.
class StandardConcept:
"""Standardized concept representation."""
Properties
name
@property
def name(self) -> str
label
@property
def label(self) -> str
Example:
# Standardization is applied automatically in statements
statement = xbrl.statements.income_statement()
df = statement.to_dataframe()
# Check for standardized vs original labels
print(df[['label', 'original_label']].head())
Rendering
RenderedStatement
Formatted statement output with rich console display capabilities.
class RenderedStatement:
"""Rich formatted statement output."""
Display Methods
str()
def __str__(self) -> str
rich()
def __rich__(self) -> RichRenderable
Example:
# Rich rendering in console
rendered = xbrl.render_statement("BalanceSheet")
print(rendered) # Displays with rich formatting
# Plain text for export
text_output = str(rendered)
Utility Functions
stitch_statements()
def stitch_statements(statements: List[Statement]) -> StitchedStatement
Parameters:
- statements: List of Statement objects to combine
Returns: StitchedStatement object
render_stitched_statement()
def render_stitched_statement(stitched_statement: StitchedStatement, **kwargs) -> RenderedStatement
Parameters:
- stitched_statement: StitchedStatement to render
- **kwargs: Rendering options
Returns: RenderedStatement object
to_pandas()
def to_pandas(obj: Union[XBRL, Statement, FactsView]) -> pd.DataFrame
Parameters:
- obj: Object to convert (XBRL, Statement, or FactsView)
Returns: DataFrame representation
Advanced Usage Examples
Multi-Period Financial Analysis
from edgar import Company
from edgar.xbrl import XBRLS
# Get multiple years of data
company = Company("AAPL")
filings = company.get_filings(form="10-K").head(3)
xbrls = XBRLS.from_filings(filings)
# Analyze income statement trends
income_stmt = xbrls.statements.income_statement()
revenue_trend = income_stmt.get_trend("Revenue")
revenue_growth = income_stmt.calculate_growth("Revenue")
print(f"Revenue Growth: {revenue_growth.iloc[-1]:.2%}")
Complex Fact Querying
from edgar import Company
from edgar.xbrl import XBRL
company = Company("MSFT")
filing = company.latest("10-K")
xbrl = XBRL.from_filing(filing)
# Complex query with multiple filters
high_value_revenue = (xbrl.facts
.by_concept("Revenue")
.by_value(min_value=50000000000) # $50B+
.by_period(start_date="2023-01-01")
.to_dataframe())
# Pivot analysis
pivot_df = xbrl.facts.pivot_by_period([
"Revenue",
"NetIncomeLoss",
"OperatingIncomeLoss"
])
Statement Comparison
# Compare statements across different companies
companies = ["AAPL", "MSFT", "GOOGL"]
statements = []
for ticker in companies:
company = Company(ticker)
filing = company.latest("10-K")
xbrl = XBRL.from_filing(filing)
if xbrl.statements.income_statement():
statements.append(xbrl.statements.income_statement())
# Create comparison DataFrame
comparison_data = []
for stmt in statements:
df = stmt.to_dataframe()
comparison_data.append(df)
# Analyze key metrics across companies
key_metrics = ["Revenue", "NetIncomeLoss", "OperatingIncomeLoss"]
for metric in key_metrics:
print(f"\n{metric} Comparison:")
for i, stmt in enumerate(statements):
value = stmt.get_concept_value(metric)
if value:
print(f" {companies[i]}: ${value/1e9:.1f}B")
Import Reference
# Core classes
from edgar.xbrl import XBRL, XBRLS
# Statement classes
from edgar.xbrl import Statements, Statement
from edgar.xbrl import StitchedStatements, StitchedStatement
# Facts querying
from edgar.xbrl import FactsView, FactQuery
from edgar.xbrl import StitchedFactsView, StitchedFactQuery
# Standardization and rendering
from edgar.xbrl import StandardConcept, RenderedStatement
# Utility functions
from edgar.xbrl import stitch_statements, render_stitched_statement, to_pandas
Error Handling
from edgar.xbrl import XBRL, XBRLFilingWithNoXbrlData
try:
xbrl = XBRL.from_filing(filing)
except XBRLFilingWithNoXbrlData:
print("Filing does not contain XBRL data")
except Exception as e:
print(f"Error parsing XBRL: {e}")
# Check for statement availability
if xbrl.statements.income_statement():
income_stmt = xbrl.statements.income_statement()
df = income_stmt.to_dataframe()
else:
print("Income statement not found")
XBRL Value Transformations (Issue #463)
EdgarTools provides a two-layer system for XBRL value handling:
Value Layers
- Raw Values (default): Values exactly as reported in the XBRL instance document
- Matches SEC CompanyFacts API
- Preserves original data for analysis
-
No transformations applied
-
Presentation Values (
presentation=True): Values transformed to match SEC filing HTML display - Applies
preferred_signtransformations from presentation linkbase - Cash Flow outflows shown as negative when appropriate
- Matches how values appear in the official 10-K/10-Q HTML
Metadata Columns
All statement DataFrames include XBRL metadata columns:
balance: Debit or credit classification from schema (accounting semantics)weight: Calculation weight from calculation linkbase (+1.0 or -1.0)preferred_sign: Presentation hint from presentation linkbase (+1 or -1)
These columns provide transparency about XBRL semantics and enable custom transformations.
Usage Examples
# Get raw values (default)
xbrl = filing.xbrl()
statement = xbrl.statements.cash_flow_statement()
df_raw = statement.to_dataframe()
# PaymentsOfDividends appears as positive (raw XML value)
dividends = df_raw[df_raw['concept'].str.contains('PaymentsOfDividends')]
print(dividends[['concept', 'balance', 'preferred_sign', '2024-09-30']])
# Output: concept=PaymentsOfDividends, balance=credit, preferred_sign=-1, value=12345000000 (positive)
# Get presentation values (matches SEC HTML)
df_presentation = statement.to_dataframe(presentation=True)
dividends_pres = df_presentation[df_presentation['concept'].str.contains('PaymentsOfDividends')]
print(dividends_pres[['concept', '2024-09-30']])
# Output: value=-12345000000 (negative, matches HTML display with parentheses)
When to Use Each Mode
Use Raw Values (default): - Cross-company financial analysis - Data science and machine learning - Comparison with SEC CompanyFacts API - When you need unmodified reported values
Use Presentation Values (presentation=True):
- Matching SEC filing HTML display
- Creating investor-facing reports
- Replicating official financial statement appearance
- When users expect "traditional" financial statement signs
Technical Notes
- Raw values are consistent across companies: Testing confirmed SEC instance data uses consistent signs
- Metadata always included: All transformations can be recreated using metadata columns
- No data loss: Raw values always preserved, transformations are reversible
Performance Tips
- Use specific queries - Filter facts early to reduce processing time
- Cache XBRL objects - Parsing is expensive, reuse when possible
- Limit statement rendering - Use
max_rowsparameter for large statements - Batch processing - Use
XBRLSfor efficient multi-period analysis
See Also
- Company API Reference - Working with company data
- Filing API Reference - Working with individual filings
- Extract Financial Statements Guide - Practical examples
- Working with Filing Guide - Filing workflows