dividend_yield_pct is consistent across snapshot, valuation, dividends, and compare: it is the annual dividend rate divided by the latest price (AAPL reports 0.35 on all four). The rate is forward-annualized when the payer’s cadence is detectable and trailing-twelve-month for irregular payers; /v2/dividends labels which convention you got via current.yield_basis. Only last-digit rounding can ever differ; a larger gap means you compared as_of timestamps that span a price move, not a data problem.
Unit suffixes
These are the suffixes you will see across the API, each verified against live fixtures.| Suffix | Meaning | Example from a live response |
|---|---|---|
_usd | Whole US dollars, not thousands, not millions. | market_cap_usd: 4512101567600 is 15.55M. |
_pct | Percentage points, already scaled, not a 0–1 fraction. | dividend_yield_pct: 0.35 means 0.35%; fcf_margin_pct: 24.04 means 24.04%. |
_seconds | A duration in seconds. | cache_age_seconds: 16096. |
| share-count fields | Whole share counts. | shares_outstanding: 14725873000, plus shares_basic, shares_diluted, shares_outstanding_cover_page. |
| no suffix | A dimensionless ratio or multiple. | current_ratio: 1.07, debt_to_equity: 0.8, beta_5y_weekly: 1.17. |
revenue, operating_cash_flow, capex) and price_per_share. The enclosing object carries "currency": "USD"; read it rather than assuming.
The headline multiples pe_ttm, ps_ttm, pb, and ev_ebitda_ttm are the exception: on /v2/snapshot and /v2/valuation they ship as objects, not bare numbers. The scalar lives at .value (snapshot AAPL pe_ttm is { "value": 36.81, "vs_5y": "near_5y_high" }; valuation wraps it as { value, vs_own_history, change? }). /v2/compare unwraps them to bare floats. A negative P/E still binds at pe_ttm.value, but carries a caveat such as "negative_earnings_period" and compare adds pe_ttm_negative_not_meaningful. Read pe_ttm.value on snapshot/valuation; never multiply the object itself.
The _pct rule, worked
_pct values are percentage points. To use one as a multiplier, divide by 100 first.
dividend_yield_pct: 0.35 as the fraction 0.35 overstates the figure by 100x. This is the single most common unit mistake; the divide-by-100 is on you, not the API.
Signs are meaningful: never flip them
A negative number is information. Preserve the sign through your pipeline.drawdown_from_high_pct: -1.8 says the price sits 1.8% below its 52-week high: the negative is the drawdown. A negative working_capital_change is a normal cash-flow movement, not an error. A negative net_value_usd on an insider rollup signals net selling. Stripping the minus sign to make a value “look clean” changes its meaning.
Timestamps and dates
Point-in-time timestamps are UTC ISO-8601 with aZ:
YYYY-MM-DD:
as_of timestamps the payload and its source context (freshness owns its semantics); period_end, filing_date, transaction_date, and event date are business or filing dates, not data-currency signals.
The /v2/history columnar shape
/v2/history returns a columnar table: a columns array naming each position, and rows as compact arrays in that exact order. Read columns first, then index rows by position; never assume a fixed layout, because the columns follow your series request.
Three fields tie a history response together:
period: the lookback window you asked for (1m,1y,5y, …); the request parameter isrange, andperiodechoes it back.cadence: the row spacing:daily,weekly,quarterly,annual, orper-payment.columns: the order of values inside every row.
/v2/history?symbol=AAPL&series=close,volume&range=1m, trimmed to the first three of its daily rows (the live response returns the full month):
GET /v2/history?symbol=AAPL&series=close,volume&range=1m
columns is ["date", "close", "volume"], so row[0] is the date, row[1] the close, and row[2] the volume. Request more series and both columns and each row grow in lockstep: series=close,volume,rsi_14 yields ["date", "close", "volume", "rsi_14"] and four-element rows. /v2/history is always freshness: "end_of_day" (never intraday); it also returns market_status, cache_age_seconds, and a coverage: {start, end} object marking the actual span the response covers. Price values are rounded to two decimal places and volume is an integer, so no further rounding is needed.
How a few cross-cutting metrics are computed
Several numbers carry a methodology that is not obvious from the suffix. Read these before labeling them in a summary:dividend_yield_pct: the annual dividend rate divided by the latest price. The rate is forward-annualized for payers with a detectable cadence and falls back to trailing-twelve-month for irregular payers;/v2/dividendstells you which one you got viacurrent.yield_basis("forward_annualized"or"trailing_12m"), always present whenyield_pctis numeric. Whichever the basis, the value is identical acrosssnapshot,valuation,dividends, andcompare.volume.latest(price action): the most recent session’s consolidated volume;avg_30dis the trailing 30-session average, andratio_to_avgislatest / avg_30d.ratio_to_sector_avgdivides the latest session’s volume by the sector benchmark ETF’s average daily volume; it is value-or-envelope with the same shape and reasons asvs_sector_pct_1ybelow.vs_sector_pct_1y(onsnapshot.performanceandtechnicals.relative_strength): the symbol’s 1-year return minus its sector benchmark ETF’s 1-year return, in percentage points. The field is value-or-envelope: a number, or{ "available": false, "reason": "not_applicable_for_etf" | "sector_benchmark_unavailable" }— the reason rides inside the field itself; there is no siblingvs_sector_statusobject. A barenullmeans exactly one thing: the symbol lacks enough trading history for the window.beta_5y_weekly(onsnapshotandcompare): beta versus the market (SPY) from weekly returns over the trailing ~5 years — the stable systematic-risk estimate (use it for WACC / cost-of-equity). An{ available: false, reason }object when it cannot be computed or comes out implausibly negative.beta_1y_weekly(ontechnicals.volatility): beta versus the market (SPY) from weekly returns over the trailing ~1 year. It is a different number frombeta_5y_weekly— the shorter window reacts faster to a recent change in volatility, so the two can diverge materially. Pick the window you actually need; never mix them.
Absent values
A value can benull, an omitted key, or an { "available": false, "reason": ... } envelope, and the three mean different things. That table is owned by the response model: check there before writing absence-handling code, and do not invent a field that the response did not return.