Financial statements on Massive are four separate endpoints under /stocks/financials/v1/, not one, and each returns a flat record with named columns rather than a nested structure you have to walk. Decide which statement you need before you write the request, because there is no call that returns all four.
Applies to
- Plans: Stocks Advanced, or any plan carrying the Financials and Ratios Expansion. Basic, Starter, Developer and plain Business do not include it. Data is end-of-day, and the oldest record covers a period ending in April 2009.
- Endpoints: /stocks/financials/v1/income-statements, /stocks/financials/v1/balance-sheets, /stocks/financials/v1/cash-flow-statements, /stocks/financials/v1/ratios.
- Asset classes: US stocks.
What one record looks like
Each record is flat: the metadata columns, then the line items as named numbers.
curl -X GET "https://api.massive.com/stocks/financials/v1/income-statements?tickers=AAPL&sort=period_end.desc&limit=1&apiKey=YOUR_API_KEY"
Response
{
"status": "OK",
"results": [
{
"tickers": ["AAPL"],
"cik": "0000320193",
"period_end": "2026-06-27",
"filing_date": "2026-07-31",
"fiscal_quarter": 3,
"fiscal_year": 2026,
"timeframe": "trailing_twelve_months",
"revenue": 466823000000.0,
"cost_of_revenue": 239700000000.0,
"gross_profit": 227123000000.0,
"operating_income": 154859000000.0,
"ebitda": 167959000000.0
}
]
}
The five metadata columns are on every record of all four endpoints. timeframe is quarterly, annual or trailing_twelve_months, and one ticker carries all three interleaved: of AAPL's 147 income statement records, 67 are quarterly, 64 trailing twelve month and 16 annual. Filter on timeframe or you will be comparing a quarter with a year.
How many fields each endpoint returns
The counts below are what AAPL's most recent record carried on each endpoint, so they are the full column set rather than a subset:
| Endpoint | Fields | Examples beyond the metadata |
|---|---|---|
| income-statements | 34 | revenue, gross_profit, operating_income, basic_earnings_per_share, ebitda |
| balance-sheets | 40 | total_assets, total_current_liabilities, retained_earnings_deficit, total_equity |
| cash-flow-statements | 34 | net_cash_from_operating_activities, purchase_of_property_plant_and_equipment, dividends |
| ratios | 23 | price_to_earnings, return_on_equity, debt_to_equity, enterprise_value, free_cash_flow |
Two things to know before you build a fixed-width table out of this. Within one era a concept the company did not report comes back as 0.0 rather than absent, so a zero is not automatically a filed zero. Across eras the column set itself moves: AAPL's 147 income statement records carry 26, 28 or 34 fields, and the oldest ones have no interest_expense, interest_income, depreciation_depletion_amortization, discontinued_operations, equity_in_affiliates, extraordinary_items, noncontrolling_interest or preferred_stock_dividends_declared at all. Read the keys that are there rather than indexing a fixed list.
The filter parameter is not the same on all four
Read this before you debug an unfiltered response. The three statement endpoints filter on tickers, plural, because a record can cover several symbols. ratios filters on ticker, singular. Passing the wrong one is not an error: it is ignored, the request succeeds, and you get whatever the default sort returns, which is a different company entirely.
curl -X GET "https://api.massive.com/stocks/financials/v1/ratios?ticker=AAPL&limit=1&apiKey=YOUR_API_KEY"
Response
{
"status": "OK",
"results": [
{ "ticker": "AAPL", "cik": "0000320193", "date": "2026-09-10", "price": 326.57,
"market_cap": 4766021362600.0, "earnings_per_share": 8.83, "price_to_earnings": 36.97,
"return_on_equity": 1.1991, "debt_to_equity": 0.78, "free_cash_flow": 136683000000.0 }
]
}
ratios is the odd one out in two more ways. It carries a date, the day the ratios were computed from that day's price, rather than a period_end. And it holds no history at all: one record per ticker, the latest, and date is not among the columns sort accepts.
The three statement endpoints also cover currently active companies only. A request for a delisted ticker returns an empty results array rather than its final filings, so this is not the place to research a company that no longer trades.
The older nested endpoint
Code written before these endpoints existed calls /vX/reference/financials, which returns one record holding all four statements nested under a financials object, each line item an object with value, unit, label and order, and no fixed field list. It still responds, but it is no longer in the documentation. The two shapes share only cik, tickers, fiscal_year, filing_date and timeframe, and even timeframe disagrees: ttm there, trailing_twelve_months here. Treat a migration as a rewrite of the parsing layer, not a change of URL.
If you see an error
A response full of the wrong company means the filter parameter was ignored. Check tickers against ticker for the endpoint you are calling.
A 400 on sort means the column is not sortable on that endpoint, and the error names every column that is. The statements sort on period_end; ratios does not accept date.
An empty results array for a company you know filed is usually a delisting. Only active companies are covered.
A NOT_AUTHORIZED response means the plan does not carry financials. It is an Advanced or Expansion feature, not part of the reference data every plan gets.

