#17397

fix(backend): Search spend: analytics shows flat price × count, Costs page omits search, several providers bill variably

Openyourspraveenopened 1d ago

Summary

Search spend is hard to see and, for several providers, recorded below what the provider bills:

  1. The Search analytics page does not use per-call cost. /api/v1/search/analytics computes cost as registry costPerQuery × number of calls (src/app/api/v1/search/analytics/route.ts:37). call_logs has no cost column, so the page cannot reflect per-mode prices, extra results or content surcharges, even when the handler computed them (#17394, #17396).
  2. The Costs dashboard never shows search spend. /dashboard/costs reads usage_history / account_events through /api/usage/analytics, and its Pricing tab lists model prices only. No search provider appears there.
  3. The only accurate per-call record is request_cost_ledger (service_tier = "search", written by recordSearchUsageCost() in open-sse/handlers/search/reportedCost.ts). It feeds per-key USD quotas, and a row is written only when the search came in with an OmniRoute API key.
  4. Several providers bill variably, but the registry records a flat price. Audit below.

Provider pricing audit (open-sse/config/searchRegistry.ts, release/v3.8.53)

How each row was checked: live = real API calls comparing Exa's costDollars; docs = the provider's own pricing docs; 3rd-party = only non-provider sources found, unverified; not checked = flat price assumed.

ProviderRegistryWhat the provider billsGapSource
exa-search$0.007 flatauto $0.007 / lite $0.004 for 10 results, +$0.001 per extra result, +$0.001 per result with contents.summaryFixed by #17394 + #17396 (the recorded cost matches costDollars in every live case)live
tavily-search$0.008 flatbasic 1 credit, advanced 2 credits, $0.008/creditsearch_depth: "advanced" (allow-listed in provider_options) recorded at half price. fast / ultra-fast not covered by the credits page. The include_usage passthrough may let the response report credits (needs checking).docs
linkup-search$0.005 flatfast/standard $0.005, deep $0.05; +$0.001 for sourcedAnswer/structureddepth: "deep" (a provider setting the builder honors) recorded at 1/10thdocs
firecrawl$0.002 flat2 credits per 10 results, rounded up; scraping adds per-page credits; $/credit depends on the plan (~$0.001–0.005)Requests above 10 results (max 100) under-recorded; $/credit is plan-specific, so a static price can only be approximatedocs
serper-search$0.001 flatReportedly 1 credit up to 10 results, 2 credits for 11–100num > 10 possibly recorded at half price3rd-party (1, 2); Serper's own pricing page returns 404
x-search$0A Grok call with the x_search tool: model tokens plus tool fees (reportedly per post/profile fetched since 2026-09-21)Recorded at $0 although every call is paid3rd-party (1, 2); xAI tool docs say tools bill tokens + invocations
perplexity-search$0.005 flat$5/1k; search_type: "fast" $1/1kNone today (the builder doesn't send fast)docs
jina-search$0.002 flatToken-based, at least 10,000 tokens per searchDepends on plan pricing; not verifieddocs (price table not reachable)
parallel-searchper mode + overageAlready modelled with costByMode / includedResults—existing
brave-search, google-pse-search, searchapi-search, youcom-search, nimble-searchflat—Not checked. You.com livecrawl (sent when full_page is requested) may cost morenot checked
searxng-search, duckduckgo-free, context7, ollama-search, zai-search, anysearch-search, xquik-search$0 or small flatfree, self-hosted or flatNot checked—

Questions for the maintainer

  1. Analytics source of truth. Should the Search analytics page sum real per-call cost? Two ways: add a cost_usd column to call_logs for search rows (a migration), or read request_cost_ledger (which misses calls made without an API key).
  2. Costs dashboard. Should search spend appear on /dashboard/costs, for example as a "Search" service tier or provider rows next to LLM spend?
  3. Provider-reported cost. Exa returns costDollars.total on every response, and Tavily can return usage with include_usage. Today reportedCost.ts only trusts a reported cost when the operator opts in (trustReportedCost), to stop a spoofed endpoint setting its own price. For providers whose base URL callers cannot override (all authType: "apikey" providers, GHSA-3f8g-pfh9-j687), should their native cost field be trusted by default, behind the opt-in, or not at all?
  4. Static pricing fixes. Are PRs like #17396 welcome for Tavily advanced, Linkup deep and Firecrawl's per-10-results billing? Those three are documented by the providers themselves. Serper and xAI need someone with a key to confirm first.

I'm happy to implement whichever direction you choose. #17394 and #17396 cover Exa only and don't depend on these answers.

Activity

1
adrielb-pm-n1d ago

On the nimble-search row, which is currently "not checked": Nimble prices by search_depth, not as a flat per-query rate.

SettingPrice
lite$1.10 per 1K searches ($0.0011 each)
standard (API default)$5.00 per 1K searches ($0.005 each)
full_content: true+$1.00 per 1K URLs, on top of either depth
Focus modes+$3.00 per 1K requests

Only successful requests are billed. The free tier is 5,000 requests per month with no card required. The Search rate limit is 10 QPS per account, and requests over it get a 429 with Retry-After.

Pricing: https://docs.nimbleway.com/nimble-sdk/admin/pricing Search API: https://docs.nimbleway.com/nimble-sdk/web-tools/search

Happy to confirm any other Nimble billing details the audit needs.