Technical writing

SEC EDGAR XBRL: The Machine-Readable Financial Statement Database Behind Every Public Company

· AI Analytics
SECFinanceFinancial DataFederal Data

Since 2009 the SEC has required every public company to tag its financial statements in XBRL — eXtensible Business Reporting Language — mapping each reported number to a standardized concept name. The SEC then publishes that structured data as a free JSON API. The result is a machine-readable archive of income statements, balance sheets, and cash flow statements for roughly 7,000 active filers, stretching back over fifteen years of quarterly and annual filings.

XBRL and the SEC Mandate

Before XBRL, public company financial disclosures arrived in two formats: paper, and then HTML. Both are readable by humans; neither is readily parsed by machines at scale. A researcher wanting revenue trends for five hundred companies had to write HTML scrapers tailored to each company's particular filing layout, or pay a commercial data vendor who had done that work. The SEC's XBRL mandate changed the underlying economics of financial data access.

eXtensible Business Reporting Language is an XML dialect originally developed by the American Institute of Certified Public Accountants in 1998 and standardized under the XBRL International consortium. Its central idea is that each number in a financial statement should carry a machine-readable label identifying what it represents, drawn from a shared taxonomy. The label for a company's reported revenue is not “Revenue” as a string buried inside an HTML table — it is the structured concept us-gaap:Revenues, pointing to a specific entry in the FASB-maintained US-GAAP taxonomy with a formal definition, unit constraints, calculation relationships, and documentation references.

The SEC phased in its XBRL requirement over three years. Large accelerated filers — those with public float above $700 million — were required to tag their financial statements beginning with fiscal years ending on or after June 15, 2009. Accelerated filers followed in 2010. All remaining filers, including smaller reporting companies, were required beginning in 2011. As of 2026 there are approximately 7,000 active operating company filers submitting XBRL-tagged financial statements; the broader EDGAR universe of registered investment companies, exchange-traded funds, and shell companies brings the total closer to 13,000 entities.

In 2019 the SEC adopted Inline XBRL — iXBRL — as the required submission format, phased in through 2020. Where the original XBRL mandate required companies to attach a separate structured XBRL document alongside their HTML filing, iXBRL embeds the XBRL tags directly inside the human-readable HTML document. The result is a single file that renders as a normal financial statement in a browser and simultaneously carries machine-readable tags that can be extracted programmatically. The SEC's EDGAR viewer highlights tagged elements in iXBRL filings, allowing anyone to click on a number and see the concept name, period, unit, and taxonomy reference behind it.

Filing Types and XBRL Coverage

Not every EDGAR filing type carries XBRL tags. The requirement applies specifically to the primary financial statement forms. Understanding which forms require XBRL, and which form types contain which financial data, is essential for building any systematic dataset.

FormDescriptionXBRL Required
10-KAnnual report; full income statement, balance sheet, cash flow, equity statementYes (iXBRL since 2020)
10-QQuarterly report; condensed financials for the three-month periodYes (iXBRL since 2020)
8-KCurrent report; material events, earnings releases, M&A announcementsNo (prose disclosure only)
DEF 14AProxy statement; executive compensation, director electionsNo (except inline financial summaries)
20-FAnnual report for foreign private issuers; similar scope to 10-KYes (IFRS or US-GAAP taxonomy)
40-FAnnual report for Canadian issuers exempt from full 20-F requirementsYes (iXBRL)

The 10-K and 10-Q forms are the primary sources of structured financial data. The 10-K is filed annually within 60 days of the fiscal year end for large accelerated filers, 75 days for accelerated filers, and 90 days for smaller companies. The 10-Q is filed within 40 or 45 days of each of the first three fiscal quarter ends. The fourth quarter has no 10-Q — those results appear in the 10-K. Together these two forms generate the time-series financial history accessible through the EDGAR XBRL APIs.

Foreign private issuers filing the 20-F may report under either US GAAP or IFRS (International Financial Reporting Standards). Companies reporting under IFRS use a different XBRL taxonomy maintained by the IFRS Foundation, with concept names under theifrs-full namespace rather than us-gaap. The EDGAR Company Facts API returns IFRS-tagged data under the ifrs-full key in the facts object for such filers. Cross-sectional screens using the frames endpoint for us-gaapconcepts will not include IFRS filers; a comprehensive global screen requires unioning results from both taxonomies for concepts that correspond to the same economic measure.

The US-GAAP Taxonomy

The US-GAAP taxonomy is maintained by the Financial Accounting Standards Board and released annually in coordination with the SEC. Each annual release is versioned; as of 2026 the current release is the 2024 taxonomy. The taxonomy contains more than 17,000 concept elements covering every aspect of US GAAP financial reporting, from the most common summary line items to highly specialized disclosures required for specific industries or accounting standards.

Concept names follow a namespace-qualified format. The three namespaces that appear most frequently in EDGAR XBRL data are:

  • us-gaap — the core US GAAP taxonomy. Contains all financial statement line item concepts: us-gaap:Revenues, us-gaap:NetIncomeLoss, us-gaap:Assets, us-gaap:LongTermDebt, us-gaap:GoodwillAndIntangibleAssetsDisclosureAbstract, and thousands of others at varying levels of specificity.
  • dei — Document and Entity Information. Contains entity-level metadata reported once per filing: dei:EntityCommonStockSharesOutstanding, dei:EntityPublicFloat, dei:TradingSymbol, dei:EntityFilerCategory, dei:DocumentPeriodEndDate. DEI concepts are essential for linking financial data to company identity and ticker symbols.
  • srt — SEC Reporting Taxonomy. Contains axis and member concepts used for dimensional data — primarily segment reporting and product line disaggregation. srt:ProductOrServiceAxis and srt:StatementGeographicalAxis appear in segment disclosure tables.

Each concept in the taxonomy carries several attributes. The label is the human-readable name displayed in the EDGAR viewer (“Net Income (Loss)” forus-gaap:NetIncomeLoss). The definition is a formal description of what the concept measures, often citing the specific ASC codification section that governs it. The balance type is either debit or credit, following accounting convention: asset and expense concepts have a debit balance, liability and equity concepts have a credit balance. The period type is either instant (a value at a single point in time, like total assets on the balance sheet date) or duration(a value over a span of time, like revenues for the quarter). The period type determines which frame period codes are valid for a concept in the EDGAR Frames API.

The taxonomy also defines calculation linkbases that specify arithmetic relationships between concepts. For example, us-gaap:GrossProfit is calculated as us-gaap:Revenues minus us-gaap:CostOfGoodsSold. These calculation relationships allow automated validation of submitted XBRL data: if a company's reported gross profit does not equal its revenues minus its cost of goods sold within rounding tolerance, the submission has a calculation inconsistency. The SEC EDGAR filing system runs these checks and flags inconsistencies, though it does not reject submissions for calculation errors — it records them in the filing's error report.

Core Financial Statement Concepts

The concepts that cover the three primary financial statements are the starting point for any quantitative use of EDGAR XBRL data. The taxonomy provides both summary and disaggregated concepts; most screens use the summary line items, which have the highest coverage across filers.

StatementConceptPeriod Type
Incomeus-gaap:RevenuesDuration
Incomeus-gaap:CostOfGoodsSoldDuration
Incomeus-gaap:GrossProfitDuration
Incomeus-gaap:OperatingIncomeLossDuration
Incomeus-gaap:NetIncomeLossDuration
Incomeus-gaap:EarningsPerShareBasicDuration
Balance Sheetus-gaap:AssetsInstant
Balance Sheetus-gaap:LiabilitiesInstant
Balance Sheetus-gaap:StockholdersEquityInstant
Balance Sheetus-gaap:CashAndCashEquivalentsAtCarryingValueInstant
Balance Sheetus-gaap:GoodwillInstant
Balance Sheetus-gaap:LongTermDebtInstant
Cash Flowus-gaap:NetCashProvidedByUsedInOperatingActivitiesDuration
Cash Flowus-gaap:NetCashProvidedByUsedInInvestingActivitiesDuration
Cash Flowus-gaap:NetCashProvidedByUsedInFinancingActivitiesDuration

The EDGAR Company Facts API

The EDGAR Company Facts API is the primary programmatic interface for retrieving all XBRL data for a single company. Its endpoint structure is:

https://data.sec.gov/api/xbrl/companyfacts/CIK{010-digit-CIK}.json

The CIK must be zero-padded to exactly ten digits. Apple Inc. has CIK 320193; the correct path is CIK0000320193.json. The response is a JSON document with two top-level keys: entityName (the company's registered name) andfacts (all XBRL facts ever reported by this entity).

The facts object is organized hierarchically: namespace at the first level (us-gaap, dei, ifrs-full), then concept name, then units, then an array of all fact values ever reported for that concept-unit combination. Each fact in the array carries:

  • accn — the accession number of the filing that contains this fact, in the format XXXXXXXXXX-YY-ZZZZZZ. The accession number uniquely identifies a single EDGAR filing and can be used to retrieve the complete filing document.
  • cik — the numeric CIK of the reporting entity.
  • entityName — the entity name at time of filing.
  • loc — the location code (country of incorporation) from the DEI namespace.
  • end — the period end date in YYYY-MM-DD format. For instant concepts this is the balance sheet date; for duration concepts it is the last day of the reporting period.
  • start — for duration concepts, the first day of the reporting period. Absent for instant concepts.
  • val — the reported numeric value. Monetary amounts are in the unit specified (USD, EUR, etc.) and in the denomination reported by the company. Most US companies report in thousands; some report in millions; a few in individual dollars. The unit field is the key in the units object, not a property of each fact.
  • accn and filed — the accession number and filing date (when the document was submitted to EDGAR). The filed date is essential for deduplicating restated periods: when a company restates results, multiple facts will share the same concept, entity, and period but differ in accession number and filed date; the most recently filed value is the authoritative one.
  • form — the form type of the filing (10-K, 10-Q, 10-K/A, etc.).
  • frame — the calendar-period code assigned by the SEC to facts that correspond to standard calendar periods. Annual facts covering a calendar year get a code like CY2023; quarterly facts get CY2023Q4 for duration or CY2023Q4I for instant concepts. Facts covering non-standard periods (a company's fiscal year that does not align with a calendar quarter) may not have a frame assigned. The frame code is also the key used by the Frames API.

For a large company like Apple, the Company Facts response can exceed 10 MB of JSON, containing tens of thousands of individual fact records across hundreds of concepts spanning fifteen years of filings. The breadth of the response is one reason programmatic access to specific concepts is also available through the Company Concept API.

The EDGAR Company Concept API

When a researcher wants only a single concept for a single company — for example, all reported values of EarningsPerShareBasic for Apple across every filing — the Company Concept API returns exactly that slice without the overhead of the full Company Facts payload:

https://data.sec.gov/api/xbrl/companyconcept/{CIK}/us-gaap/{concept}.json

For Apple's basic EPS history: https://data.sec.gov/api/xbrl/companyconcept/CIK0000320193/us-gaap/EarningsPerShareBasic.json

The response structure mirrors the per-concept slice of the Company Facts API: a units object keyed by unit string, each containing the array of fact values with the same fields described above. The Company Concept API is the efficient choice when building a single-concept time series for a small number of companies; the Company Facts API is more efficient when multiple concepts are needed for the same company (one large request versus many small ones).

The EDGAR Frames API

The Frames API is the most powerful query type in the EDGAR XBRL data infrastructure for cross-sectional analysis. Instead of returning one company's history for a concept, it returns all companies' reported values for a single concept in a single period:

https://data.sec.gov/api/xbrl/frames/us-gaap/{concept}/{unit}/{period}.json

Period codes follow a consistent convention. Duration concepts (income statement items) use CY{year}Q{n} for quarterly periods or CY{year}for annual periods. Instant concepts (balance sheet items) append an Isuffix: CY{year}Q{n}I. Examples:

  • us-gaap/Revenues/USD/CY2024Q4.json — all companies' reported revenues for Q4 2024 (three-month duration ending December 31, 2024 for calendar-year companies).
  • us-gaap/Assets/USD/CY2024Q4I.json — all companies' total assets as of the Q4 2024 balance sheet date.
  • us-gaap/NetIncomeLoss/USD/CY2024.json — all companies' annual net income for the 2024 calendar year (twelve-month duration).

The Frames response contains an array named data where each element is a six-item array: [accessionNumber, cik, entityName, locationCode, periodEnd, value]. A single frames call for a high-coverage concept like Revenues or Assets returns data for two thousand or more companies. This makes the Frames API the correct starting point for any cross-sectional screen — ratio analysis, sector aggregations, valuation multiple construction — because two API calls (one per period) provide data across the entire public company universe, compared to thousands of individual Company Facts calls.

The Frames API only returns facts with an assigned frame code, meaning it covers facts that the SEC has mapped to a standard calendar period. Companies with fiscal years that do not align with calendar quarters may have some filings excluded from specific frame periods. The Company Facts API with filtering on formtype and end date is the fallback for fiscal-year-aligned analysis.

Data Quality Challenges

Working with EDGAR XBRL data at scale requires anticipating and handling several systematic data quality issues that affect most large analytical applications.

Extension Elements

The US-GAAP taxonomy does not cover every possible financial statement line item. Companies are permitted to define custom extension elements — concept names under their own company-specific namespace — for items that have no direct taxonomy equivalent. A telecommunications company might report a primary revenue line under an extension like xyz:WirelessServiceRevenue rather than the standard us-gaap:Revenues. A bank might report net interest income under an extension rather than the relevant GAAP concept. Studies of EDGAR XBRL data have found that roughly 30 percent of all XBRL facts submitted by US public companies use extension elements rather than standard taxonomy concepts.

Extension elements do not appear in the Frames API (which only covers standard taxonomy concepts) and reduce the coverage of cross-sectional screens built on standard concepts. The effect is most pronounced in regulated industries — banking, insurance, utilities, real estate investment trusts — where GAAP reporting requirements diverge most from the standard taxonomy structure. A revenue screen built solely onus-gaap:Revenues will miss companies that report their revenue underus-gaap:RevenueFromContractWithCustomerExcludingAssessedTax (the ASC 606 concept adopted by most companies after 2018), companies that use industry-specific concepts like us-gaap:BankingRevenues, and companies that use extension elements entirely. A comprehensive screen unions multiple revenue concepts and accepts that coverage will be imperfect.

Taxonomy Concept Changes Over Time

Companies sometimes change which standard concept they use to report the same economic item across filing periods. This occurs after major accounting standard updates: the adoption of ASC 606 (Revenue from Contracts with Customers) in 2018 caused thousands of companies to switch from us-gaap:Revenues to us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax for their primary revenue line. A time-series analysis of a single company's revenue built on the Company Facts API will show a gap at the adoption date if it only fetches one concept. Production implementations maintain a concept mapping table that identifies which alternative concepts to fetch and union for each line item, per company, across periods.

Dimensional Data and Segments

XBRL supports multidimensional data through a mechanism called context. A fact reported for a specific business segment or product line carries dimension-member pairs in its context: for example, revenues reported for the North America segment carry the axis srt:StatementGeographicalAxis with member country:US. The top-level consolidated revenue fact has no dimensional context. In the EDGAR XBRL data model, facts without dimensional context are the consolidated-entity totals; facts with dimensional context are disaggregated by segment, geography, product, or other dimensions.

The Company Facts API returns all dimensional facts alongside the consolidated totals. For a company with eight geographic segments and five product lines, the response contains the consolidated revenue figure plus potentially forty or more dimensional revenue facts. A cross-sectional screen that sums all revenue facts without filtering out dimensional facts will double-count revenue. The correct approach is to include only facts without a dimensional context for consolidated-entity analysis, or to explicitly select the dimensional context of interest for segment-level analysis.

Fiscal Year Alignment

US public companies report on fiscal years that do not always align with the calendar year. Apple's fiscal year ends in late September; retailers like Walmart end their fiscal year in January; banks generally align with the calendar year. When using the Frames API for a quarterly period like CY2024Q4, the result includes only companies whose three-month reporting period ended on or around December 31, 2024. A company with a September fiscal year end reports Q4 results in December for its fiscal Q4 ending September 30 — that result appears in the Frames API under CY2024Q3 (the calendar-year quarter containing September). Cross-sectional comparisons using the Frames API mix companies from different points in their respective business cycles, which is relevant for any screen sensitive to seasonality.

Zero Values and Missing Data

A reported value of zero is not the same as a missing observation. Companies that report zero goodwill have made no acquisitions (or have fully impaired their goodwill); that zero is a meaningful data point. Companies that do not report a concept at all — because the line item is not applicable to their business — do not appear in the Frames API for that concept. The absence of a fact in the Frames API therefore conflates two very different situations: the company reported zero, and the company did not report this concept at all. The Company Facts API can distinguish these cases by directly checking whether any fact exists for the concept; the Frames API cannot.

Analytical Applications

The breadth and depth of the EDGAR XBRL dataset supports a range of quantitative applications that previously required expensive commercial data licenses or manual data collection.

Valuation multiple screening. Price-to-earnings, price-to-book, and enterprise-value-to-EBITDA ratios require financial statement inputs that are all available in EDGAR XBRL data. A quarterly EV/EBITDA screen requires operating income (us-gaap:OperatingIncomeLoss), depreciation and amortization (typically from the cash flow statement or notes), long-term debt, and cash from the balance sheet. All four are available via the Frames API with two to four requests per quarter, yielding a complete cross-section of EBITDA multiples across the US public company universe.

Sector and industry aggregation. The EDGAR XBRL data does not itself contain SIC codes or GICS sector classifications, but the SEC's company submissions endpoint (https://data.sec.gov/submissions/CIK{010-digit-CIK}.json) includes the SIC code and SIC description for each filer. Joining the Frames API output to submissions-derived SIC codes produces sector-aggregated revenue or earnings totals — a bottom-up reconstruction of sector-level financial metrics from individual company filings.

Fraud detection research. The Beneish M-score, a widely cited financial statement fraud detection model, requires eight financial ratios constructed from income statement and balance sheet data. All eight ratio components are available as standard XBRL concepts: days sales in receivables (accounts receivable and revenue), gross margin (gross profit and revenue), asset quality (current and noncurrent assets), sales growth (two periods of revenue), depreciation, SG&A expenses, leverage (total debt and assets), and total accruals (net income and operating cash flow). A cross-sectional Beneish screen across all EDGAR filers for a given period requires approximately twelve Frames API calls and can be built without any commercial data vendor involvement.

ESG metric extraction. The SEC adopted mandatory climate-related disclosure rules in 2024 requiring large accelerated filers to disclose Scope 1 and Scope 2 greenhouse gas emissions in their 10-K filings beginning with fiscal years ending in 2025. These disclosures use standardized XBRL tags under the us-gaap taxonomy extensions adopted for environmental reporting. As filings accumulate, the Frames API will support cross-sectional ESG emissions screens analogous to the financial statement screens currently available.

Time-series financial modeling. The Company Facts API provides a complete per-company history of every XBRL-tagged line item across every 10-K and 10-Q ever filed. For a company like Apple with filings going back to fiscal year 2009, this is more than fifteen years of quarterly revenue, earnings, and balance sheet data available in a single API call. Building a discounted cash flow model, a regression of revenue against macroeconomic variables, or a time-series decomposition of margin trends requires only the Company Facts API and a filtering step to select the relevant concept, form type, and period range.

Data Access and Rate Limits

All EDGAR XBRL APIs are available at data.sec.gov without authentication or an API key. The SEC imposes a rate limit of ten requests per second from a single IP address. Requests that exceed this rate receive HTTP 429 responses and may trigger temporary blocking. Every automated request must include a descriptive User-Agent header identifying the requester by name and contact email — for example, User-Agent: Research Project researcher@university.edu. Requests with generic or missing User-Agent headers are blocked.

For high-volume work, the SEC publishes quarterly bulk downloads at www.sec.gov/dera/data/financial-statements through its Division of Economic and Risk Analysis. Each quarterly zip contains four pipe-delimited files:

  • num.txt — all numeric XBRL facts for the quarter; one row per fact with accession number, concept tag, period, unit, and value.
  • sub.txt — one row per filing with entity metadata: CIK, company name, SIC code, fiscal year end, state of incorporation, and filing dates.
  • pre.txt — the presentation structure of each filing, showing which concepts appear in which financial statement and in what order.
  • cal.txt — calculation relationships from the filing's calculation linkbase, showing which concepts add up to which totals within the filing.

The num.txt file for a single quarter can exceed 500 MB uncompressed and contains every XBRL fact reported by every filer during that period. For bulk analysis covering many companies across many quarters, downloading and loading the quarterly zip files into a local database is more efficient than streaming from the API. For targeted per-company or per-concept queries, the JSON APIs are simpler and do not require managing local data infrastructure.

Python: Apple Revenue and Net Income History

The script below fetches Apple's full XBRL company facts from the SEC API, extracts annual revenue and net income series from the 10-K filings, aligns them by fiscal year end date, prints a summary table with net margin, and produces a dual-axis time series chart with revenue as a bar chart on the left axis and net income as a line on the right axis. The same pattern generalizes to any concept and any CIK by substituting the concept name and CIK constant.

import requests
import json

# Apple Inc. CIK, zero-padded to 10 digits
APPLE_CIK = '0000320193'
HEADERS = {'User-Agent': 'research@example.com'}

def fetch_company_facts(cik: str) -> dict:
    url = 'https://data.sec.gov/api/xbrl/companyfacts/CIK' + cik + '.json'
    r = requests.get(url, headers=HEADERS, timeout=30)
    r.raise_for_status()
    return r.json()

def extract_annual_series(facts: dict, concept: str) -> list:
    '''
    Extract annual (10-K) values for a us-gaap concept.
    Returns list of (year_end_date, value) sorted by date.
    Keeps only facts with a 'frame' assigned (deduplicates restatements).
    '''
    usd_facts = (
        facts
        .get('facts', {})
        .get('us-gaap', {})
        .get(concept, {})
        .get('units', {})
        .get('USD', [])
    )
    # Keep 10-K filings with a frame (annual calendar-year periods)
    annual = [
        f for f in usd_facts
        if f.get('form') == '10-K' and f.get('frame', '').startswith('CY') and 'Q' not in f.get('frame', '')
    ]
    # Deduplicate by frame, keeping the most recently filed value
    best = {}
    for f in annual:
        frame = f['frame']
        if frame not in best or f['filed'] > best[frame]['filed']:
            best[frame] = f
    result = sorted(best.values(), key=lambda x: x['end'])
    return [(r['end'], r['val']) for r in result]

print('Fetching Apple XBRL company facts...')
facts = fetch_company_facts(APPLE_CIK)
entity_name = facts.get('entityName', 'Apple Inc.')
print('Entity: ' + entity_name)

# Revenue: Apple reports under RevenueFromContractWithCustomerExcludingAssessedTax (post-ASC 606)
# Fall back to Revenues for older years
revenue_series = extract_annual_series(facts, 'RevenueFromContractWithCustomerExcludingAssessedTax')
if not revenue_series:
    revenue_series = extract_annual_series(facts, 'Revenues')

net_income_series = extract_annual_series(facts, 'NetIncomeLoss')

# Align by fiscal year end date
rev_map = dict(revenue_series)
ni_map = dict(net_income_series)
common_dates = sorted(set(rev_map) & set(ni_map))

BILLION = 1_000_000_000

print('')
print(entity_name + ' Annual Revenue vs Net Income ($ billions)')
print('-' * 60)
print('Fiscal Year End'.ljust(20) + 'Revenue ($B)'.rjust(16) + 'Net Income ($B)'.rjust(18))
print('-' * 60)
for date in common_dates:
    rev_b = round(rev_map[date] / BILLION, 2)
    ni_b  = round(ni_map[date] / BILLION, 2)
    margin = round(ni_map[date] / rev_map[date] * 100, 1)
    print(date.ljust(20) + str(rev_b).rjust(16) + str(ni_b).rjust(18) + ('  (' + str(margin) + '% margin)'))

# --- Dual-axis time series plot ---
# Requires matplotlib; install with: pip install matplotlib
try:
    import matplotlib.pyplot as plt
    import matplotlib.ticker as mticker

    dates_plot  = common_dates
    rev_vals    = [round(rev_map[d] / BILLION, 2) for d in dates_plot]
    ni_vals     = [round(ni_map[d] / BILLION, 2)  for d in dates_plot]
    labels      = [d[:4] for d in dates_plot]  # extract year from YYYY-MM-DD

    fig, ax1 = plt.subplots(figsize=(11, 5))
    ax2 = ax1.twinx()

    color_rev = '#0b4a8f'
    color_ni  = '#c0392b'

    ax1.bar(labels, rev_vals, color=color_rev, alpha=0.75, label='Revenue')
    ax2.plot(labels, ni_vals, color=color_ni, marker='o', linewidth=2, label='Net Income')

    ax1.set_xlabel('Fiscal Year', fontsize=11)
    ax1.set_ylabel('Revenue ($ billions)', color=color_rev, fontsize=11)
    ax2.set_ylabel('Net Income ($ billions)', color=color_ni, fontsize=11)
    ax1.tick_params(axis='y', labelcolor=color_rev)
    ax2.tick_params(axis='y', labelcolor=color_ni)

    lines1, labels1 = ax1.get_legend_handles_labels()
    lines2, labels2 = ax2.get_legend_handles_labels()
    ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')

    plt.title(entity_name + ' Revenue and Net Income (SEC EDGAR XBRL)', fontsize=13)
    plt.tight_layout()
    plt.savefig('apple_revenue_net_income.png', dpi=150)
    print('')
    print('Chart saved to apple_revenue_net_income.png')
except ImportError:
    print('')
    print('matplotlib not installed -- skipping chart. pip install matplotlib to enable.')

A few implementation notes. Apple switched from us-gaap:Revenues to us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax when it adopted ASC 606 in fiscal year 2019; the script checks for the newer concept first and falls back to the older one, so the full history stitches together correctly. The frame-based deduplication step — keeping only facts with a frame attribute starting with CY and no Q (annual periods) — filters to standard annual facts and eliminates restatements by keeping the most recently filed value per frame. Apple's fiscal year ends in late September, so the “year” label on the chart is the calendar year of the fiscal year end date rather than Apple's own fiscal year numbering.


SEC Form 13F: The Institutional Holdings Disclosure Behind Every Hedge Fund Tracker — the companion EDGAR dataset covering institutional equity ownership: how the Section 13(f) requirement works, the 45-day filing lag, what 13F systematically excludes (short positions, most derivatives, all non-equity instruments), and Python for parsing Berkshire Hathaway's quarterly holdings from the EDGAR quarterly index.

BEA GDP and National Accounts: The Federal Dataset That Measures the US Economy — the macroeconomic context behind the company-level financial data in EDGAR: how the Bureau of Economic Analysis constructs GDP from C + I + G + (X−M), the three estimate vintages (advance, second, third), and the BEA API for quarterly GDP and PCE data.

Treasury TIC Data: Foreign Ownership of US Securities and the Federal Capital Flow Dataset — the Treasury International Capital system that tracks who holds US Treasuries, agency securities, corporate bonds, and equities: how the custody problem distorts country-level holdings, China's reserve management strategy, and working with TIC data in Python.