Sign up

Overview

Status

Changelog

Customer-visible changes to the Wealthnow REST API, MCP server and dashboard, newest first. Dates are UTC; changes before 2026-09-19 are listed by month. Within each date or month, breaking changes and deprecations come first.

Note:

Follow this page in a feed reader at https://app.wealthnow.io/docs/changelog.xml (Atom). Live health for each service is on the status page.

2026-10-18

Devin Desktop (formerly Windsurf) MCP config uses url

The Devin Desktop example in MCP and SDKs now sets the server with url, the field current Devin Desktop reads, instead of the older serverUrl. Nothing changed on the server.

Links to the dashboard shared in chat apps and social posts now preview with the square Wealthnow logo.

2026-10-17

Clearer sign-up, trial checkout and account pages

Signing up from a plan now names it ("Start your 7-day free Pro trial"), and the Plans page says when it is taking you to secure checkout, with a way to stay on Free. Checkout fills in your account email and says the trial starts with no charge today. The Plans page states each trial's credits, the yearly amount after an annual trial, and "Skip trial, subscribe now" for paying today. New keys show their created date and "Never" until first used, and Usage reads "1,000 of 1,000 credits left". No prices, limits or credit costs changed.

Billing and trial wording matches what happens

A failed payment reads "Payment needed" everywhere, and your API key keeps working on the Free plan until you update your card; the trial checkout states the first charge as 7 days after you start, a refused repeat trial says so with no charge, and usage figures during a trial follow the trial's credit limit.

2026-10-16

7-day free trial on Starter, Pro and Expert

Starter, Pro and Expert can start with a 7-day free trial at checkout: same plans, same prices, and the card is charged when the trial ends unless you cancel first in Billing. During the trial, usage stops at the plan's trial credits. If a payment fails, your API key keeps working on the Free plan, with the monthly Free credits, until you update your card.

The dashboard's credit balance is the one the API reports

"Credits remaining" on the dashboard now shows the same wallet balance as X-Credits-Remaining and MCP account_status, including overage headroom on paid plans; the included credits left before overage are shown beside it.

2026-10-14

Congressional trades and every FIRM analytic are back on the API; only the Copilot and the Brain agent are not sold

The 2026-10-13 change also took congressional trades, and the analytics that size or propose a position, off the plans and out of these docs. They are back, on the plans and at the prices they had before:

  • Congressional and politician trades (/api/v3/intel/congress, /politicians, /corporate_donors): Starter and up, 2 credits. See Congressional Trades.
  • Decisions and trade setups (/api/v3/decision/*), the signal and decision streams and Kelly sizing: Quant Signals, Starter and up.
  • Index hedge proposals: Risk Analytics, Pro and up.
  • Trading plans and portfolio-aware scores: Execution & Research, Pro and up.
  • The rest of what the 2026-10-13 entry lists: the congressional feature panel and /api/v2/congressional, portfolio allocations (HRP, risk parity, the RL allocator), pairs signals, briefings, agent outputs and account VaR, each on the plan it had before.

Starter has Congressional again and keeps Crypto Market Data. The endpoint reference lists 353 endpoints. Only the Copilot and the Brain agent API are not sold: a key that calls a Copilot route gets 403 not_offered, whatever its plan, and is not charged.

The seller line no longer says the data is not personalized to anyone's portfolio, because some sold routes, such as the portfolio-aware score and the historical VaR scenarios, compute on holdings you send.

2026-10-13

Breaking: the API sells data only

Market data and analytics for software developers. Not investment advice, and not personalized to anyone's portfolio.

These are no longer offered on the API. A call to one answers 403 not_offered, whatever your plan or add-ons, and is not charged:

  • Copilot (/api/v1/copilot/*, /api/v3/copilot/*) and the Copilot add-on.
  • The Brain agent API (brain.wealthnow.io/v1).
  • Congressional and politician trades (/api/v3/intel/congress, /politicians, /corporate_donors, /api/v2/congressional) and the feature panel built from them.
  • Decisions and trade setups (/api/v3/decision/*), trading plans, Kelly sizing, portfolio allocations (HRP, risk parity, the RL allocator), hedge proposals, pairs signals, the signal and decision streams, briefings, agent outputs and account VaR.

Starter adds Crypto Market Data in place of Congressional. Expert is now data at scale: every data product, 10,000,000 credits plus Private Markets Pro's 1,200,000-credit pack (11,200,000 in all), overage to a 50,000,000-credit ceiling, and 1,500 requests a minute.

Breaking: the tengu.co hosts are retired

Wealthnow no longer owns the tengu.co domain. Anything still pointed at firm.tengu.co, keys.tengu.co, app.tengu.co or a connector added as firm.tengu.co/mcp must move to the wealthnow.io hosts now: a request sent to a tengu.co host, and the key it carries, goes to whoever runs that domain.

  • REST API: https://firm.wealthnow.io. The published 0.1 SDKs default to firm.tengu.co, so set the host explicitly (see MCP and SDKs).
  • MCP server: https://mcp.wealthnow.io/mcp. Remove a connector added under the old host and add it again.
  • Dashboard: https://app.wealthnow.io.

Your keys, plan and credits are unchanged. If you think a key was sent to a tengu.co host after 2026-10-12, rotate it under Developers → API Keys (see Manage your keys).

2026-10-02

Breaking: a news filter a route does not take is refused, not dropped

A news route used to drop a filter it did not take and serve its unfiltered answer, so a filtered request could read news about something else. news/aggregated?ticker=AAPL returned the market-wide feed, and news/ticker_news?tickers=XOM&sector=Technology returned XOM's news unfiltered.

  • Every /api/v3/news/*, /api/v3/intel/news_*, /api/v3/news_crypto/* and /api/v3/news_forex/* route now answers 422 with error: "unsupported_filter", not billed, when it is sent a non-blank ticker, tickers, symbol, symbols, sector, sectors, industry, topic or topics it does not declare. On news_forex, pair, pairs, currency and currencies are also refused. detail names the parameter or route to use, unsupported lists the refused names and accepted the route's own parameters.
  • news/ticker_news no longer takes sector. The news source ignores it beside tickers. Filter by sector on news/category.
  • Any other unknown parameter is still ignored, and a blank value is not a filter.

See News & Sentiment.

News: news/aggregated takes one ticker, and news/alerts takes tickers alone

  • news/aggregated?ticker=AAPL reads the same trailing window from the source's ticker feed and keeps only the headlines tagged with that symbol. The answer echoes ticker (null market-wide), and source is news_ticker. An empty window is an empty list with a note and symbol_check. A symbol no listing knows is 404 unknown_ticker, not billed.
  • news/alerts?tickers=AAPL now means category=ticker and returns those symbols' alerts. It used to be a 503 upstream_unavailable. tickers with category=general is 422 unsupported_filter.
  • The docs now describe hours as it has worked since 2026-09-29. On news/aggregated and news/events, hours is a trailing window read newest first, at most 150 items per call. coverage says whether the read reached the window's start. The guide still described calendar buckets and one page of 50.

Unverified symbols: a current listing is never unverified

An empty answer about a symbol in the security master's current listings, such as AAPL over a quiet minute of news/latest, could carry ticker_verification: "unverified" and be refunded when the live symbol check did not finish in time. A current listing now counts as verified: such an answer is an ordinary empty answer, billed, without the field. An unknown symbol is unchanged. See Unverified symbols.

Tape: a day with more prints than limit says so, and pages exactly

GET /api/v3/tape/futures/{root} and GET /api/v3/tape/options/{ticker} used to return limit prints of a busy day with nothing to say the day held more.

  • Every answer with rows carries rows_total, truncated, window and next_cursor. rows_total is the day's exact print count for the request, truncated is true when the day holds more than was returned, and window names the prints a first page holds: most_recent on futures, largest_premium on options.
  • Pass next_cursor back as cursor to read the next page, until it is null. Pages never repeat or skip a print, identical prints included, so a finished day pages to exactly rows_total prints.
  • tape/futures also takes before=<ISO-8601 instant> as a coarse place to start.
  • A bad position is a 422, not billed: invalid_cursor, invalid_before, or cursor_and_before when both are sent.
  • tape/futures_curve adds rows_total and truncated.

Existing fields are unchanged. See Page a day that holds more prints than limit.

Fundamentals: a fiscal fourth quarter carries its own EPS and share count

A fiscal-Q4 row on quarterly income_statements was the year less the first three quarters, which is right for revenue and net income and wrong per share: it carried the year's weighted share count and an EPS that could be a cent or two from the company's release.

  • eps_basic, eps_diluted and shares_outstanding on a fiscal-Q4 row are now the quarter's own, from the filer's SEC filings: served as filed where the filer reports the quarter, otherwise derived from the year and the first three quarters, day-weighted. Apple's fiscal Q4 2025 reads 1.85 and 1.85 on 14,815,308,000 shares, where it read 1.86 and 1.84 on 14,948,500,000.
  • fiscal_q4_per_share says what was done: status (reported, derived, or a withheld state with its reason), the period, and cent_ambiguous for an EPS whose cent the filer's rounded share counts cannot decide.
  • The previous figures stay in vendor_eps_basic, vendor_eps_diluted and vendor_shares_outstanding.
  • TTM figures use the corrected quarter, including eps_ttm, pe_ttm and forward_pe on price_snapshot: Apple's eps_ttm is 8.72, was 8.71.

See Fundamentals.

Prices and quotes: the last trade, whole-share volumes, exact changes

  • price is the last eligible trade in any session, never an exchange's official-close message. After 20:00 ET, NYSE Arca ETFs such as SPY, IWM and GLD were served at their regular close, because the feed's last print was the exchange's closing message and not a trade. price_session, extended_hours and day_change now follow the real last trade. This applies to price_snapshot and to /api/market/quote/{ticker}.
  • Volumes are whole shares. day_volume and regular_session.volume on price_snapshot and volume on equity bars from fundamentals/prices are integers (40711869, not 40711869.0).
  • A finished session's regular_session.volume is the consolidated daily total, the same figure fundamentals/prices serves for that day. While the session forms it is the live running tally.
  • On /api/market/quote/{ticker}, change is the exact price difference (it could read 4.850000000000023) and change_pct has 6 significant figures.
  • On /api/market/quote/{ticker}, bid, ask and their sizes are null outside a session. They are served only in pre-market, regular or after-hours trading, and only when the quote was set during that session, as on price_snapshot. The route used to serve a stale bid and ask with the market closed.

See Read the latest price and its freshness.

Errors: route fields in the body, and an unverifiable symbol is not billed

  • An error body carries the fields its route names, after the envelope keys: available on an unknown dataset or index, known_roots on an unknown futures root, symbol and hint on the crypto 404s, reason and ticker on excluded_from_equity_ranking. The guides documented these fields, but several never reached the body. The envelope keys and X-Error-Code are unchanged. See Detail fields.
  • A symbol that cannot be verified is a 200 that is not billed. When a metered call's answer is empty and its symbol could not be checked in time, the answer carries ticker_verification: "unverified" and a note, with X-Credits-Cost: 0 and X-Credits-Settlement: refunded. It used to be a billed empty answer. A symbol known not to exist is still 404 unknown_ticker, not billed. See Unverified symbols.

MCP: account_status reads the balance live

account_status could return a cached balance, 30 seconds old or more, so it disagreed with the X-Credits-Remaining of the call just made. It now reads the key service on every call and returns as_of (when the balance was read) and stale_seconds (0 for a live reading). It still costs no credits.

The five newswire tools are in the spec and the MCP catalogue

intel_news_pro, intel_news_why_moving, intel_news_movers, intel_news_press_releases and intel_news_crypto (/api/v3/intel/news_*, News & Sentiment, Starter and up, 2 credits) are now listed in GET /api/openapi.json, which has 370 operations, and in the MCP catalogue. The routes already answered over REST. See Newswire stories.

2026-10-01

New API keys start with wn_; the variable is WEALTHNOW_API_KEY

  • Keys created from today start with wn_. Keys created before start with tengu_ and keep working on every surface. Nothing to change.
  • The guides, snippets and MCP configs name the environment variable WEALTHNOW_API_KEY. The SDKs still read TENGU_API_KEY when that is the one set.
  • A first key needs no plan first. On a workspace with no plan, Create key under Developers → API Keys starts the Free plan (1,000 credits a month) and shows the new key to copy. Signing up from the API pages starts it for you.

2026-09-30

The MCP tool list shows every tool a Free key can call; search_tools finds the rest

  • Without a catalog option, tools/list shows account_status, search_tools and the tools your key can call. A key that can call 40 tools or fewer, as on Free, sees all of them; it used to see the first 12. A larger plan sees about 100, never more than 120: every Free tool, a curated set of headline tools and your add-on tools, then your own plan's tools. See What the default list shows.
  • New: search_tools finds tools anywhere in the public catalogue by topic and gives each match's cheapest plan and credits per call. It runs no data tool and costs no credits. See Find a tool.

2026-09-29

Breaking: the dividend yield is the trailing 12 months, split-adjusted

/api/v3/fundamentals/price_snapshot (MCP manifest 2.154) served dividend_yield as the last fiscal year's dividends paid over that year's shares, divided by the live price. That lagged a raise by up to a year and ignored later splits. NVDA's dividend yield was the prior fiscal year's (0.017%); it is now the trailing 12 months (0.23%). BKNG read 23.8% because its pre-split dividend was divided by the post-split price; it now reads about 1.0%.

  • dividend_yield is regular cash dividends with an ex-date in the last 365 days, split-adjusted, over the live price, as a fraction. Specials are excluded. It is null, never 0, for a non-payer, when the dividend record cannot be read, or when the figure fails a plausibility check.
  • New: dividend_yield_indicated (the latest declared regular dividend times payments per year, over the price), the dividend block with its status (ok, non_payer, overdue, irregular, unavailable, currency_mismatch, split_adjustment_unresolved, implausible), and units for every valuation field.
  • Trailing EPS is on today's share basis. Quarters filed before a split are restated, so KLAC's pe_ttm is about 53, not 7.16, and BKNG's about 17, not 1.22. eps_ttm_basis says how, and eps_ttm is null when the basis cannot be established.
  • forward_pe is unchanged in kind (trailing P/E over one plus the latest fiscal year's growth, not an analyst consensus), but the growth is read as a percent, and it switches to net income growth when a split sits between the two years. forward_pe_basis and forward_pe_growth say so.
  • Every new dividend field is null when valuation_status is not ok.

See Dividend status.

Breaking: the day change runs from the previous close in every session

price_snapshot served NVDA at -0.39% before the open on a day it was up 1.68%: before the open, on weekends and on holidays, day_change was the previous session's move from its open to its close. It is now price - prev_close in every session, the exact difference, with the percent to 6 significant figures.

  • session now comes from the exchange clock: pre, regular, post or closed. off_hours is gone, and regular no longer appears at 22:00 ET. market_session carries the same value.
  • bid and ask are served only while the exchange is in a session, and only a quote set during that session (quote_as_of). The 20:00 quote is no longer served overnight.
  • New: price_session (the session of the last trade; the closing auction reads regular), prev_close_date, day_bar_date, regular_session (the latest regular session, dated, with its own change), extended_hours (the move since that close) and session_detail_status.

See Read the latest price and its freshness.

Breaking: research-grade price history is on one basis, with total returns

/api/v3/prices/history/{ticker}:

  • prc is the price as traded on every row. Live rows fetched after a later split carried that split's basis, and were then divided by cfacpr again: BKNG's 2025-01-02 close read 197.01. It now reads 4,925.29, and prc / cfacpr is 197.01. price_basis names the rows restored and how each row's basis was established.
  • ret is the total return on live rows, the price return plus the cash dividend on its ex-date over the prior close (KLAC 2026-08-17: 1.00% became 1.11%). It used to be the price return under a note that said it included dividends. retx is now served on live rows as the price return. returns_basis states both per segment; live ret is null when the dividend record could not be read.
  • Refusals, not billed: 503 price_series_discontinuous (a price step no recorded split explains; retryable: false), 503 split_events_unread (the split record could not be read and the series steps; Retry-After: 30) and 503 price_basis_unknown (a live row's basis cannot be placed). A step across a missing session is served and listed in price_basis.flagged_steps.
  • Also: archive rows carry source: "archive_daily"; a window with no rows warns no_rows:archive or no_rows:live; a live-tail answer that skipped the security lookup gives identity.reason: "security_id_lookup_skipped". The documented default start was wrong: with no start the window is the last 365 days, as start_defaulted and window_note say.

See Research-grade daily history.

Breaking: screener filters state their unit, and a fraction under a percent name is refused

/api/v3/fundamentals/screener read its filters in percent but said so nowhere. A screen for ROE above 0.2 and revenue growth above 0.2, meant as 20%, returned 137 rows; 91 of the first 100 failed the intended screen.

  • New percent parameters: roe_min_pct, roe_max_pct, roa_min_pct, net_margin_min_pct, revenue_growth_min_pct, eps_growth_min_pct and payout_ratio_max_pct (20 is 20%, 0.5 is 0.5%). The older names still work, in the same percent unit.
  • Refused, not billed (422): ambiguous_percent_threshold (an older name with a value strictly between -1 and 1, other than 0), conflicting_filter_values (an older and a _pct name that disagree), debt_to_equity_max_out_of_range (outside 0 to 20) and non_finite_threshold.
  • Negative equity never passes a debt-to-equity ceiling. ABBV (-20.49) and HCA (-7.71) passed "D/E below 0.5". Such rows now carry equity_negative: true and leave the screen.
  • Revenue growth beyond ±100% is checked against the SEC 10-K on one revenue concept. TRV's +1,123% compared net investment income with premiums; it is now +5.18%, and the growth floor is applied again to the checked figure. growth_status, revenue_growth_basis and vendor_revenue_growth_yoy say what was checked.
  • New: filters_applied (each filter with its unit and basis: the row's latest fiscal year, not TTM) and units.
  • Over MCP, an argument the screener does not declare is refused with the accepted list, instead of being ignored. Every tool's parameter descriptions, with units, now reach MCP inputSchema.

See Screener units and refusals.

Breaking: company snapshots state every block's period and unit

/api/v3/fundamentals/metrics_snapshot, company_full/{ticker} and full/{ticker} put a quarter's ratios as fractions (NVDA roe 0.2812), trailing ratios in percent (roe_ttm 84.23) and a percent payout_ratio (0.81 meaning 0.81%) side by side, unlabelled.

  • Every block carries period (quarter, fiscal_year, ttm, partial_ttm, point_in_time) and units, and the ratio blocks carry definitions. balance_sheet_mrq was labelled TTM; it now names its quarter.
  • A TTM is four consecutive quarters, or null with ttm_status. TRV's "TTM" skipped a quarter. A field some quarters leave empty is summed over the rest and named in ttm_underfilled_fields.
  • debt_to_assets is total debt over total assets. The old figure was total liabilities over assets and is now liabilities_to_assets (NVDA 0.285 against 0.1042). Debt to equity is null with debt_components_missing when the balance sheet lacks a debt component.
  • The cash flow reconciles. change_in_working_capital was only the receivables change, so the served parts summed to 85.2B against NVDA's 24.1B of operating cash flow. It is now null with a status, and change_in_receivables, other_operating_items and operating_cash_flow_components sum to operating cash flow, with cash_conversion_pct.
  • Dividends: dividends_paid_ttm was the fiscal year's total (NVDA 0.97B); it is now the trailing four quarters (6.73B), and the fiscal year's total is dividends_paid_fiscal_year. dividend_history rows add per_share_split_adjusted, so a split no longer reads as a cut; dividend_per_share_growth_yoy_pct and years_consecutive_per_share_growth measure per share. The same applies on /api/v3/fundamentals/dividends.
  • full serves top institutional holders again, from 13F filings with their report period and freshness. They were always an empty list. Insider trades and holders now need a plan with the funds product (Starter and up), as the standalone routes do; on Free they are null with status plan_required. period_pairs pairs statements of the same period.
  • Growth beyond ±100% is checked in the growth block too; see below.

See Periods and units in company snapshots and Dividends.

Breaking: SEC filing extracts serve the narrative sections

/api/v3/sec/filing/{ticker}/{filing_type} returned null text sections under a _status note. It now serves risk factors, MD&A, market risk, business and subsequent events, verbatim from the filing, for the sections named in the new sections parameter, paged with max_chars and offset. Each section is an object with a status, and text_sections._status is extracted, partial, not_extracted or available_on_request.

  • New: accession_index_url (the filing's own document index) and cash_flow_summary.period (a 10-Q's cash flows are year to date).
  • diluted_trend_4q holds one value per quarter end. NVDA's 2026-07-26 appeared twice, as the quarter's and the year-to-date diluted shares.

See Narrative sections.

Breaking: a stock or unlisted ticker on the crypto derivatives routes is a 4xx, not an outage

/api/v3/crypto/derivatives/funding, open_interest, basis and vol_surface used to answer 503 whenever no row could be served, so an agent read "NVDA" or a typo as "the exchanges are down" and retried.

  • A ticker the venues list as a security, not a coin (for example NVDA, which OKX, Gate and Coinbase International list as a stock perp): 422 with error: "not_a_crypto_asset". rows[].reason names each venue's declaration.
  • A symbol no venue lists (every first-party venue answered): 404 with error: "symbol_not_listed". On vol_surface, a currency Deribit lists no options on.
  • Both are standing answers: not billed, retryable: false, no Retry-After. They keep rows, coverage and sources.
  • A real venue failure is still a 503 with error_code: "crypto_derivatives_unavailable" (or crypto_vol_surface_unavailable). It now carries error: "upstream_unavailable", Retry-After, is_empty_result_set: false and Cache-Control: no-store, like every other refusal. sources names the venue that failed.
  • A request that mixes a coin with a stock (symbols=BTC,NVDA) is unchanged: a 200 whose NVDA row is marked refused.

XBRL facts are read from SEC, dated

/api/v3/fundamentals/companyfacts/{ticker} and xbrl/{ticker}/{concept} read a bulk copy that stopped refreshing in the spring of 2026. They now read SEC's live companyfacts document: source is sec_edgar_companyfacts_live (it was warehouse:edgar/companyfacts), or archive when SEC cannot be read. New fields: as_of (companyfacts) or data_as_of (xbrl), last_filed, financial_facts_last_filed, is_stale, freshness and, on xbrl, concept_last_filed, field_notes and each row's own period_fy, period_fp and period_months (fy and fp stay the filing's). is_stale is true when the financial facts lag the filings (TSM). SEC unreachable with no archived copy is 503 sec_edgar_unavailable, not billed. See XBRL freshness.

Growth, bars, peers and history say what they are

  • /api/v3/fundamentals/growth checks revenue growth beyond ±100% against the SEC annual report. On vendor_concept_mismatch the SEC figure and recomputed CAGRs are served (TRV: 3-year 157.83% became 9.8%), the margins computed on the wrong revenue are withheld into vendor_margins, and the source's figures stay in vendor_revenue_growth_yoy, vendor_revenue_growth_3y_cagr and vendor_revenue_growth_5y_cagr. sec_check_pending and unverified_outlier serve null. See The revenue growth check.
  • /api/v3/fundamentals/prices adds adjusted, adjustment_basis (splits, unverified or none) and adjustment_note: equity bars are split-adjusted, not dividend-adjusted, so KLAC's 2,108.06 close before its split reads 210.806. See Adjusted bars.
  • /api/v3/fundamentals/historical/{ticker} runs to next calendar year when end_year is omitted, so NVDA's fiscal 2027 quarters are included, and says so in end_year_default.
  • /api/v3/fundamentals/peers/{ticker} adds peers_status (ok, none_after_size_filter, none_after_data_error_filter, vendor_no_peers, vendor_unavailable) and documents min_relative_size. NVDA's peers were all dropped by the size floor with no reason given.
  • Crypto derivatives funding, open interest and basis add coverage.not_covered (the venues in no value, with a reason_code), coverage.headline_scope (verified_venues) and headline_scope_note, so no total reads as market-wide. The aggregator's reason is not_enabled, and the Binance and Bybit reason is reworded.

Served text says what the data is

Notes, hints and messages on about 40 routes described our own vendors, plans, scripts and jobs. They now say what the data is and what you can do; every code, status and flag beside them is unchanged. A few fields that only described our vendor arrangements are gone or empty: used, cap, limit, priority and scope on news budget refusals; vendor, monthly_limit, daily_limit, per_minute_limit and safe_threshold on monitoring/api_quotas; data_source_options and checked_alternatives on borrow_cost (now [], removed next release). intel/ml_drivers source is model_shap_attributions, and a 504 says "request exceeded the N s time limit; retry, or narrow the request".

Voter attribution: options flow is attributed; failures are refusals

GET /api/v3/intel/voter_attribution/{ticker} (MCP manifest 2.151):

  • voter=options_flow now attributes the voter from the ticker's options-flow alerts. Every call used to fail and answer a billed 200 with error: "uw_read_failed". Each alert takes its direction from option type and side of the book (a call bought or a put sold is bullish), and mixed alerts are skipped. Alerts are weighted by premium. The new fields alerts_read and window_truncated say how much was read. score_reconstruction is a reconstruction from those alerts, not the voter's own score.
  • When the options-flow feed cannot be read: 503, error_code: options_flow_unavailable, Retry-After: 30. It is not billed and never cached. On a server without the feed configured, Retry-After is 3600 and retryable is false.
  • voter=insider_flow:
    • A Form 4 read that fails answers 503 with archive_read_failed or archive_read_timeout.
    • An empty filings archive answers 503 dataset_unavailable.
    • A ticker with no filings answers 200 with n_events: 0 and score_reconstruction: null. It used to be a neutral 0.0.
  • Input checks: days_back outside 1-90 and an unknown voter answer 422.

Status routes and vendor errors carry codes, never text

  • /api/v3/stream/* status routes and the universe status report a daemon's last error as a code (flush_failed, subscribe_timed_out, ...). Any stored value that is not one of our codes is served as unclassified_error.
  • vendor_error, upstream_error and vendor_errors blocks carry the kind, status, codes and path of a failed vendor call, never the vendor's response body or the exception's text.

Every response refuses to be framed; sign-in pages are never cached

  • Every response carries Content-Security-Policy: frame-ancestors 'none', so no page can be embedded in another site's frame. It restricts framing only.
  • Every /oauth/ response carries Cache-Control: no-store unless the route sets its own policy. The consent page used to carry none.

The point-estimate disclosure is measured on the served book

The point_estimate block that describes predicted_return_pct now reports what the served book's column actually holds: blended_score * 100, model return estimate, mixed or none. It adds definition_basis (measured_on, at, n_rows). It says unmeasured until a book has been served since the server started. is_calibrated_forecast stays false, and the value stays withheld.

2026-09-28

Breaking: predicted returns are withheld on every route

predicted_return_pct is blended_score × 100, a rescaling of the ranking score, not a calibrated return forecast. Six routes already withheld it; every other route that serves the prediction book now withholds it too, with its band (interval_lo, interval_hi, interval_half_width), as null with withheld: "uncalibrated_point_estimate".

  • The routes: /api/v3/decision/universe_scan, /api/v3/signals/fusion, /api/v3/learning/prediction_tracker, and the v1 routes /api/predict/{ticker}, /api/predictions, /api/top-picks, /api/signals, /api/scan, /api/snapshot/{ticker}, /api/risk/{ticker} and /api/dashboard.
  • /api/risk/{ticker}: kelly_fraction is null with kelly_withheld. It used to be a fabricated 0.0.
  • Averages: /api/predictions/accuracy and /api/v2/models/calibration withhold the mean point estimate and the band's mean width. Hit rate, IC and coverage are still served.
  • /api/results: withholds its predicted column and says why in track_record_note.

Rank, decile, direction, conviction and the order of rows are unchanged, so rank on those. See Withheld magnitudes.

Breaking: the /api/users routes are removed

The undocumented /api/users routes (user records with a broker account, and pause or resume controls) now answer 404. They were never offered as a product, and they held no users.

New: private company news

GET /api/v3/private_markets/news (MCP tool private_markets_news, Pro, 5 credits) serves news about private companies: funding rounds, M&A, IPO filings, partnerships, launches, leadership, legal and layoffs. It gives one story per event, with its other outlets listed, each tagged with its companies. It filters by category, sector, company_id and q, sorts by latest or top, and pages with a cursor. A feed that cannot be read answers a 503 refusal, not billed, never an empty list. The MCP manifest is 2.150. See Private company news.

The crypto vol surface ends at the last completed hour

The venue stamps the hour in progress with its end time, so /api/v3/crypto/derivatives/vol_surface served a partial hour, stamped in the future, as realized_vol, and built vol_risk_premium on it. realized_vol now ends at the last completed hour. vol_risk_premium adds iv_as_of and rv_as_of, the time of each leg.

Error bodies name a code, never an internal error

  • No internal error detail in any body. No response carries an internal exception's class or text, which could name storage paths or queries.
  • An unexpected failure answers 500 with error: "internal_error" and detail: "request_id=...". Quote the request id when you contact support.
  • /api/health/private_markets reports detail: "probe_failed" or "probe_timed_out".
  • Security master unavailable: when the service that maps a symbol to its security cannot be reached, the routes that need it answer 503 identity_unavailable instead of a billed 200.
  • sentiment_history?entity= is served again during a security-master outage, stamped identity_verified: false.

Sign-in: a client metadata document must be served directly

For developers of MCP clients that sign in with a client ID metadata document. The document at your client_id URL must answer 200 directly. A redirect is refused, as the draft specification requires, and client_id must equal the URL exactly. Every client we know of already does this: Claude, Claude Code, Codex, Goose, Grok, VS Code, Zed and Copilot CLI.

2026-09-27

Breaking: company events return each key development once

/api/v3/events/{ticker} returned the vendor's version history as separate events: every version of a key development, and a row for each type and role. For Apple since 2025 that was 309 rows for 244 developments, and repeats pushed real events past the limit.

  • It now returns each development once, from the vendor's current version. New types and roles fields list every type it is filed under and every role the company plays.
  • limit and n count events.
  • With a type filter, the top-level type is one you asked for.
  • Developments the vendor has closed are left out. The new include_closed=true adds them, flagged vendor_status: "closed" with closed_at.
  • until now includes the whole day.

The MCP manifest is 2.149. See One event per key development.

Breaking: price refusals carry error: "upstream_unavailable"

When every price source fails, /api/v3/fundamentals/prices and /api/v3/fundamentals/price_snapshot still answer 503 with error_code: "price_feed_unavailable", but error is now "upstream_unavailable" (it was "price_feed_unavailable"), as for every other refusal where the data behind a route could not be read. The code is also sent on the X-Error-Code header, and the body adds is_empty_result_set: false and data_available: false. The prices refusal no longer carries items: [], which read as a symbol with no bars. A crypto snapshot older than 15 minutes is refused the same way, with error_code: "data_stale". Read the cause from error_code. See When no price can be retrieved.

The price snapshot refuses when its live quote fails

/api/v3/fundamentals/price_snapshot answered 200 with price: null when the live quote failed, because the failure was lost before the response was built. It now answers the 503 price_feed_unavailable refusal above. When a snapshot is rebuilt within 5 seconds of a good quote for the same ticker, it reuses that quote instead of fetching a new one; a failed quote is never reused.

Five MCP tools say they cannot answer today

The MCP manifest is 2.148. The descriptions of five tools now open with UNAVAILABLE, so an agent does not reach for them. The routes behave as before.

  • intel_ml_drivers: 503 upstream_dead_drivers_unbound.
  • market_events_catchup: no producer publishes market events.
  • intel_voter_ic_drift: its table has not been produced, so it answers with no voter rows.
  • tape_options and tape_futures: capture stopped after 2026-07-29 and 2026-07-30; earlier days are still served.

2026-09-26

Breaking: crypto funding is weighted by open interest, from first-party venues only

/api/v3/crypto/derivatives/funding, /open_interest and /basis read first-party venues only: OKX, Deribit, Kraken, dYdX, Bitget, Gate and Coinbase International, each marked tier: "verified" in sources. One former source was retired and is no longer read. funding_rate_8h keeps its name and unit but now means the open-interest-weighted rate across verified venues, or their median when a verified venue has no open interest; it was the plain mean of the served venues, which is still served as mean_rate_8h_verified. A new funding_rate_basis field names the method ("oi_weighted_verified"). Binance and Bybit, which answered ok through a relayed feed, now report status: "geo_blocked": their APIs are blocked from the API's region and are not reached another way. Deribit reports not_applicable instead of missing when a request names neither BTC nor ETH. The three routes take a new optional detail (auto, full or summary, default auto) that keeps responses under about 15,000 characters; existing calls are unchanged. The MCP manifest is 2.147. See Derivatives, events and on-chain.

Breaking: earnings press releases are retired

/api/v3/fundamentals/earnings_press_releases answers 410 with error_code: "press_release_text_unavailable", not billed, and its MCP tool is gone (the full catalogue is 276 tools). The upstream source of the release text no longer exists and nothing else holds it; the route had been answering with an earnings calendar instead. For report dates and estimates use /api/v3/earnings/next/{ticker}; the filed release is usually exhibit 99.1 of the Item 2.02 8-K (/api/v3/sec/filing/{ticker}/8-K).

Breaking: corporate actions come from the current archive, as known on a date

/api/v3/prices/corporate_actions/{ticker} read a distributions archive frozen at 2024-12-31. It now reads the current archive, which records every version of each event, and serves each event in its newest version known on the new optional as_of. The response adds as_of, current_through with current_through_basis, clock, coverage and a versions block; rows add the version fields and distype, and distcd is now null. An unreadable archive is a 503 warehouse_read_failed, not billed, never the old archive.

Breaking: news topics are the news source's own categories

topic on /api/v3/news/by_topic and /api/v3/news/category, and topic, topic_or and topic_exclude on /api/v3/news/ticker_news, now accept only the news source's categories: earnings, dividend, layoffs, lawsuit, mergers (M&A, also acquisition or m&a, sent as manda), pricemovement, tanalysis, product, ceo, pressrelease, podcast, oil and futures, and the access tags paywall and paylimitwall. Anything else returns 422 with error: "invalid_topic", naming the supported topics, and is not billed. ipo, fda, analysts, pricetarget, guidance, stock_buyback, stocksplit, partnership, spinoff, bankruptcy, insider, esg and crypto were documented, but the source has no such category and always answered an empty list, which read as "no news"; the 422 now says there is no such category. mergers and acquisition, which answered empty too, now reach the M&A category. by_topic adds source_topic, the category it sent. See News topics.

Breaking: the analyst ratings calendar answers from a live feed

/api/v3/intel/calendar/ratings served an archive whose newest action was 2026-06-26, because its own feed was retired. It now answers from the live analyst-ratings feed behind /api/v3/news/ratings, with source: "vendor:news-ratings-feed", and falls back to the archive only when that feed fails, in which case a 503 advises a retry. Live rows carry analyst_name, company and url as null and add time_zone. action_company is Upgrades, Downgrades, Initiates, Reiterates or Resumes. action_pt is derived from the prior and current targets (Raises, Lowers, Maintains, Announces), and the Maintains filter also matches a reiterated rating. The response adds scan (rows_read, pages, complete, filtered_after_read): an action matched over only the newest rows read says result: "not_found_in_rows_read" or "partial_scan" instead of reading as an empty answer. See The analyst ratings calendar.

Breaking: funds and ADRs leave the equity rankings

The prediction book carries an equity-model score for several thousand funds and ADRs, which the equity model cannot rank: in the 2026-09-14 book SPY ranked 98th and AGG sat in decile 10. A fund or ADR (by security class in the security master) whose row carries an equity-model score is now removed before any ranking or per-ticker prediction, and so is a row the pipeline's quarantine list says to refuse. rank and decile keep the book's own values; nothing is re-ranked. Ranking routes add a book_guard block that counts what was removed, with non_equity_constant_ml_score for funds and ADRs in excluded_by_reason: /api/ml/top-picks, /api/v3/decision/trade_setups, /api/v3/decision/universe_scan, /api/v3/decision/ultimate, /api/v3/signals/fusion, /api/v3/learning/prediction_tracker when it answers from the book, and /api/v2/predict/multi_horizon. For a fund or ADR asked for by name:

  • /api/ml/predict/{ticker} answers 404 with error: "excluded_from_equity_ranking".
  • /api/v2/predict/{ticker}/intervals, /api/v3/decision/ultimate?ticker= and /api/v3/validation/{ticker}/kelly_uncertainty answer 200 with the value null and an excluded block.
  • /api/v3/intel/ml_prediction/{ticker} answers available: false with excluded, which reaches you as 503 paid_data_unavailable, not billed.
  • /api/v2/predict/multi_horizon lists it in missing_tickers and in the new excluded_tickers.
  • /api/snapshot/{ticker} serves prediction: null with prediction_excluded.

When the guard cannot load its sources, these routes answer 503 with error: "upstream_unavailable", error_code: "book_guard_unavailable" and Retry-After: 60, not billed, and the batch route answers 503 snapshot_cold or snapshot_unusable. See Funds and ADRs.

Breaking: the prediction book is dated, and a stale answer says so

The prediction book has not been regenerated since 2026-09-14, and the routes that serve it answered as if it were current. They now carry data_freshness, graded on the served rows when you call: data_as_of, data_age_hours, data_age_days, sessions_behind, max_sessions_behind (2), is_stale and stale_reason. It is on /api/v3/intel/ml_prediction, /api/ml/predict, /api/ml/top-picks, /api/v2/predict/{ticker}/intervals, /api/v3/decision/ultimate, trade_setups and universe_scan, /api/v3/signals/fusion, /api/v3/learning/prediction_tracker when it answers from the book, and /api/v1/copilot/score, /top-picks, /universe and /macro-regime. While the book stays frozen, every one of them answers is_stale: true. snapshot_stale on /api/v1/copilot/top-picks, /api/v1/copilot/universe and /api/v3/decision/universe_scan is now also true when the book is more than 2 sessions old; it used to mean only that the server's copy had failed to refresh for 4 hours, so the ten-day-old book read false. A stale Copilot answer's narrative starts with STALE BOOK:, and the top picks say "picks from the 2026-09-14 book" instead of "picks today". as_of_ts on the Copilot top picks, universe and macro regime is the book's newest time, null when it is unknown. data_staleness_days on a served prediction row is computed from the row's as_of_ts when you call; it was a stored 0.0 on every row. See How old the prediction book is.

Breaking: ml/top-picks and prediction intervals withhold the return estimate

/api/ml/top-picks served each pick's predicted_return_pct (the blended score × 100) and the band around it. They are now null, with withheld: "uncalibrated_point_estimate" on each pick and one magnitudes_withheld block on the response. /api/v2/predict/{ticker}/intervals withholds interval.lo, interval.hi and interval.half_width the same way, with interval.withheld and magnitudes_withheld, because the band is built around that number. Rank, decile, blended score and conviction are unchanged. /api/ml/top-picks also adds data_freshness and book_guard, and its as_of_ts is the newest time among the served rows. See Quant Signals.

Breaking: tape bars take one row per minute, and websocket minutes have no trade count

/api/v3/tape/bars/{ticker} kept the last-written row for each minute, which was the REST fallback's on every minute the websocket also wrote. It now serves one row per minute: the websocket's final bar, and the fallback's only for a minute the websocket never wrote. n_transactions is null on websocket minutes, whose stored number was an average trade size, not a count, and a 5m, 15m or 1h bar's count is null unless every minute in it has one. /api/v3/tape/index_bars keeps summing n_ticks. See One bar per minute.

The price snapshot answers without waiting on fundamentals

/api/v3/fundamentals/price_snapshot fetched its valuation inputs (trailing EPS, diluted shares, book value per share, growth and dividends per share) again with every price, which made a ticker's call take 1.5 to 8 seconds, longest when several tickers were asked for at once. The inputs are now kept for 6 hours and applied to the live price, so pe_ttm, forward_pe, price_to_book, dividend_yield and market_cap still move with the price; the price itself is at most 30 seconds old, as before. The snapshot adds valuation_as_of, when the inputs were fetched. A ticker's first call in 6 hours still waits for them.

The price snapshot says whether its valuation fields could be computed

/api/v3/fundamentals/price_snapshot adds valuation_status to every equity snapshot: ok when the valuation inputs were fetched, unavailable when the fetch failed or the provider has no inputs for the security (a fund, for example). With unavailable, every valuation field and valuation_as_of is null, which says nothing about the company's earnings; before, the same nulls were indistinguishable from a company with no positive earnings. The valuation keys are always present, as null when there is no value.

Crypto news is unavailable until 2026-09-30 00:51 UTC

The crypto news provider's monthly call quota ran out on 2026-09-25 at about 15:00 UTC, and it resets on 2026-09-30 at 00:51 UTC. Until then every /api/v3/news_crypto/* route answers 503 with error_code: "upstream_quota_exhausted" and the reset time, not billed. Wealthnow's own daily limit on these calls restarted from zero on each deploy, so it did not hold the monthly quota; it now holds across deploys.

Book routes answer a ticker's first call without the wait

/api/v3/intel/ml_prediction, /intel/voter_coverage, /signals/mtf, both kelly_uncertainty routes, /api/v2/predict/{ticker}/intervals, /api/v3/decision/ultimate, /api/predict, /api/snapshot and /api/risk read one shared, pre-loaded copy of the prediction book instead of reading it per ticker, so a ticker's first call no longer takes 9 to 22 seconds. Every one of them now answers from the same snapshot: when the newest snapshot is degraded they fall back to the last healthy one (up to 4 days back), and a ticker missing from the newest snapshot is reported as missing rather than answered from an older one (ml_prediction, voter_coverage and signals/mtf used to differ). While the shared copy loads after a restart, these routes answer 503 with error_code: "snapshot_cold" and Retry-After: 5, not billed.

Corporate actions the archive holds are served

/api/v3/prices/corporate_actions/{ticker} counts a version as known from the earlier of its vendor date and first_held_at, the new row field for when Wealthnow first stored it. A recent action the archive already holds (for example a dividend whose vendor date is weeks away) is no longer withheld.

Credit-default-swap history keeps its latest quotes and wide windows

/api/v3/credit/cds/{ticker}/history and the CDS block of /api/v3/credit/{ticker} read less of the archive and answer faster. Explicit start/end windows of any width are served, and a name that stopped quoting still returns its newest quotes (window.widened_to_whole_archive).

Price history names missing adjustment factors

/api/v3/prices/history/{ticker} rows before 2000 carry null cfacpr and cfacshr. The response now says so in an adjustment block, and its note no longer suggests adjusting with them.

Latest news serves the window you ask for

/api/v3/news/latest accepted minutes up to 1440 but read only the last hour above 60, so a day's window could answer empty in the evening. It now reads the requested window, and an empty one carries a note.

Crypto bars mark the bar still forming

With asset_class=crypto, /api/v3/fundamentals/prices now marks today's UTC day bar and the open week's bar partial: true, and adds as_of, as_of_basis, last_bar_partial, data_through and session_complete, as equity bars do. It used to serve those bars as if final. See Bars still forming.

FX news takes EURUSD

The pair and pairs parameters of the /api/v3/news_forex/* routes now also accept EURUSD and C:EURUSD, beside EUR-USD, EUR/USD and eur_usd, and send each as EUR-USD. EURUSD used to reach the feed unchanged: latest answered 503, and sentiment_stats and summary came back empty. See FX news, sentiment and prices.

The last report date follows the options-flow calendar

On /api/v3/earnings/next/{ticker}, most_recent_reported_date came from dates found in web search, so an old date could stand as the latest report: NVDA read 2025-05-28. When the options-flow earnings calendar, which also dates earnings_history, holds a newer reported quarter, its date now wins, with most_recent_reported_source: "options_flow_calendar", and the superseded date and its source are kept in most_recent_reported_alternatives. NVDA now reads 2026-08-26. See Macro, Rates & FX.

Quota refusals say when the quota resets

When a news feed refuses for quota on the /api/v3/news/*, /api/v3/news_forex/* or /api/v3/news_crypto/* routes and states when its quota resets, upstream_error adds quota_resets_at (UTC) and quota_resets_at_basis, and the body adds retry_after_basis: "firm_recheck". Retry-After is when Wealthnow next checks the feed, within the hour, not when the quota returns: on 2026-09-25 the crypto news refusal said to retry in 40 minutes for a quota that resets at 2026-09-30T00:51:46Z. See News & Sentiment.

On-chain flows answer

/api/v3/crypto/onchain/flows answered 503 with error_code: "crypto_onchain_unavailable" on every call, because the server could not compute the event hashes it filters the chain logs by. It now computes them itself and serves the stablecoin flows and Aave liquidations. See Crypto.

Empty answers say why

  • /api/v3/intel/corporate_donors/{ticker} adds a note when the source answered and holds no records for the company: many companies have no corporate PAC.
  • /api/v3/intel/short_interest/{ticker} answers with every field, null, when it has no record. It used to omit avg_daily_volume_at_settlement and prior_settlement_date exactly then.

Outside the public API, /api/status and /api/ready report a stale book. While the served book is more than 2 sessions old, /api/status answers status: "red" with model_ready: false, model_ready_reason: "predictions_stale" and pipeline_status: "stale", and /api/ready answers ready: false with reason: "predictions_stale". Both add prediction_freshness and still answer HTTP 200. /api/v1/copilot/portfolio-impact answers 404 excluded_from_equity_ranking for a fund or ADR candidate, and for a book whose holdings are all funds or ADRs; a book with no covered holding keeps its sentence, "none of the held tickers are in the prediction universe", now naming any excluded holdings. /api/v1/copilot/portfolio and /api/v1/copilot/top-picks-portfolio-aware leave funds and ADRs out of holdings and candidates and add data_freshness, book_guard and the STALE BOOK lead. /api/signals/history, /api/research/screen and /api/briefing in the firm shape withhold predicted_return_pct and its band, with magnitudes_withheld. /api/v3/signals/veto_state applies the same guard, so its vetoes exclude funds and ADRs and it can answer 503 book_guard_unavailable, and it adds data_freshness and book_guard. /api/v2/predict/{ticker}/multi_horizon and /api/v3/signals/mtf/{ticker} answer a fund or ADR with an excluded block, and /api/v2/predict/{ticker}/multi_horizon and /api/v2/briefing/overnight name predicted_return_pct in point_estimate.field.

2026-09-25

Breaking: the screener floors, types and sorts on Wealthnow's own data

/api/v3/fundamentals/screener used to pass the fundamentals source's screen through: it served nano caps and shells first, ignored sort_by=market_cap, and ranked revenue in yen and won as if it were dollars. It now sizes every matched row from exchange prices and shares outstanding and applies the floors itself. With no new parameters it returns only common stock with at least a $300M market cap and $1M a day of dollar volume, sorted by market_cap_usd, so the same query returns different rows. New parameters: min_market_cap_usd (default 300000000, 0 turns it off), min_adv_usd (default 1000000) and security_class (comma list of common, adr, preferred, fund, unit, warrant, other; default common). sort_by is validated: an unknown key returns 400 invalid_sort_by and an unknown class 400 invalid_security_class, each listing the allowed values. Rows add listing_ticker, security_class, exchange, price_usd, adv_21d_usd, market_cap_usd, reporting_currency, currency_status, revenue_usd, net_income_usd, fx and fundamentals_stale; the response adds floors, total_matched, total_pages, universe, field_sources and as_of. market_cap_usd is null when the close has moved more than 3x from the close on the share count's date, and liquid listings the floor dropped for lack of a verifiable size are named in universe.liquid_without_market_cap. While its size data loads the route answers 503 build_timeout (Retry-After: 10); when its data cannot be read, 503 upstream_unavailable with screener_size_data_unavailable or screener_vendor_unavailable. None is billed. See How the screener sizes and sorts.

Breaking: a news or alternative-data feed that failed returns 503, not an empty list

When the feed behind a route fails and returns nothing, the route now answers 503 with error: "upstream_unavailable", not billed and with no result field, where it used to answer a billed 200 with an empty list and a vendor_error. A feed that answered with nothing is still a 200, and a partial failure keeps its rows. This applies to the /api/v3/news/* newswire routes, every /api/v3/news_forex/* route and the /api/v3/news_crypto/* routes, with error_code upstream_<kind> (for example upstream_quota_exhausted, or upstream_not_configured when no feed is configured, which used to be a 503 with ok: true); and to intel/congress, intel/politicians, intel/corporate_donors, intel/insiders, intel/lobbying, intel/gov_contracts, intel/gov_contracts_live, intel/patents, intel/exec_compensation, intel/wsb, intel/off_exchange and intel/etf_holdings, with upstream_dead_no_archive, archive_read_timeout, archive_read_failed or build_busy. intel/twitter, whose upstream dataset returns no rows for any ticker, answers 503 upstream_dataset_empty (Retry-After: 86400) instead of an empty list with a note. On news_forex/by_category, a section other than general or alltickers returns 422. See News & Sentiment, Macro, Rates & FX and Alt-Data.

Breaking: per-ticker alternative data comes from a dated warehouse copy

intel/lobbying, intel/gov_contracts, intel/patents, intel/off_exchange and intel/congress with provider=alternative_data and a ticker now read a warehouse copy of the feed first, and the live feed only for a ticker the copy does not hold. The copy's history starts where it began: lobbying filings from 2025-01-19, congressional trades from 2025-05-20 (earlier for some tickers), off-exchange sessions from 2026-05-08, contract quarters from fiscal 2020, and a recent window per ticker for patents. Answers say so in data_from, history_floor, coverage_note, as_of, archive_ingested_at and data_through, with source: "warehouse:<product>-archive". Lobbying rows add registrant; contract rows are quarterly totals on the US federal fiscal year, with fiscal_basis, period_start and period_end; off-exchange rows from the copy have total_volume: null. See Alt-Data, Congressional and Options Flow.

Breaking: /api/ml/predict withholds the return estimate

/api/ml/predict/{ticker} served the stored prediction row as it was, with predicted_return_pct (the blended score × 100) and the interval built around it. predicted_return_pct, interval_lo, interval_hi and interval_half_width are now null, with withheld: "uncalibrated_point_estimate", point_estimate and point_estimate_supported: false, as on ml_prediction. The score, conviction, decile, rank and voter sub-scores are unchanged. See Quant Signals.

Breaking: price bars run through today, and the forming bar is marked

With no end_date, /api/v3/fundamentals/prices used to end an equity window on the previous UTC day. It now ends with today's session, so the newest bar can still be forming; such a bar carries partial: true. The response adds as_of, data_through, as_of_basis, last_bar_partial and session_complete. C: forex and X: crypto tickers on this path are graded on the UTC day. A window with no bars yet is a 200 with items: []; end_date before start_date returns 422 invalid_date_range, and a date that does not exist 422 invalid_date. See Bars still forming.

Breaking: earnings history is dated by the announcement and measures the reaction

On /api/v3/intel/earnings_history/{ticker}, report_date is now the day results were announced, and null when no date is known. It used to be the end of the calendar quarter, sometimes a day that had not happened yet, which is now calendar_quarter_end. day_move_pct and day_range_pct are measured on the session that traded on the release (the next session after an after-close report), named in day_move_date with day_move_basis; they used to be measured on the quarter-end date. New fields: reaction_move_pct, avg_abs_reaction_move_pct, max_abs_reaction_move_pct, fiscal_year, fiscal_quarter, fiscal_quarter_end, report_time, report_date_source and eps_basis (adjusted) with eps_basis_note, and warning: "report_dates_unavailable" when the date source failed. See Earnings reaction moves.

Breaking: the analyst price-target mean is the source's own

On /api/v3/intel/analyst_consensus/{ticker}, when the band comes from a source's summary, price_target_avg was the mean of that source's mean, high and low, and price_target_count (3) read like a number of analysts. price_target_avg is now the source's own mean. price_target_count keeps its value and price_target_count_basis says what it counts (rating_actions, news_rating_actions or summary_fields); price_target_analysts is the number of analysts behind the targets when the source says. See News & Sentiment.

Breaking: skill counts from a failed feed are null

In ta_master and apex_equity, a count or total from a feed that was not fetched or whose call failed is now null, not 0, with the reason in vendor_errors: the components counts, and apex_equity's intel fields such as congress_30d and ttm_lobbying_usd. A cached ta_master or trading_plan answer is no longer shared between keys with and without the Congressional product, so congressional rows reach only plans that include it. See Execution & Research.

Breaking: warehouse reads that fail return 503

The private-markets relations routes answered a relation read that failed or timed out as a billed 200 with no rows. They now answer 503 with error: "upstream_unavailable" and error_code: "warehouse_read_failed", not billed and never cached, with a Retry-After of 30 to 60 seconds; a genuinely empty relation is still a 200. /api/v3/news/sentiment_history/{ticker} refuses a failed read the same way. The board relation now leads with the current board, then the current team, then former members, each row with role_kind, and investor and service-provider deals and fund returns come newest first. See Private Markets.

Breaking: the market-event catch-up refuses instead of answering empty

/api/v3/events answered 200 with no events when the event store could not be read. It now answers 503 with error_code: "redis_unavailable" (Retry-After: 5), and a read from the start of a stream that holds no events at all answers 503 with error_code: "dataset_unavailable"; neither is billed. A malformed since returns 422; since takes <ms>, <ms>-<seq> or -. The response adds feed.last_event_age_s. See Catch-up refusals.

Breaking: Kelly sizes are in decimals, and withheld without a calibrated forecast

/api/v3/validation/{ticker}/kelly_uncertainty divided a percent return by a percent variance, so every fraction it served was 100 times too small. It now computes in decimals, (predicted_return_pct / 100) / ((interval_half_width / 1.645) / 100)^2 × conviction, capped at ±0.25. The size is withheld, with kelly_fraction and inputs both null, when the expected return is not a calibrated forecast (magnitude_unsupported, explained in magnitudes_withheld) or when the book is more than two US equity sessions old (book_stale); withheld_reasons lists each. The expected return is the blended score × 100 for every ticker today, so no size is served: on 2026-09-25 AAPL answered withheld_reasons: ["book_stale", "magnitude_unsupported"] with book_age_sessions: 9 and is_stale: true. A missing interval or conviction is no longer replaced by 0.02 or 0.5; the answer is null with warning no_interval_half_width or no_conviction. New fields: as_of_ts, book_age_sessions, is_stale, stale_after_sessions, withheld_reasons and magnitudes_withheld, and on a served size kelly_uncapped, cap_binding and the inputs keys mu_pct, sigma_pct, mu_decimal, sigma_decimal, interval_half_width_pct and units. The alias /api/v2/risk/{ticker}/kelly_uncertainty, outside the public API, computes and withholds the same way. See Kelly sizing.

Breaking: SHAP drivers are served only for the model serving now

/api/v3/intel/ml_drivers/{ticker} serves drivers only when every row of the drivers file names the model bundle serving now. Otherwise it answers 503 with error: "upstream_unavailable" and error_code: "upstream_dead_drivers_unbound", not billed, with binding.status (unbound, mixed_bundles, foreign_bundle or active_bundle_unknown) and Retry-After: 3600 (300 for active_bundle_unknown). The published file records no model bundle, so on 2026-09-25 every ticker is refused with unbound; the route used to serve drivers from that file. A failed read of the file is refused with archive_read_timeout, archive_read_failed or upstream_dead_no_archive. None of these refusals means the ticker is outside the model's universe. A served answer adds model_bundle_sha256, as_of, stale and report_freshness. In ticker_transparency and ticker_full, the ml_drivers layer is now missing with the reason http_503. See Example 2.

Breaking: report snapshots are aged by their own timestamp

Routes that serve a model-health or research report snapshot now grade it by the timestamp inside it, never by when the file was written or copied. /api/v2/models/drift and /api/v2/features/decay, when served from the voter-drift snapshot, add stale and report_freshness (80-hour limit), and each voter row's stale in drift is now also true when the snapshot is stale, with the producer's own flag in stale_at_build. /api/v2/features/importance and /api/v3/intel/factor_importance, when served from the factor-loading and voter-weight reports, add stale, report_freshness and a note that the rows are factor betas and voter weights, not feature importances. In /api/v1/copilot/track-record, each report_freshness entry adds as_of, as_of_field, provenance and max_age_hours; a report with no generation time of its own is stale: true with provenance: "unknown_generation_time" and report_age_hours: null. It used to be aged by the time the file was uploaded, or answer stale: null when that was unknown too. Outside the public API, /api/v1/copilot/voter-ic-drift adds stale and report_freshness on an 80-hour limit and re-grades voters[].stale the same way; on 2026-09-25 its snapshot was dated 2026-08-28, 659 hours old. See Example 5.

Breaking: the DSR trial registry is loaded, and an empty one refuses

Outside the public API, /api/v3/validation/trials, /api/v3/validation/trial_count, /api/v3/validation/deflate, /api/research/strategies, /api/swarm/strategies, /api/v2/research/alpha_discoveries and /api/v2/research/strategy_evolution serve the research loop's registered trials, its published trial corpus merged with an archive of its earlier cycles and each trial counted once, with a registry block (count_is_lower_bound: true): 189 trials on 2026-09-25, 80 of them in oos_backtest_top_decile. An empty or unreadable registry answers 503 with error_code: "dataset_unavailable", not billed: reason is source_unreadable, or table_empty when the corpus loaded and holds no trials, with Retry-After: 300. trials and trial_count used to answer a billed count of 0. deflate adds n_trials_is_lower_bound, answers 409 insufficient_trials for a family with no registered trials instead of deflating against a single trial, and needs n_trials_override to be at least 1.

Breaking: trade setups count only LONG and SHORT toward the skew

/api/v3/decision/trade_setups no longer counts a NEUTRAL setup (blended_score between -0.15 and 0.15) as long or short by the sign of its score. universe_skew is computed over LONG and SHORT setups only, so a screen of neutral setups is mixed, with no regime_warranted_alternative. The response adds the book's age: as_of_ts, book_age_sessions, is_stale (more than two sessions old) and stale_after_sessions. See Quant Signals.

Breaking: the credit snapshot rates the issuer's outstanding issues

rating on /api/v3/credit/{ticker} was the newest rating action on any of the issuer's instruments, often the withdrawal (NR) of a note that had just matured. It is now the latest long-term rating on the issuer's outstanding issues, leaving out withdrawn and matured ones, and the most common one when they differ. rating_basis says how it was derived (grain: "issue", status, n_live_issues, issue_ratings, dispersed, complete), as_of and outlook follow the rating served, and newest_action_date keeps the date of the newest action of any kind. rating is null when no outstanding issue has a long-term rating. See Example 1.

Breaking: cross-asset signals withhold values the source cannot have meant

/api/v3/signals/cross_asset serves null for a futures level that is missing, holds the source's 0.0 marker for a missing input, or falls outside a wide plausibility range, and for every field derived from it, regime_score included. Each bucket's invalid_fields names the withheld fields and why. dx_front held EUR per USD from the euro future, not the Dollar Index: it is now always null, the value is served as usd_proxy_eur_per_usd, and dxy_momentum_basis says what dxy_momentum measures. dx_curve_slope is always null. See Withheld values in cross_asset.

Breaking: multi-timeframe signals withhold the return estimate

Outside the public API, /api/v3/signals/mtf/{ticker} now serves predicted_return_pct, interval_lo, interval_hi and interval_half_width as null, with withheld: "uncalibrated_point_estimate", as ml_prediction does. blended_score, conviction, decile and rank are still served: on 2026-09-25 AAPL showed blended_score 0.0272 and decile 9.

Batch predictions for up to 200 tickers

GET /api/v2/predict/multi_horizon (MCP multi_horizon_batch, Quant Signals, Starter and up) is now in the public API. tickers is required (1 to 200 symbols) and horizons defaults to 1m,3m,1y. Only the 1m training horizon carries a leg, with direction and percentile; other horizons are null, and predicted_return_pct is withheld while point_estimate_supported is false. Every ticker is in rows or missing_tickers. A server that has not loaded the prediction book answers 503 snapshot_cold with retry_after_s: 5. The public API now has 286 operations, and the full MCP catalogue lists 277 tools: the 276 operations that are not streams, plus account_status. See Batch predictions.

The regime forecast says which day its regime describes

/api/v2/regime/forecast adds current_regime_as_of, current_regime_source, as_of, book_age_sessions, stale_after_sessions and stale: the forecast starts from the regime in the prediction book, and stale is true when that book is more than 2 sessions old. A cold call used to time out with a 504; a server still loading the book now answers 503 dataset_unavailable with Retry-After: 5, not billed. See Example 6.

Price history answers cold tickers

/api/v3/prices/history/{ticker} no longer times out on a ticker that is not cached. When its 2025-to-date bars cannot be read in time, the archive rows are served with live_segment: "unavailable" and a warning, or, when nothing else covers the window, it answers 503 dataset_unavailable, not billed. A split the source lists twice is applied once, which corrects cfacpr and cfacshr for such tickers. See Research-grade daily history.

One next-report date, and EPS labelled by basis

/api/v3/earnings/next/{ticker} and /api/v3/intel/street_estimates/{ticker} both add next_report_resolved, one next-report date resolved from the same inputs, with status, confirmed_by, source, alternatives and inputs. street_estimates labels its actual EPS eps_basis: "unknown" with eps_basis_note, and returns 400 invalid_ticker for a ticker that is not a plain symbol. street_estimates_history and street_estimates_guidance return the newest limit rows in the window, oldest first, add data_through, is_stale, max_lag_days and window_after_coverage, and report a failed read as error: "warehouse_read_failed". On income_statements, an EPS whose sign contradicts its row's net income is replaced from a second source or withheld, and the row carries eps_repair. See One next-report date and Fundamentals.

Smaller fixes

  • /api/v3/fundamentals/company_facts refuses a symbol that cannot exist with 422 invalid_ticker before calling any source.
  • /api/v3/credit/loans/{ticker} and the snapshot's loans block add n_active_facilities, active_facility_amt_by_currency and total_facility_amt_note: total_facility_amt counts every facility ever, at face, not outstanding debt.
  • /api/v3/intel/iv_analytics/{ticker} serves expiry on skew rows, compares the term structure's expiries nearest 30 and 90 days (0-DTE excluded) and names them in shape_tenors, and adds n_expiries.
  • options_flow_history and darkpool_history summaries add scope, n_rows, complete and scope_note; peak_snapshot_premium_usd is null, not 0, when no snapshot carried a premium.
  • /api/v3/origin/short_activity/{ticker} labels its daily volumes as FINRA-reported off-exchange volume (volume_scope, volume_note), not the stock's total volume.
  • /api/v3/origin/ipo_pipeline adds expected_price_date and share_price (the filed range on an upcoming deal).
  • accounting_flags adds first_event_date, last_event_date and n_events_last_5y to the restatement and auditor flags.
  • /api/insider/clusters adds n_purchases, n_purchases_priced and total_shares, counts a re-carried or restated purchase once, and serves total_value_usd only when every purchase is priced.
  • The ready frame of every SSE stream adds producers and producer_live, which say whether anything is publishing; ready: true describes the transport only. /api/v3/stream/events reads only the calling key's namespace and refuses a request with no key with 403.

Outside the public API, on the deprecated /api/v3/news/earnings_calendar, date_range is validated (422 invalid_date_range), and nextNdays (1 to 366) is sent as an explicit window from today, returned as resolved_date_range.

Regime history says how far its labels reach

When /api/v2/regime/history refuses a days window with no label in it, the refusal adds window_days and min_days_with_labels, the smallest days that reaches coverage_end: 105 on 2026-09-25. A served history adds window_days, as_of, stale and report_freshness, grading its newest label on a daily report's 72-hour limit, so while the sequence ends on 2026-06-12 it is always stale. See Example 6.

The quote's exchange calendar runs through 2028

The exchange calendar behind market_status on /api/market/quote/{ticker} now lists NYSE holidays and early closes through 2028-12-25; it used to end at 2026-12-25. Past its last listed holiday, holidays are unknown to it, and a holiday is recognised only from the data vendor's empty day bar during core hours.

2026-09-24

Breaking: a route with no data behind it returns 503, not an empty list

A route whose dataset was never built, or whose read came back with nothing, used to answer a billed 200 with an empty list or a null. It now answers 503 with error: "upstream_unavailable" and error_code: "dataset_unavailable", not billed. The body has no result field and carries reason (table_missing, table_empty or source_unreadable) and retryable, with a Retry-After header: 3600 seconds for a dataset that was never built, which a retry will not fix, and 60 otherwise. Among the documented routes, this applies to /api/insider/clusters when the insider feed cannot be read or has no rows, to /api/v2/regime/history when no label falls inside days, and to /api/v3/intel/factor_importance, /api/v2/features/importance, /api/v2/models/drift and /api/v2/features/decay when no measurement is published. Routes outside the public API that read a dataset that was never built refuse the same way. See When a dataset is unavailable.

The reference clients no longer retry a 503 that says "retryable": false or asks for a wait longer than 120 seconds.

Breaking: regime history counts days on the calendar

days on /api/v2/regime/history now means calendar days back from today, not the last N recorded labels, so fewer rows can come back. From the recorded sequence the response adds coverage_end, the last date the sequence covers. That sequence currently ends on 2026-06-12, so on 2026-09-24 a days below 104 is refused as above. See Example 6.

Breaking: 13F routes refuse a failed read instead of answering empty

/api/v3/intel/sec13f, /api/v3/intel/sec13f/changes and /api/v3/intel/top_shareholders/{ticker} answer 503 with error: "upstream_unavailable", not billed, when neither the 13F archive nor the alternative-data feed can answer. They used to return a billed 200 with an empty list, or a 504 at the time limit. error_code is archive_read_timeout (Retry-After: 60), archive_read_failed (120) or upstream_dead_no_archive (120). /api/v3/fundamentals/institutional_ownership and /api/v3/intel/sec13f_history/{ticker} refuse a failed archive read the same way, with archive_read_failed. In the Copilot ticker aggregations, the sec13f_changes and institutional_ownership layers are then missing with the reason http_503. See When the 13F archive cannot answer.

Breaking: 13F changes no longer report a missing filing as a trade

On /api/v3/intel/sec13f/changes?ticker=, a manager with no filing at all for one of the two quarters compared is left out, instead of being shown as having closed or opened the position. unreported_prev_holders, unreported_cur_holders and unreported_note count them. is_closed and is_new now always describe a manager that filed for both quarters and held the stock in only one of them.

Breaking: whale holdings are dated per manager and valued in dollars

/api/v3/origin/whale_holdings serves each tracked manager's newest filing only when it is for the quarter now due or the one before; older filings are named in managers_with_old_filings instead. The answer can be available: false when no manager has filed recently enough, and a ticker= answer can name older_filings_with_position. Filings that state value in thousands of dollars are scaled to dollars, and positions are ranked by that value. New fields: period_ending, manager_periods, lagging_note and the 13F freshness fields. See Example 4.

Breaking: 13F history sums each holder's lots

/api/v3/intel/sec13f_history/{ticker} now sums each holder's equity lots per quarter and leaves out option lots, so a manager that files a stock in several lots appears once, with its total, and values change. Its quarters window reaches one quarter further back, so an archive a quarter behind EDGAR still returns the number of quarters asked for.

Breaking: SEC filings come from EDGAR first

/api/v3/fundamentals/sec_filings reads SEC EDGAR first and falls back to a third-party filings index only when EDGAR cannot answer, so filings that index had not yet listed now appear. On EDGAR rows, url and report_url point to the filing in the EDGAR archive under the company's CIK. New fields: source, newest_item_date, is_stale, is_partial, accepted_date_timezone, and where they apply searched_back_to, partial_note, fallback_reason and staleness_note. Answers are cached for up to 5 minutes, or 1 minute from the fallback, instead of 30 minutes. See Where the index comes from.

Breaking: forward returns are no longer served as characteristics

/api/v3/factors/characteristics/{ticker} withholds the panel's realized forward returns (the fwd_* columns and excess_ret_1m): they are the return after as_of, not a characteristic known then. outcome_labels_withheld names them. /api/v3/factors/characteristics/{ticker}/history refuses them with 422 unknown_characteristic. See Example 9.

Breaking: feature panels are dated by observation and graded on their data

/api/v3/features/{dataset} keys observation_date, since, until and the latest cross-section on the date a row describes. On equity_fundamental_features_monthly that is now the month, where it used to be the ratios' publication date, which moves to its own column, ratio_vintage. beta_features adds returns_data_through. Rows dated after today, and monthly rows for a month still in progress, are no longer served. A dataset is current only when its data is also on its own rhythm; a live producer over old data is archival with not_current_reason: "data_behind_its_own_cadence", and daily panels count that clock in weekdays. A servable dataset measured into withheld now returns 404 dataset_withheld, not billed. Where they apply, the catalogue and reads add vintage_column, vintage_clock, data_clock_counted_in, data_lag_business_days, typical_gap_business_days, coverage.rows_dated_after_today and coverage.rows_written_before_month_end. See Feature store.

Breaking: quote volume is today's only, and market status follows the exchange calendar

market_status on /api/market/quote/{ticker} now comes from the exchange calendar and the New York clock, not from the data vendor's session flag. It no longer says open before the session starts, reports exchange holidays as closed all day, ends the session at 13:00 ET on early-close days, and says open during the session even for a stock that has not traded yet that day. day_volume on the quote, and quote.volume on /api/snapshot/{ticker}, are the current session's only: null before 04:00 ET, on weekends and holidays, and when the daily bar is empty or from an earlier day, where they used to carry the previous session's volume. /api/snapshot/{ticker} also fills quote.as_of_ts, quote.change and quote.change_pct, which were always null.

13F answers say how current they are

sec13f, sec13f/changes, sec13f_history, top_shareholders and institutional_ownership add newest_report_period, expected_report_period, is_stale, quarters_behind, vendor_ceiling and staleness_note, so an answer a quarter behind EDGAR says so. A stale ?ticker= answer on sec13f and sec13f/changes adds current_quarter: the tracked managers' newer filings from EDGAR. See How current a 13F answer is.

Insider clusters are dated by their latest purchase

On /api/insider/clusters, each cluster's as_of_ts is the date of its latest purchase instead of the time of the request. Clusters count open-market purchases (Form 4 code P) only, and the 30-day window is read in full up to a row cap. The response adds rows_scanned and complete, which is false if the cap was reached, and source is now warehouse:insider-archive.

Docs follow the public API route by route

The docs now describe the routes in the OpenAPI spec and name the MCP tool beside each one in the API reference. New pages cover Crypto and Reference Data. Execution & Research, Private Markets and LLM Research document their remaining routes, among them the trading plan and equity brief, the private-company page, valuation and syndicate, and page reading with research_fetch_url.

Routes that are outside the public API are no longer listed as endpoints: portfolio VaR and stress, pairs signals, execution cost estimates, smart routing, the scenario simulator, and the quote-stream subscribe call (connecting to /api/v3/stream/quotes subscribes coverage by itself). Cost estimates and smart routing still answer 503 capability_data_unavailable and consume no credits. /api/market/quote/{ticker} stays documented as a supported route outside the spec.

The pages also document the billing cases that are not one charge per call. A 200 from a paid route is billed even when it holds no data: many routes answer an empty list, a null value or a warning instead of an error, and the trading plan, the technical read and page reading report some problems in an error field. Withheld results, such as a private-company valuation without enough priced rounds, arrive as 503 paid_data_unavailable and are not billed.

2026-09-23

Breaking: malformed JSON returns 400

A request body that is not valid JSON now returns 400 invalid_json ("request body is not valid JSON"). It used to return 422.

Breaking: OpenAPI operation ids use the new tool names

The OpenAPI spec now uses the new tool names as operationId, so an SDK regenerated from it gets new method names: for example fundamentals_full instead of tengu_v3_fundamentals_full. Calling an unknown MCP tool now returns JSON-RPC error -32602, as the MCP specification expects, instead of -32601.

Breaking: internal and unavailable tools removed from MCP

39 internal operations tools and tools with no data behind them are no longer listed in tools/list or the OpenAPI spec, and can no longer be called over MCP. The 12 server-sent-event stream tools stay on REST only, because MCP cannot relay a stream. The public API has 282 operations; the MCP server lists the 270 that are not streams, plus account_status: 271 tools. Later the same day, stream_realtime_guide and market_events_catchup joined MCP, because they answer JSON rather than a stream. Since then the MCP server has listed 273 tools, and 10 stream operations are REST only.

Breaking: ops routes need an admin key

GET /api/logs and GET /api/cache/stats now require the platform's admin key and are gone from tools/list, the OpenAPI spec and /api/capabilities.

MCP tool names drop the brand prefix

Tools have plain names: tengu_v3_fundamentals_full is now fundamentals_full, tengu_v2_short_interest is short_interest and tengu_ml_predict is ml_predict. The old names, and the names of tools retired earlier, still work in tools/call, so agents built against them keep working. Every tool now carries an MCP title and the annotations readOnlyHint, destructiveHint, idempotentHint and openWorldHint.

The MCP starter list matches your plan

Without a catalog option, tools/list returns account_status plus the first 12 tools your plan and add-ons can call, so no first call answers 402. tools/call accepts any public tool, listed or not. Use ?catalog=full to list every tool. Each OpenAPI operation names its lowest plan in x-wealthnow-min-plan.

Listed in the MCP Registry as io.wealthnow/mcp

The server is in the official MCP Registry as io.wealthnow/mcp: Streamable HTTP at https://mcp.wealthnow.io/mcp, with OAuth sign-in or an X-API-Key header. The old io.github.Hlobo-dev/tengu-firm entry is deprecated and points to it. The server card at https://mcp.wealthnow.io/.well-known/mcp/server-card.json now lists every MCP tool with its full description and input schema: 271 when it was published, and 273 once the two JSON routes above joined MCP.

Rate-limit headers

Authenticated responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix time in seconds), read from your key's budget. A 429 now carries the real Retry-After in seconds, at least 1, instead of a fixed 60. X-RateLimit-Remaining and X-RateLimit-Reset are left out when the count came from a cache. See response headers.

Deprecation headers (RFC 9745)

Deprecated routes now answer with a Deprecation header in RFC 9745 form (Deprecation: @1790121600, the date the signal was first sent). When a route has a replacement, they also send Link: </api/...>; rel="successor-version". Look for the header in your client to find the calls to move. No route has a Sunset date. See response headers.

Every error body carries doc_url, a link to the error table. A 422 validation error names the offending parameter in param (for example "limit") and says what is wrong: invalid 'limit': Input should be greater than or equal to 1. The 401 body links the quickstart.

Settlement headers on every metered call

Failed calls are never charged: every non-2xx response is refunded and says so in X-Credits-Settlement. The same applies to a paid response that could not be delivered and to a stream that aborts. Metered responses carry X-Credits-Receipt, and JSON responses also carry X-Credits-Settlement (consumed or refunded). A stream settles after its last byte. See response headers and how credits work.

Usage alerts at 80% and 100% of included credits

A workspace gets one email when it has used 80% of the credits included in its billing cycle (the plan's grant plus credits bought this cycle), and one at 100%. The 100% email says what happens next: on Free, calls return 402 usage_exceeded until the cycle resets; on paid plans, calls keep working and overage is billed at the plan's per-credit price. Each alert is sent at most once per cycle.

Team members and roles

Invite people from Settings, Team as Admin or Member. Every plan has 20 Basic Seats for members; Full Seats for admins are 1 on Free and Starter and 10 on plans that include a team. A pending invitation holds a seat and expires after 7 days. Members open the workspace from the workspace switcher, and can see and create named keys. Only admins can buy plans, add-ons and top-ups, open the billing portal, rotate, revoke or reveal keys, and disconnect agents. Only the owner can delete the workspace.

Docs at /docs, readable by agents

The docs live at https://app.wealthnow.io/docs, and old /tengu-docs links redirect there. Agents can read them as plain text: /llms.txt, /docs/llms-full.txt, and every page as markdown at /docs/<page>.md.

2026-09-22

Tengu is now Wealthnow

The API has a new name and new hosts:

ServiceHost
Dashboardhttps://app.wealthnow.io
REST APIhttps://firm.wealthnow.io
MCP serverhttps://mcp.wealthnow.io/mcp
Key servicehttps://keys.wealthnow.io

The tengu.co hosts still answer, so existing integrations and connectors added as firm.tengu.co/mcp keep working. Keys keep the tengu_ prefix. The OpenAPI spec is titled "Wealthnow API" and lists https://firm.wealthnow.io first. The Claude Desktop extension is now wealthnow.mcpb, and the old tengu.mcpb link redirects to it.

Connect an agent by signing in

Claude (web, Desktop and mobile), Claude Code, Cursor and ChatGPT connect to https://mcp.wealthnow.io/mcp with Sign in to Wealthnow (OAuth 2.1 with PKCE), with no key to paste. In Claude Code, run claude mcp add --transport http wealthnow https://mcp.wealthnow.io/mcp, then /mcp. A connection holds a one-hour access token (tengu_at_…) with a rotating refresh token, never your API key. Clients revoke a token with POST https://mcp.wealthnow.io/oauth/revoke (RFC 7009), and GET https://mcp.wealthnow.io/oauth/userinfo returns the connection's plan and credits_remaining. The dashboard lists connected agents on the Overview page and can disconnect them. See MCP and SDKs.

Fixed intermittent 502 errors on key verification

Metered calls verify your key and reserve credits reliably. A 502 unkey_unreachable is a transient key-service error: it carries Retry-After, is never charged, and is safe to retry. See error responses.

September 2026

Breaking: private-markets search is a paid call

/api/v3/private_markets/search needs Pro or the Private Markets add-on and costs 5 credits. On other plans it returns 402 plan_required. MCP tools accept companyid as an alias for company_id.

Breaking: price history is daily

/api/v3/prices/history serves daily bars and interval defaults to 1d. An intraday interval returns 422 interval_not_daily, pointing to /api/v3/tape/bars/{ticker}?date=. /api/v3/prices/* needs Starter or above.

Data fixes

The dataset freshness report /api/data/freshness added as_of_period, the fiscal period a dataset covers, next to pipeline freshness; it is not part of the public OpenAPI spec. The legacy quote route /api/market/quote fills prev_close and change_pct when it falls back to a second vendor, and reports the pre-market, after-hours or closed session outside market hours; its OpenAPI equivalent is /api/v3/fundamentals/price_snapshot.

August 2026

Breaking: plan changes for older routes

Some older routes were reachable on plans that did not include their data. /api/snapshot/{ticker} now needs Quant Signals (Starter). /api/v3/prices/history, /api/v3/prices/corporate_actions, /api/v3/fundamentals/segments and the street-estimates routes need Starter. Check each route's plan in the API reference.

Breaking: rate-limited calls return 429

A billable call over your rate limit is refused with 429 rate_limited instead of being served. When the credit check cannot complete, the answer is 429 billing_unavailable with Retry-After.

Crypto derivatives, events and on-chain flows

New on every plan: /api/v3/crypto/derivatives/funding, /api/v3/crypto/derivatives/open_interest, /api/v3/crypto/derivatives/basis and /api/v3/crypto/derivatives/vol_surface; /api/v3/crypto/events/announcements (listings, delistings and trading-caution flags from the exchanges' own announcements) and /api/v3/crypto/events/headlines (breaking crypto-native headlines, mapped to coins); and /api/v3/crypto/onchain/flows (stablecoin issuance and lending liquidations, read from the chain).

Refuse stale data with freshness

/api/v3/intel/macro_snapshot and the options-flow and dark-pool routes accept freshness=off|live|strict. The default, off, serves the data with its age. live returns 503 data_stale when a live-market field is stale, and strict when any field is past its own update cadence.

Price fixes

When every price source fails, price routes return 503 price_feed_unavailable instead of a 200 with no items. A limit without dates returns the most recent bars, not the oldest. asset_class=crypto returns real crypto prices and bars.

Failed calls are not charged

A call that fails (any status of 400 or above) is refunded and reports X-Credits-Cost: 0.

New research routes

New: /api/v3/origin/* (short activity, whale holdings, insider flow, macro pulse and the IPO pipeline), /api/v3/workforce/* (hiring momentum and WARN-Act layoffs), /api/v3/supply_chain/* (customers, suppliers and revenue exposure) and /api/v3/fundamentals/pit/* (point-in-time fundamentals, as known on a date). /api/v3/credit/indices serves credit-index composites (Pro), and /api/v3/intel/macro_snapshot adds a credit block with CDX IG and HY spreads.

Live 1-minute bars stream

/api/v3/stream/bars streams live 1-minute bars as server-sent events, on Starter and above. See Streaming.