Products
Market Data
Equity prices with their freshness, OHLCV bars, survivorship-bias-free daily history, corporate actions, charts, and ETF composition. A successful request does not guarantee a real-time price, so every quote says how old it is.
What's inside
- Latest price.
price_snapshotreturns the last trade in any session, the latest regular session and any extended-hours move with their dates, the current daily bar, the quote while the exchange is in a session, and valuation context computed against the live price, withas_of,price_age_secondsandis_stale. - OHLCV bars.
fundamentals/pricesreturns bars at second, minute, hour, day, week or month granularity for any window. - Research-grade history.
prices/historyreturns daily closes as traded, total and price returns, volume and cumulative adjustment factors from a survivorship-bias-free archive (default window the last 365 days; passstartfor history back to 2000 and earlier), stitched with live daily bars from 2025 to today, plus the delisting record.prices/corporate_returns dividends, splits and distributions from first listing, each as it was known on anactions as_ofdate, with the date the data runs through. - Charts. A candlestick chart as a base64 PNG with RSI, MACD and Bollinger overlays and a summary block of levels.
- ETFs. Holdings of an ETF, the ETFs that hold a stock, and a one-call ETF rollup.
Crypto quotes, the crypto universe, derivatives positioning and crypto news are on Crypto. Two routes on this page also serve crypto when you pass asset_class=crypto: /api/ and /api/v3/fundamentals/prices. The other routes here do not take asset_class, so /api/v3/prices/history/BTC looks up the US-listed security with the ticker BTC, not the crypto asset.
For live bars and ticks over SSE, see Streaming. For intraday minute bars from Wealthnow's own capture, see Tape & Intraday History.
Access
| Routes | Product | Credits/call | Plans |
|---|---|---|---|
/api/v3/intel/chart, /api/v3/intel/etf_* | market_data | 1 | Free and up |
/api/, /api/v3/fundamentals/prices | fundamentals | 1 | Free and up |
/api/v3/prices/history, /api/ | funds | 4 | Starter and up |
The research-grade history routes read a licensed archive and bill with the other licensed research datasets. Failed requests are not billed. See Pricing & credits.
Endpoints
| Method | Path | Plan / credits | MCP tool | Description |
|---|---|---|---|---|
GET | /api/ | Free+ / 1 (fundamentals) | fundamentals_ | Latest price for one ticker: last trade in any session, the exchange session now, the latest regular session and the extended-hours move with their dates, the current daily bar, bid and ask while the exchange is in a session, previous close, freshness fields, and valuation context. ticker (string, required). asset_class (equity or crypto, default equity). |
GET | /api/v3/fundamentals/prices | Free+ / 1 (fundamentals) | fundamentals_prices | OHLCV bars for one ticker, oldest first. ticker (string, required). interval (second, minute, hour, day, week or month, default day). interval_multiplier (integer, 1 to 60, default 1), for example 5 with minute for 5-minute bars. start_date, end_date (YYYY-MM-DD, optional). For equities, a missing start_date starts the window 90 days back for day bars (1 day for second, 5 days for minute, 14 for hour, 365 for week, 5 years for month), and a missing end_date ends it with today's session (the New York date), so the newest bar can still be forming and is marked partial: true. With asset_class=crypto, a missing end_date means today (UTC), and a bar whose UTC period has not ended is partial: true. Equity bars are split-adjusted, not dividend-adjusted; adjusted, adjustment_basis and adjustment_note say which basis a response is on (see Adjusted bars). end_date before start_date returns 422 invalid_date_range, and a date that does not exist (2026-09-31) returns 422 invalid_date. limit (integer, 1 to 5000, default 1000): keeps the most recent N bars in the window. asset_class (equity or crypto, default equity). See Bars still forming. |
GET | /api/ | Starter+ / 4 (funds) | prices_history | Survivorship-bias-free daily history: closes as traded, total and price returns, volume and cumulative split adjustment factors from the archive (factors are null before 2000; see below), stitched with live daily bars from 2025 to today, plus the delisting record. Every row is on one basis, stated in price_basis and returns_basis; a price step no recorded split explains is refused with 503, never served. start (YYYY-MM-DD, default: the last 365 days, with start_defaulted: true and a window_note; pass start=2000-01-01 for the full daily archive). end (YYYY-MM-DD, default today). limit (integer, 1 to 15000, default 15000). |
GET | /api/ | Starter+ / 4 (funds) | prices_ | Cash dividends, stock splits and distributions from first listing, each event in its newest version known on as_of, with distribution types, amounts, price and share factors, and declare, ex, record and pay dates. as_of (YYYY-MM-DD or ISO 8601, optional; a bare date means the end of that UTC day; default now). start (YYYY-MM-DD, optional): earliest ex-date; default is all history. limit (integer, 1 to 5000, default 1000): rows, oldest ex-date first. |
GET | /api/v3/intel/chart/{ticker} | Free+ / 1 | intel_chart | Candlestick chart as a base64 PNG plus an OHLCV summary block. Bars end on the previous UTC day at every interval, so current_price is the last completed bar's close, not a live price; use price_snapshot for the current price. interval (minute, hour, day, week or month, default day). bars (integer, 20 to 500, default 120). indicators (comma-separated subset of rsi, macd, bb; default rsi,macd,bb). |
GET | /api/v3/intel/etf_ | Free+ / 1 | intel_etf_holdings | ETF composition. etf (string): full holdings of that ETF. ticker (string): every ETF that holds the stock, with its weight. Pass at least one; when both are passed, etf wins. limit (integer, 1 to 1000, default 50). When neither the warehouse copy nor the live feed can answer, the route returns 503 with error: "upstream_ (for example error_), not billed, instead of an empty list. |
GET | /api/ | Free+ / 1 | intel_etf_summary | One-call ETF rollup: top constituents by weight, commodity exposure for commodity-tracking ETFs (USO, BNO, GLD, IAU, SGOL, SLV, SIVR, UNG, BOIL, CPER), and available_signals, a fixed list of related tools. |
Crypto quotes (/api/crypto/{ticker}) moved to Crypto.
Read the latest price and its freshness
price_snapshot returns one snapshot object. Read the freshness and session fields before you treat price as current: off hours, on weekends and on holidays the price is an older trade, and the snapshot says so instead of hiding it.
| Field | Meaning |
|---|---|
price | The last eligible trade in any session: pre-market, regular or after hours. It is never an exchange's official-close message: when the feed's last print is an official open or close, a corrected close, or a late or away-priced trade, the newest real trade at or before it is served, with its own time. If there is no trade, today's bar close, then the previous session's close. null, never 0, when no price exists. |
price_session | The session price traded in: pre, regular, post or closed. The closing auction print, stamped just after the bell, is regular. |
as_of, time | UTC time of the last trade. time_milliseconds is the same instant in epoch milliseconds. |
price_age_seconds | Seconds between the last trade and the response. null when the trade time is unknown. |
is_stale | true when the last trade is more than 6 hours old. The price is still returned, flagged rather than removed. false when price_age_seconds is null: the age could not be measured, and price may then be a previous session's close, so treat that case as unverified, not fresh. |
session, market_session | The exchange session when the answer is served, from the exchange clock and calendar (US Eastern): pre 04:00 to 09:30, regular 09:30 to 16:00, post 16:00 to 20:00, closed otherwise and all day on weekends and exchange holidays. On half-days the regular session ends at 13:00 and post at 17:00. The two fields always carry the same value. |
bid, ask, bid_size, ask_size, quote_as_of | The latest quote, returned only while the exchange is in a session (pre, regular or post) and only when the quote was set during that session. quote_as_of is when the quote last changed (UTC), at or after the session's start; an unchanged quote on a quiet name is still the live one. Otherwise all five are null, never a quote from an earlier session. |
prev_close, prev_close_date | The official regular-session close before the trading day of the last trade, and that session's date. |
day_change, day_change_percent | price - prev_close, and that change over prev_close in percent points (1.5 is 1.5%), in every session. Outside regular hours they include extended-hours trading. Every change on this route is the exact price difference (2.715, never 2.7150000000000034), and every percent has 6 significant figures. |
regular_session | The latest regular session: date, status (in_progress, or closed from 5 minutes after the bell), open, high, low, close (null until the session is closed), last (the latest regular-session trade, or the close), volume (the daily bar's, extended hours included), prev_close, prev_close_date, change and change_percent (against prev_close). Use it for how the stock did in that session. |
extended_hours | The move since the latest regular close: session (pre or post), price, as_of, reference_close, reference_close_date, change and change_percent. null during regular hours, or when the last trade was a regular-session trade. Never report this move as a session's change. |
day_open, day_high, day_low, day_close, day_volume, day_vwap, day_bar_date | The daily bar of day_bar_date. Before the open, that is the previous session's bar. |
session_detail_status | ok, or why a session field is null: partial (a date or an earlier close could not be established) or unavailable. |
eps_ttm | Diluted EPS in USD per share, summed over the four latest consecutive fiscal quarters, each on today's share basis: a quarter filed before a split is restated for it. null when that basis cannot be established; eps_ttm_basis says why. |
eps_ttm_basis | How eps_ttm was put on today's share basis: status (ok, or why EPS is withheld, for example incomplete_ttm, split_basis_unknown, inconsistent_ or cross_check_failed), basis (as_reported when no split touches the four quarters, split_adjusted when quarters were restated, as_ when the split history could not be read and the quarters show one share basis), splits_checked and splits_source (last_known_good when the latest read of the split history failed and the last good copy was used). split_since_inputs appears when a split executed after the inputs were fetched and EPS, book value and shares were rescaled for it. |
pe_ttm | price / eps_ttm, a multiple. null when eps_ttm is not positive or is null. |
book_value_per_share, price_to_book | Book value per share of the latest quarter in USD, on today's share basis, and price / book_ as a multiple. |
forward_pe | pe_ttm / (1 + g), where g is the latest fiscal year's year-over-year EPS growth, or its net income growth when a split may sit between the two fiscal years. It is arithmetic on trailing figures, not an analyst consensus. null when either input is missing or 1 + g is not positive. |
forward_pe_basis | Always pe_, the formula above. |
forward_pe_growth | The growth forward_pe used: source (eps_growth_yoy or net_income_growth_yoy), fiscal_year, percent (a percent number: 22.7 is 22.7%) and splits_checked. null when forward_pe is null. |
dividend_yield | A fraction (0.0031 is 0.31%): regular cash dividends per share with an ex-date in the last 365 days, split-adjusted to today's share basis, over the live price. Specials are excluded. null, never 0, for a non-payer, when the dividend record could not be read, when the dividends are in another currency than the price, or when the figure fails the plausibility check (see dividend.status). |
dividend_ | A fraction: the latest declared regular dividend, split-adjusted, times payments per year, over the live price. A declared dividend counts before its ex-date. null unless dividend.status is ok. |
dividend | The per-share figures behind both yields, in the dividends' currency (see units): status, reason, trailing_per_share, trailing_window_days (365), indicated_, frequency (payments per year), special_ttm_per_share (specials in the same window, excluded from both yields), currency, last_regular (cash_amount as paid, split_, currency, the declaration, ex-dividend, record and pay dates, frequency and distribution_type), source (the market-data corporate-actions feed) and as_of (the US Eastern date the window ends on). See Dividend status. |
market_cap | USD: the live price times the latest quarter's share count, on today's share basis. |
units | The unit of every valuation field above, by field name. |
valuation_as_of | When the inputs to the valuation fields (trailing EPS, share count, book value per share, growth, and the dividend and split events) were fetched. Complete inputs are kept for up to 6 hours; the price they are applied to is always the live one, and the 365-day dividend window moves with the calendar. Set only when valuation_status is ok. |
valuation_status | ok: the inputs were fetched; a field can still be null when this company lacks that one input (no dividend, no growth figure). unavailable: the fetch failed, or the provider has no inputs for this security (a fund, for example). pending: the inputs are still being fetched; call again in a few seconds. pending is not returned yet; the changelog will say when it is. When the status is not ok, every valuation field is null, including dividend_, dividend, eps_ttm_basis, forward_pe_growth and valuation_as_of; only forward_pe_basis and units are still served. Neither unavailable nor pending means the company has no earnings or no dividend. |
Volumes are whole shares. day_volume and regular_session.volume are integers, extended hours included, and so is volume on equity bars from prices (fractional-share trades are rounded to the nearest share). While a session is forming, both snapshot volumes are the live day bar's running tally. Once the session is final, regular_session.volume is the consolidated daily total, the same figure prices serves for that day; the running tally can differ from it by a few hundred shares.
With asset_class=crypto, the snapshot is the crypto asset's last trade and its event time. Valuation fields are omitted, not set to null, because a crypto asset has no P/E. A crypto price older than 15 minutes, future-dated or undated is not served: the call is refused (see below), and the snapshot reports the limit as max_age_seconds.
Dividend status
dividend.status says which yields you can use. reason gives the detail when the status is not ok.
status | Meaning | dividend_yield | dividend_ |
|---|---|---|---|
ok | A regular payer on schedule. | Served | Served |
non_payer | Nothing went ex-dividend in the last 365 days and nothing is declared. | null | null |
overdue | The last regular ex-date is more than 1.5 payment intervals old (137 days for a quarterly payer) and nothing newer is declared. | Served | null |
irregular | No regular dividend, or the payment frequency cannot be established. | Served when regular dividends went ex in the window | null |
unavailable | The dividend record could not be read. No fallback figure is used. | null | null |
currency_mismatch | The dividends are in another currency than the price; currency names it. | null | null |
split_ | A dividend's split-adjusted amount disagrees with the split history, so it is not guessed. | null | null |
implausible | A yield above 10% that a second basis does not confirm within 2x (trailing dividends paid from the cash-flow statement over shares, or else the other yield). Both yields are withheld. | null | null |
Before 2026-09-29 (MCP manifest 2.154), dividend_yield was the last fiscal year's dividends paid over that year's shares, as filed. That lagged a raise by up to a year and ignored later splits: NVDA read 0.017% against 0.23% trailing, and BKNG read 23.8% against about 1% after its 25-for-1 split.
When no price can be retrieved
When every price source fails, price_snapshot and prices answer 503, not billed, with error: "upstream_, error_ (also on the X-Error-Code header), is_ and a vendor_error naming the failure. prices carries no items field, so the refusal cannot be read as a symbol with no bars. price_snapshot keeps its snapshot, with price: null.
A crypto snapshot whose last trade is older than 15 minutes answers the same 503 with error_. Its snapshot carries is_stale: true, price_age_seconds and stale_price_suppressed, and the price is withheld. A future-dated or undated trade is price_feed_unavailable, with vendor_error.kind future_live_trade or invalid_live_trade.
Treat both as "price unknown", never as a symbol with no price. Neither says anything about the whole route: another ticker can answer normally at the same moment. An empty series with no vendor error, such as a delisted symbol or a window with no bars, is a 200 with items: [].
timestamp on the response envelope is when the API answered. It is not a market time.
Bars still forming
fundamentals/prices serves the bar that is still forming instead of stopping at yesterday: for equities, for C: forex and X: crypto tickers, and, since 2026-09-25, with asset_class=crypto. A bar whose period has not ended carries partial: true: its close is the latest price, not a final close. For a US equity, a day bar stays partial until the tape closes at 20:00 ET. A C: or X: day bar, and every asset_class=crypto bar, runs on the UTC day: today's day bar is partial until that UTC day ends, and the open week's bar until its week ends. Before 2026-09-25, asset_class=crypto served today's bar and the open week's bar as if final, without these fields. Finished bars are returned unchanged, without a partial key. The response adds:
| Field | Meaning |
|---|---|
as_of, data_through | How far the data runs, the same instant under two names. While the last bar is forming it is the fetch time; otherwise it is that bar's end (16:00 ET for a finished equity day bar, even on an early-close day). |
as_of_basis | fetched_at, bar_end or session_close: which of those as_of is. |
last_bar_partial | Whether the newest bar in items is still forming. |
session_complete | false while more bars can still arrive in the window: today's session is in it and the tape has not closed, or it reaches a later trading day. For C: and X: tickers and asset_class=crypto it follows the UTC day. |
A window with no bars yet, for example today's minute bars before the open, is a 200 with items: [], not an outage. Skip partial: true bars when you need settled closes, and read session_complete before treating a window as final.
Adjusted bars
fundamentals/prices states the price basis of its bars in three fields:
| Case | adjusted | adjustment_basis | adjustment_note says |
|---|---|---|---|
| Equity bars from the primary market-data source | true | splits | Split-adjusted to the share basis in force when fetched, not adjusted for dividends. A bar before a split is not the traded price: multiply it (and divide the volume) by the split ratio, or use prices/history for as-traded prices with cfacpr. |
| Equity bars from the fallback source | null | unverified | The fallback source's split adjustment is not verified, so do not treat these bars as either traded or adjusted prices without checking. |
asset_class=crypto | false | none | Crypto bars are traded prices; no corporate actions apply. |
For example, KLAC traded at 2,108.06 on 2026-06-08, before its 10-for-1 split on 2026-06-12; the day bar for that date reads 210.806.
Research-grade daily history
prices/history is built for backtests and long-horizon charts. The archive keeps securities that later delisted, so a universe drawn from it does not silently drop the losers.
- Window. With no
start, the window is the last 365 days, and the response says so withstart_defaulted: trueand awindow_note. Passstart=2000-01-01(or earlier) for the full daily archive. - Rows carry
date,prc(the close as traded),openprc,bidlo,askhi,vol(shares traded that day, as traded),ret,retx,shrout(nullon live rows), the cumulative adjustment factorscfacpr(prices) andcfacshr(shares), andsource:archive_dailyfor archive rows,live_daily_barsfor live rows. - One price basis on every row.
prcis the price as traded, on archive and live rows alike.prc / cfacpris the price on today's share basis, andvol * cfacshrthe volume on today's share basis. Before a forward splitcfacpris above 1 (BKNG's 2025 rows carry 25 for its 25-for-1 split); before a reverse split it is below 1 (a 1-for-30 reverse split gives 1/30). A negativeprcfollows the archive's convention for a day with no trade: it is the bid/ask midpoint, so useabs(prc). - Returns.
retxis the price return, close to close, dividends excluded, on every row.retis the total return. On archive rows it is the archive's own figure. On live rows it isretxplus the cash dividend on its ex-date over the prior close, that is(P_t + D) / P_(t-1) - 1with the USD dividendD(two dividends on one ex-date are summed). Liveretisnullwhen the dividend record could not be read, on a day whose dividend is not in USD, whenretxisnull, and on a dividend day when the split events could not be read;retxis still the price return. Compoundretfor a total-return series, neverretx. returns_basis(whenever the window runs past 2024-12-31) states whatretandretxmean in each segment (archive,live),live_dividends_status(applied,not_configured,timeout,read_failed,bad_payload,truncatedorno_time_left), the dividends applied (live_dividends:ex_date,cash_amount,currency,split_) andadjusted_ cash_ amount live_rows_without_ret. When the dividend record could not be read on this call (any status butappliedandnot_configured),warningsays so and the answer is not cached: retry for the total returns.price_basis(whenever the window runs past 2024-12-31) states the price basis:prc(as_traded, orunverified_when the split events could not be read),split_ events_ unread adjusted_price(prc / cfacpr),adjusted_volume(vol * cfacshr),live_rows_restoredandrestored(the live rows that had been stored on a later split's basis and were put back to as traded:first_date,last_dateand the splitfactor),evidence(how each live row's basis was established, counted by kind),continuity_checked(true) and, when present,flagged_steps.- Continuity check. Every seam where a live row follows another row within 5 calendar days is checked. A price step of 1.25x or more that no recorded split explains is refused (see below) when the two sessions are adjacent and the later row carries its return, or when the step is the size of a recorded split. A step across a missing session, or onto a row with no return, is served and listed in
price_(basis.flagged_ steps from_date,to_date,from_segment,unexplained_factor,why:unexplained,missing_roworno_return,refused: false). - Segments.
segmentisstitchedwhen the answer includes archive rows andlive_tailwhen it came entirely from live daily bars. A smalllimitfor recent dates is served from the live tail alone. The archive covers 2000 to 2024; rows from 2025 on come from live bars. When a response carrieslive_splitsor anadjustment_note, read it before adjusting across that boundary. A split the source lists twice is applied once. A window with no rows carrieswarningno_rows:archiveorno_rows:live, naming the segment that was read. - When the live bars cannot be read. The 2025-to-date bars are read within the request's time budget. If they cannot be read and the archive covers part of the window, the archive rows are served with
live_and asegment: "unavailable" warningthat rows after 2024-12-31 are omitted; retry for them. If nothing else covers the window, the route answers503witherror: "upstream_andunavailable" error_(code: "dataset_ unavailable" reason: "source_), not billed, instead of aunreadable" 504or an empty list. When the post-2024 split events cannot be read and the series does not step, the answer is a200withprice_and abasis.prc: "unverified_ split_ events_ unread" warningthatcfacprandcfacshrmay not reflect them; when it does step, it is the503 split_events_unreadbelow. - Before 2000, adjustment factors are null. The archive's rows before 2000 were loaded without
cfacprandcfacshr, so both arenullthere until the archive is restated. When any returned row lacks them, anadjustmentblock says so (factors_,available: false rows_without_factors, the dates they span) andnoteno longer suggests adjusting with them.retandretxare unaffected: compute returns from them, and do not treat a null factor as 1. - ETFs and ADRs. Between 2000 and 2024 the archive holds common stocks only, and for many ETFs and ADRs the archive key cannot be proved at all, so such a symbol can return no rows before 2025.
notesays why (for example SPY and TSM in 2010). - Delisting.
delistingholds the archive's delisting record when the security left the tape. - Identity. The ticker is resolved to one security first. When no archive key can be proved for the symbol, the response returns
error: "ticker_with no rows rather than another company's history.not_ in_ registry" identityandidentity_verifiedstate how the match was made; on a live-tail answer that skipped the lookup,identity.reasonissecurity_.id_ lookup_ skipped
Refusals on the price basis. These answer 503 with error: "upstream_, the code in error_code and on X-Error-Code, retryable, Retry-After, is_ and billed: false. None is billed, and none carries rows.
error_code | retryable | Retry-After | When |
|---|---|---|---|
price_ | false | 3600 | The series steps between two rows and no recorded split explains it. seams (up to 10) names each step; narrow the window to one side of it. |
split_events_unread | true | 30 | The split events could not be read and the series steps by more than its returns explain. Retry. |
price_basis_unknown | false | 3600 | A live row before a later split has no return chain or fetch time that places its basis. Widen the window past the split date. |
prices/corporate_ reads a distributions archive that records every version of each event. For every event (security, ex-date, sequence number) it serves the newest version whose known_from is at or before as_of; an event whose newest such version removes it is left out. The response carries as_of, current_through (the newest ex-date the archive holds, read from the data) with current_through_basis, and a versions block counting events held, known, removed, not yet known and revised after as_of. clock.clock_basis is vendor_: a version counts as known from the earlier of known_from (the vendor's release schedule) and first_held_at (when Wealthnow first stored it), so an event the archive already holds is served even when the vendor's schedule dates it later. Treat history before 2026-09-23 as the vendor's record rather than point-in-time observation. The archive is refreshed by hand until its weekly job runs (coverage.refresh). If it cannot be read, the route answers 503 with error_, not billed; it never falls back to the retired archive. Rows are sorted by ex-date (exdt) and carry the per-share cash amount (divamt), the price factor (facpr), the share factor (facshr), the declare, record and pay dates, distype and the related type fields, and the version (version_seq, known_from, first_held_at, change_kind, changed_fields). distcd is null: the archive's type fields replace it. distype FRS marks a split or stock dividend; a facpr of 1.0 is a 2-for-1 split, 0.5 is a 3-for-2 split.
Examples
Latest price with freshness (1 credit)
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/fundamentals/price_snapshot?ticker=AAPL"Live response captured 2026-10-02 at 16:15 UTC, in regular hours (units and dividend left out):
{ "ok": true, "timestamp": "2026-10-02T16:15:27.432530+00:00", "ticker": "AAPL", "snapshot": { "ticker": "AAPL", "price": 332.805, "as_of": "2026-10-02T16:15:27Z", "source": "market-data:v2-snapshot", "price_age_seconds": 0.17134451866149902, "is_stale": false, "day_change": 2.485, "day_change_percent": 0.752301, "prev_close": 330.32, "bid": 332.79, "ask": 332.82, "bid_size": 280, "ask_size": 120, "quote_as_of": "2026-10-02T16:15:27Z", "time": "2026-10-02T16:15:27Z", "time_milliseconds": 1790957727261, "day_open": 333.26, "day_high": 334.54, "day_low": 330.61, "day_close": 332.97, "day_volume": 14069369, "day_vwap": 332.6572, "session": "regular", "pe_ttm": 38.16571100917431, "forward_pe": 31.104898947982324, "price_to_book": 45.36476650036804, "dividend_yield": 0.0031850483015579694, "market_cap": 4877626688550.0, "eps_ttm": 8.72, "book_value_per_share": 7.3362, "dividend_yield_indicated": 0.0032451435525307614, "forward_pe_basis": "pe_ttm_over_trailing_yoy_growth", "eps_ttm_basis": { "status": "ok", "basis": "as_reported", "splits_checked": true, "splits_source": "market_data" }, "forward_pe_growth": { "source": "eps_growth_yoy", "fiscal_year": 2025, "percent": 22.7, "splits_checked": true }, "valuation_as_of": "2026-10-02T14:56:14+00:00", "valuation_status": "ok", "market_session": "regular", "price_session": "regular", "prev_close_date": "2026-10-01", "day_bar_date": "2026-10-02", "regular_session": { "date": "2026-10-02", "status": "in_progress", "open": 333.26, "high": 334.54, "low": 330.61, "close": null, "last": 332.805, "volume": 14069369, "prev_close": 330.32, "prev_close_date": "2026-10-01", "change": 2.485, "change_percent": 0.752301 }, "extended_hours": null, "session_detail_status": "ok" }}The exchange was open, so market_session and price_session are both regular, a bid and ask are present with their own quote_as_of, and extended_hours is null. regular_session.close is null because the session had not finished. After 20:00 ET the same call answers with market_, bid and ask null, and price the last after-hours trade, with the move since the close in extended_hours.
import os, requests r = requests.get( "https://firm.wealthnow.io/api/v3/fundamentals/price_snapshot", params={"ticker": "AAPL"}, headers={"Authorization": f"Bearer {os.environ['WEALTHNOW_API_KEY']}"}, timeout=30,)r.raise_for_status()snap = r.json()["snapshot"]if snap.get("price") is None: print("No price available.")elif snap.get("price_age_seconds") is None: print(f"{snap['ticker']} {snap['price']} (trade time unknown; may be an older close, do not quote as live)")elif snap.get("is_stale"): print(f"{snap['ticker']} last traded at {snap['price']} on {snap['as_of']} (stale; do not quote as live)")else: change = snap.get("day_change_percent") label = f"{change:+.2f}%" if isinstance(change, (int, float)) else "change unavailable" print(f"{snap['ticker']} {snap['price']} ({label}), last trade {snap['as_of']}, session {snap['session']}")Daily bars with a date range (1 credit)
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/fundamentals/prices?ticker=NVDA&interval=day&start_date=2026-06-01&end_date=2026-07-02&limit=30" # Response structure (abbreviated):# {# "ok": true,# "timestamp": "2026-09-23T02:00:26Z",# "ticker": "NVDA",# "asset_class": "equity",# "interval": "day",# "interval_multiplier": 1,# "start_date": "2026-06-01",# "end_date": "2026-07-02",# "adjusted": true,# "adjustment_basis": "splits",# "adjustment_note": "Split-adjusted to the share basis in force when fetched; not adjusted for dividends. ...",# "items": [# {# "ticker": "NVDA",# "open": 215.73,# "high": 224.87,# "low": 215.7,# "close": 224.36,# "volume": 212850685.062286,# "vwap": 221.4785,# "trades": 3465498,# "time": "2026-06-01T04:00:00Z",# "time_milliseconds": 1780286400000# },# ...# ]# }Bars are oldest first, with time in UTC. Equity bars are split-adjusted to the share basis in force when fetched and not dividend-adjusted, so a bar before a split is not the price that traded (see Adjusted bars). limit keeps the most recent N bars in the window. Several crypto symbols are also US-listed equity tickers (BTC, LINK), so pass asset_class=crypto for the crypto asset.
import os, requests r = requests.get( "https://firm.wealthnow.io/api/v3/fundamentals/prices", params={ "ticker": "NVDA", "interval": "day", "start_date": "2026-06-01", "end_date": "2026-07-02", "limit": 30, }, headers={"Authorization": f"Bearer {os.environ['WEALTHNOW_API_KEY']}"}, timeout=30,)r.raise_for_status()data = r.json()bars = data["items"]print(f"{len(bars)} bars for {data['ticker']} ({data['interval']})")for bar in bars[-5:]: print(f"{bar['time'][:10]}: {bar['open']:.2f} -> {bar['close']:.2f}")Split-adjusted history for a backtest (4 credits, Starter and up)
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/prices/history/AAPL?start=2020-01-01&end=2024-12-31"Dividend and split history (4 credits, Starter and up)
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/prices/corporate_actions/AAPL?start=2014-01-01"Chart with overlays (1 credit)
curl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/intel/chart/AAPL?interval=day&bars=120&indicators=rsi,macd"The response carries the image in chart.data_b64 (media_type: "image/png") and a data block with first_bar_time, last_bar_time, data_freshness, current_price, period_high, period_low, period_change_pct, sma_20, sma_50, sma_200, atr_14, recent_support and recent_resistance. A moving average needs enough bars: in a 120-bar daily chart, sma_200 is null. The bars end on the previous UTC day, so current_price (also served as last_close) is the last completed bar's close and data_freshness is that bar's time. For the current price, call price_snapshot.
import base64, os, requests r = requests.get( "https://firm.wealthnow.io/api/v3/intel/chart/AAPL", params={"interval": "day", "bars": 120, "indicators": "rsi,macd"}, headers={"Authorization": f"Bearer {os.environ['WEALTHNOW_API_KEY']}"}, timeout=30,)r.raise_for_status()body = r.json()with open("AAPL.png", "wb") as f: f.write(base64.b64decode(body["chart"]["data_b64"]))print(f"Last close {body['data']['last_close']}, data through {body['data']['data_freshness']}")ETF composition (1 credit)
# Holdings of an ETFcurl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/intel/etf_holdings?etf=SPY&limit=25" # Every ETF that holds a stockcurl -H "Authorization: Bearer $WEALTHNOW_API_KEY" \ "https://firm.wealthnow.io/api/v3/intel/etf_holdings?ticker=NVDA"etf_summary is the one-call version. Live response to GET /, captured 2026-09-23 and abbreviated:
{ "ok": true, "ticker": "SPY", "is_etf": true, "holdings": [ { "etf_symbol": "SPY", "holding_symbol": "NVDA", "holding_name": "NVIDIA CORP", "weight_pct": 8.158692, "value_usd": 64204621094.9616, "as_of": "2026-09-22T00:00:00+00:00" }, { "etf_symbol": "SPY", "holding_symbol": "AAPL", "holding_name": "APPLE INC", "weight_pct": 7.440639, "value_usd": 58553921106.3972, "as_of": "2026-09-22T00:00:00+00:00" } ], "holdings_count": 25, "commodity_exposure": null, "available_signals": { "tengu_v3_intel_etf_holdings": "top constituents by weight (etf=SPY)", "tengu_v3_news_summary": "news summary for SPY", "tengu_v3_intel_options_volume": "unusual options flow for SPY" }}available_signals is a fixed list, the same for every ETF, with one extra tengu_ entry for a commodity-tracking ETF. It is not a check that data exists for the ticker. Its keys are legacy tool names and are not current MCP tool names; the full list also carries tengu_, tengu_v3_options_chain and tengu_. The current tools for the same data are intel_etf_holdings, news_summary, intel_off_exchange, intel_options_chain, intel_options_volume, intel_darkpool_ticker and intel_commodities. Do not call a tool by an available_signals key.