Home / Guides / Text extraction
Text extraction

Extract segment revenue from a 10-K with XBRL

Get revenue and operating income by business segment, product line and geography from a 10-K's XBRL, using Alphabet's fiscal 2025 annual report.

Segment data lives in the notes to the financial statements, not on the face of the income statement. In XBRL it is tagged with dimensions: the same concept (revenue) is reported many times, once per business segment, product line or region, each with a segment qualifier. /v1/xbrl-to-json with notes=true returns those notes, so you can rebuild the segment tables without parsing HTML.

How dimensional facts look

Every fact in the response has a value, a period and, when it is a breakdown, a segment. One dimension comes back as an object, several as a list:

{
  "value": "224532000000",
  "period": {"startDate": "2025-01-01", "endDate": "2025-12-31"},
  "unitRef": "usd",
  "segment": [
    {"dimension": "srt:ProductOrServiceAxis", "value": "goog:GoogleSearchOtherMember"},
    {"dimension": "us-gaap:StatementBusinessSegmentsAxis", "value": "goog:GoogleServicesMember"}
  ]
}

The axis tells you what the breakdown is (StatementBusinessSegmentsAxis for segments, ProductOrServiceAxis for product lines, StatementGeographicalAxis for regions). The member tells you which one.

The script

import os
import requests

BASE = "https://secfeedapi.com"
HEADERS = {"Authorization": f"Bearer {os.environ['SECFEED_KEY']}"}

# Alphabet's 10-K for fiscal 2025 (find it with /v1/filings?query=ticker:GOOGL AND formType:"10-K")
params = {"accessionNo": "0001652044-26-000018", "cik": "1652044", "notes": "true"}
d = requests.get(f"{BASE}/v1/xbrl-to-json", params=params, headers=HEADERS, timeout=180).json()

def dims(fact):
    seg = fact.get("segment") or []
    seg = [seg] if isinstance(seg, dict) else seg          # one dimension comes as an object, several as a list
    return {s["dimension"].split(":")[1]: s["value"].split(":")[1].removesuffix("Member") for s in seg}

def by_member(facts, axis, ignore=("ConsolidationItemsAxis", "StatementBusinessSegmentsAxis")):
    """{(member, fiscal year): $ billions} for full-year facts broken down on `axis` and nothing else."""
    out = {}
    for f in facts:
        dm = dims(f)
        if axis in dm and all(k == axis or k in ignore for k in dm) and "startDate" in f["period"]:
            out[(dm[axis], f["period"]["endDate"][:4])] = int(f["value"]) / 1e9
    return out

def table(title, data, years=("2024", "2025")):
    print(title)
    print("  " + "".ljust(42) + "".join(f"FY{y}".rjust(9) for y in years) + "   change")
    for m in sorted({m for m, _ in data}, key=lambda m: -data.get((m, years[-1]), 0)):
        a, b = (data.get((m, y)) for y in years)
        chg = f"{(b / a - 1) * 100:+.0f}%" if a and b and a > 0 and b > 0 else ""
        print("  " + m.ljust(42) + "".join(f"{v:9.1f}" if v else " " * 9 for v in (a, b)) + chg.rjust(9))
    print()

revenue = d["RevenuesRevenueByType"]["RevenueFromContractWithCustomerExcludingAssessedTax"]
op_income = d["InformationAboutSegmentsAndGeographicAreasRevenueAndOperatingIncomeLossBySegment"]["OperatingIncomeLoss"]

table("Revenue by segment ($ billions)", by_member(revenue, "StatementBusinessSegmentsAxis", ignore=()))
table("Operating income by segment ($ billions)", by_member(op_income, "StatementBusinessSegmentsAxis", ignore=("ConsolidationItemsAxis",)))
table("Revenue by product or service ($ billions)", by_member(revenue, "ProductOrServiceAxis"))
table("Revenue by geography ($ billions)", by_member(revenue, "StatementGeographicalAxis"))

Output

Revenue by segment ($ billions)
                                               FY2024   FY2025   change
  GoogleServices                                304.9    342.7     +12%
  GoogleCloud                                    43.2     58.7     +36%
  AllOtherSegments                                1.6      1.5      -7%

Operating income by segment ($ billions)
                                               FY2024   FY2025   change
  GoogleServices                                121.3    139.4     +15%
  GoogleCloud                                     6.1     13.9    +128%
  AllOtherSegments                               -4.4     -7.5         

Revenue by product or service ($ billions)
                                               FY2024   FY2025   change
  GoogleAdvertisingRevenue                      264.6    294.7     +11%
  GoogleSearchOther                             198.1    224.5     +13%
  SubscriptionsPlatformsAndDevicesRevenue        40.3     48.0     +19%
  YouTubeAdvertisingRevenue                      36.1     40.4     +12%
  GoogleNetwork                                  30.4     29.8      -2%

Revenue by geography ($ billions)
                                               FY2024   FY2025   change
  US                                            170.4    194.2     +14%
  EMEA                                          102.1    117.2     +15%
  AsiaPacific                                    56.8     67.7     +19%
  AmericasExcludingUnitedStates                  20.4     23.9     +17%

Reading it

  • Alphabet reports three segments. Google Cloud grew revenue 36% and more than doubled operating income in fiscal 2025.
  • The product table mixes levels: GoogleAdvertisingRevenue is the subtotal of GoogleSearchOther, YouTubeAdvertisingRevenue and GoogleNetwork. Companies tag subtotals and their parts alike, so check the HTML table once before summing members.
  • Operating income by segment is tagged with an extra ConsolidationItemsAxis (operating segments versus corporate costs), which is why the script ignores that axis for that table.

Making it work for other companies

  • Note names differ. Sections are named after the company's own notes (RevenuesRevenueByType here). Search the keys for Segment or Revenue to find them: [k for k in d if "Segment" in k].
  • Concepts differ. Some companies tag segment revenue as Revenues, others as RevenueFromContractWithCustomerExcludingAssessedTax. Try both.
  • Members are company specific. goog:GoogleCloudMember is Alphabet's own extension. Standard members such as us-gaap:OperatingSegmentsMember are rarer.
  • Annual versus quarterly. The script keeps duration facts (with a startDate) and keys them by end year. For 10-Qs, key by the full period to separate the quarter from year to date.

Next steps

More text extraction guides

Run this guide with your own key

500 free API calls a month. Every example on this page works on the free plan.

Get free API key →