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.query().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: the FilingSummary MenuCategory name (Statements, Details, Notes, Tables, Policies, 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
# A Statement has no per-concept getter; read values from its DataFrame.
# Standardized line items carry a standard_concept; period columns start with the date.
def latest_value(statement, standard_concept):
df = statement.to_dataframe()
period = max(c for c in df.columns if c.startswith("20")) # newest period
return df[df["standard_concept"] == standard_concept].iloc[0][period]
# Access different statement types
if statements.balance_sheet():
bs = statements.balance_sheet()
print(f"Total Assets: {latest_value(bs, 'Assets')}")
if statements.income_statement():
is_stmt = statements.income_statement()
print(f"Revenue: {latest_value(is_stmt, '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.
Reading values
Statement has no per-concept getter. Read values from to_dataframe(): every row carries its concept, rows mapped to a standardized concept carry it in standard_concept, and each period is a column whose name starts with the period's end date (for example 2025-09-27 (FY)).
Example:
statement = xbrl.statements.income_statement()
# Render the statement
rendered = statement.render()
print(rendered)
# Convert to DataFrame
df = statement.to_dataframe()
# Get specific values from the newest period
period = max(c for c in df.columns if c.startswith("20"))
revenue = df[df["standard_concept"] == "Revenue"].iloc[0][period]
net_income = df[df["concept"] == "us-gaap_NetIncomeLoss"].iloc[0][period]
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
query()
def query(self) -> FactQuery
by_* filters (by_concept(), by_label(), by_value(), by_date_range(), ...) are FactQuery methods, documented below, not FactsView methods. xbrl.query() starts the same query.
Returns: FactQuery object for filtering
Analysis Methods
pivot_by_period()
def pivot_by_period(self, concept_pattern: Optional[str] = None,
statement_type: Optional[str] = None) -> pd.DataFrame
Parameters:
- concept_pattern: Regex matched against concept names, as in by_concept() (default: all)
- statement_type: Statement type to filter by (default: all)
Returns: DataFrame with concepts as rows and periods as columns
time_series()
def time_series(self, concept: str, exact: bool = True) -> pd.DataFrame
Parameters:
- concept: Concept name
- exact: If True, require exact match; otherwise, use regex
Returns: DataFrame 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.query().by_concept("Revenue")
revenue_facts = revenue_query.execute()
# Query by label and value
large_expenses = facts.query().by_label("expense").by_value(lambda v: v >= 1_000_000)
expense_facts = large_expenses.to_dataframe()
# Time series analysis (exact concept name by default)
revenue_ts = facts.time_series("us-gaap:Revenues")
print(revenue_ts.head())
# Pivot analysis (concept_pattern is a regex)
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, value_filter: Union[Callable, str, int, float, list, tuple]) -> FactQuery
lambda v: v > 1_000_000, an exact value, or a (min, max) tuple.
by_date_range()
def by_date_range(self, start_date: Optional[str] = None, end_date: Optional[str] = None,
exact: bool = False) -> FactQuery
by_statement_type()
def by_statement_type(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
Example:
# Chain multiple filters
query = (xbrl.query()
.by_concept("Revenue")
.by_date_range(start_date="2023-01-01")
.by_value(lambda v: v >= 1_000_000))
# Execute in different ways
facts_list = query.execute()
facts_df = query.to_dataframe()
count = len(facts_list)
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]
cash_flow_statement()
def cash_flow_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.query()
.by_concept("Revenue")
.by_value(lambda v: v >= 50000000000) # $50B+
.by_date_range(start_date="2023-01-01")
.to_dataframe())
# Pivot analysis (concept_pattern is a regex)
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 (standard_concept names)
key_metrics = ["Revenue", "NetIncome", "OperatingIncomeLoss"]
for metric in key_metrics:
print(f"\n{metric} Comparison:")
for i, df in enumerate(comparison_data):
period = max(c for c in df.columns if c.startswith("20")) # newest period
rows = df[df["standard_concept"] == metric]
if not rows.empty:
value = rows.iloc[0][period]
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