Changelog
Try it on a test key
curl -s "https://api.wellwardhealth.com/v1/health"
2026-10-28 — API Terms v3: standalone
The API Terms now stand on their own: the Wellward Terms of Service no longer apply to the API, and the terms they used to bring in (warranty disclaimer, limitation of liability, indemnification, dispute resolution, governing law, notices and general terms) are written out in §§11–19. Arbitration now distinguishes Individual and Business Users: Individual Users keep a 30-day opt-out; Business Users arbitrate in New York and cannot opt out. A signed agreement takes precedence over the API Terms where they conflict. You may now combine Wellward prices with your own data or other sources (§4); the earlier rule against blending them into one figure is gone. Nothing else changes: fees, notice periods, caching, withdrawn prices and logging are as before. An org admin is asked to accept the new version in the dashboard (Billing).
2026-10-26 — max_age_days re-calls a listed provider whose price is older
A provider you listed on a price request is now called again when the price we hold for them was quoted more than max_age_days ago. Before, any price we held for a listed provider was the answer, however old, and only prices within max_age_days counted toward your request, so an older one could never be refreshed.
- Without
max_age_days, nothing changes: a price we already hold for a listed provider is still the answer for them. - Billing is unchanged: a request is billed when it produces a price within
max_age_dayswhile it is open.
2026-10-22 — Required consults are prerequisites, not add-ons
A consult or office visit the patient must pay for before a service is now a prerequisite variant, not an add-on. It is never part of the service's price. Eight services had one listed as an add-on: colonoscopy and upper-endoscopy-egd (pre-procedure consult), lasik, wart-removal, mohs-surgery, hernia-repair-inguinal, vasectomy (consultation) and dental-extraction (exam and X-rays).
variants[].typecan beprerequisite. Three services (colposcopy, lip-tie and tongue-tie release) already returned it, but it wasn't documented.- These consults are no longer in
addons[], andaddons=no longer accepts their keys. - A price request whose only result is a required consult completes as
no_price_obtainedand is not billed, as with an add-on on its own.
2026-10-21 — Prices: abutment and crown component names, for dental implants
coverage.includes, coverage.excludes and coverage.components[].component can now name two more core components: abutment and crown. They apply to dental implant prices, alongside the existing implant (the implant fixture). For example, a quote of "implant $2,840, abutment $1,138, crown $2,045" returns each as a component price of the implant-and-crown service.
- Treat a component name you don't recognise as a valid component: new ones may be added.
- No other field changes.
2026-10-21 — Prices: each stated part of a quote is also its own component price; all_in filled in from the call
When a provider quotes a price and says what each part costs, each part is now also its own component price. For example, an MRI quoted as "$490: the scan $380 and the radiologist's read $110" returns the $490 price and two component prices: the scan ($380, coverage.includes ["technical"]) and the read ($110, ["professional"]).
- These component prices follow the existing rules: they're delivered by id (in a price request's
price_idswhen the quote came from your request) and never appear in search, sorting or benchmarks. - The $490 price is unchanged, and its
coverage.componentsstill lists the two amounts. coverage.all_inwasnullfor some prices whose coverage was recorded on the call but not yet confirmed by a second check. It now gives the call's answer (trueorfalse). No other field changes.
2026-10-12 — Price requests: via, when a listed provider is priced through the group at their facility
Each entry in a price request's providers gains via. When you list an anesthesiologist, the price usually comes from the anesthesia group that covers the facility they work at, not from the doctor. That price now counts for the doctor you listed: it is in their price_ids, and via says how it was reached: { facility: { id, name }, group: { id, name } }. For example, Dr. Bentrem at Stony Point Surgery Center, priced through NorthStar Anesthesia.
practice_idon that entry is the group's.- The price's own
facilitynames the same facility. viaisnullwhen the price is the provider's own. No other field changes.
2026-10-12 — Prices: facility, for a price that applies at a facility
Every price gains facility. It is set when the price applies at a facility the practice works at rather than at its own office: an anesthesia group's total for a procedure at the surgery center or hospital it covers. For example, NorthStar Anesthesia's total for a deep facial tumor removal at Stony Point Surgery Center, Richmond VA.
facilityhasid,name,address_line1,city,stateandzip. It isnullfor a price at the practice's own office.- When
facilityis set,practice.geoanddistance_milesare the facility's: a group that works across many states is found near the facilities it covers. - Additive: no other field changes.
2026-10-09 — Prices: each component's own amount, when the provider stated it
Every price's coverage gains components: each core component of the service with its status and, when the provider stated it on the call, its own amount. For example, a delivery package quoted as "$19,126.77: the hospital's $14,756.77 and the physician's $4,370, anesthesia billed separately" returns facility and professional with their amounts (included) and anesthesia excluded.
- An amount is only ever a figure the provider said. We never estimate, split or derive one, and a range gives no amount.
- An
includedcomponent's amount is part of the price'samount; when every included component has one, they add up to it. Anexcludedcomponent's amount is quoted separately, on top. - Amounts are given only for services with two or more core components.
- Additive:
all_in,includesandexcludesare unchanged.componentsis empty when a price's components weren't established.
2026-10-08 — Price requests: prices from calls we made for you count at any distance
A completed price request now includes a price from any provider we called for that request, whatever its distance. Where few providers are near your location, we sometimes look further than radius_miles; a price from one of those calls used to be left out, and is now sent.
- Providers you list in
providersstill count at any distance. - Other nearby prices (ones we already held, not gathered for your request) still come only from within
radius_milesby road. distance_mileson each price tells you how far it is. No fields change.
2026-10-07 — Billing: charged daily
Card accounts are now charged daily: shortly after midnight US Eastern, one invoice for the previous day's (US Eastern) Reads and completed price requests. Days without billable usage aren't charged. Rates are unchanged. The first daily charges cover October 1 onward; nothing from October is billed again on November 1.
- The dashboard's Billing tab lists each day's charge with its invoice.
billing.payment_failedkeepsmonth(YYYY-MM) and addsday(YYYY-MM-DD) when the unpaid invoice is a daily charge.- Invoiced (net 30) accounts are unchanged: billed monthly.
2026-10-06 — Price requests: nearby means within driving distance
A price request's nearby prices now come from providers within radius_miles by road. Before, distance was a straight line, so a provider across water (for example 20 miles across Long Island Sound from Norwalk, CT, and 60+ miles by road) counted as nearby.
- Providers you list in
providersstill count at any distance. /v1/pricessearch anddistance_mileson prices are unchanged; they stay straight-line.- No fields change.
2026-10-06 — Price requests: one price per practice
A practice's rate is one data point. When we reached the same practice on two lines (for example its main number and a doctor who works there) and both quoted the same amount for the same variant and coverage, a request's price_ids carried it twice. It now carries it once. If one of those lines was a provider you listed, the kept price is that one, so it still appears under that provider in providers. Different amounts, variants or coverage are still separate prices. No fields change.
2026-10-06 — Price requests: one listed provider's price isn't another's
If you list a hospital or practice and a clinician whose backup number turns out to be that same hospital's line, the hospital's price now counts only under the hospital's entry in providers, not under the clinician's too. Before, it appeared under both. The price is still in the request's price_ids. No fields change.
2026-10-06 — Price requests: service is null until a query is matched
A request sent with query (and no service) now shows service: null until our team matches your words to a catalog service, which happens before any calls. Before, service showed our automatic first guess straight away, and that guess was often wrong.
- Affects the create response,
GET /v1/price-requests/{id}and webhook payloads. - A request sent with
serviceis unchanged. - No fields are added or removed.
2026-10-06 — Price requests: an add-on on its own is not a result
A provider whose only price for the service is an add-on (for example, allergy skin testing priced per antigen, with no consult price) is no longer in a request's price_ids. That matches what Request a price already said. An add-on still arrives alongside the same provider's core or component price.
- A request whose only result was an add-on now completes as
no_price_obtainedand is not billed. Before, it could complete asprice_obtainedand be billed. - No fields change.
2026-10-06 — Practice names: never a different person's name
A practice is now named after the organisation that quoted the price, never after a clinician who isn't that practice. Previously a practice could be named after any clinician whose NPI it carried, for example a hospital's price-estimation desk published under one surgeon's name.
practice.namefollows these rules, in order:- the practice's own registry organisation;
- a registry organisation matched by both the business name and the street address (
practice.npiandentity_type: "organization"then describe that organisation); - the clinician's own name, when the business is evidently theirs (their surname is in its name);
- otherwise the practice's business name from its Google listing (
identity_source: "google", withentity_typeandnpinull).
identity_sourcegains a value,google. Most practices without a registry organisation now have a name. Before this, more than half of all prices had none.- No fields are removed.
2026-10-06 — Price requests: listed-provider results follow the call
A listed provider's price_ids now hold the prices from the numbers we called for that provider, and any recent price we already held for it. They used to hold any price at the practice the provider was grouped under. Only that matching changes; no fields change.
- A price can't drop out of a listed provider's results when its practice is regrouped.
- A price from a different provider at the same practice no longer counts as yours. It can still arrive as a nearby price.
2026-10-05 — coverage on every price, and component prices
Every price now says which core components it includes. Price requests also deliver component prices, such as a surgeon's fee alone. Nothing existing changes.
coverageis a new field on every price:all_in(true,falseornull), plusincludesandexcludes, which list core components:professional,facility,anesthesia,standard_lensandtechnical. No existing field changes.- Component prices: a quote for part of a service, such as a listed surgeon's professional fee with the facility and anesthesia billed separately. They're now delivered with price requests and fetchable with
GET /v1/prices/{id}.coverage.all_inisfalseon them. They never appear in search, sorting or benchmarks. - Billing: a price request that obtains only component prices is billed as a completed request, the same as one that obtains an all-in price.
2026-10-05 — Prices aim for the all-in total
Our calls now ask every provider for the all-in price, everything the patient would pay, and how it splits between facility, professional and anesthesia fees. No fields change.
amounton prices gathered from today is the all-in total whenever the provider could give it. Older prices are unchanged. Many were already all-in, andconditions.notesays when one isn't.conditions.notegives the breakdown when the provider gave one:All-in $T = facility $A + professional (surgeon) $B + anesthesia $C. A line startingElsewhere:is a price at a different location, such as another facility the same surgeon operates at. It is never this practice's price.- Imaging prices aim for the global price, meaning the scan plus the radiologist's read.
- Price requests that list clinicians at a facility: we ask for each clinician's professional fee at that facility, the all-in there, and whether they do it for less anywhere else they practice.
2026-10-01 — Price requests: two business days when we can't match the service
A request whose query matches no catalog service now gets an expected_by two business days out, not one: our team adds the service before any calls are made. Requests for a catalog service are unchanged.
2026-10-01 — Price requests: a blank service or query counts as not sent
"service": "" or null alongside a query is now accepted, as is a blank query alongside a service. Nothing that worked before changes.
- Either field may be blank or
nullas long as the other has something; a request with neither is still 400 ("Giveserviceorquery"). - The request-a-price guide now covers asking for a procedure that isn't in the catalog: describe it in
query(CPT codes welcome) and leaveserviceout.
2026-09-29 — Fixes from a first-customer walkthrough
Practice names, benchmark defaults, coverage by market, MCP radii, and clearer docs. No fields removed.
- Practice names: about 320 practices were named
<UNAVAIL>— a placeholder in the federal registry's "other name" field. They now show the practice's legal name. /v1/benchmarkswith novariantnow uses the service's default variant — the one/v1/pricesshows — and falls back to the variant with the most practices only when the default has no distribution there. (It used to take the largest sample, so colonoscopy benchmarked a different procedure from the prices beside it.)/v1/coverageacceptsmarket=, as/v1/pricesdoes.- MCP
find_cash_pricesaccepts every REST radius (75 and 100 were refused). - Docs: guide tables render as tables;
placeid_onlypractices, add-ons a practice hasn't quoted,summary.countvsservice_coverage.price_count, idempotent replays, and MCP'sobservation_idare explained.
2026-09-29 — Price requests: what counts, and the service we gather
A price we already hold at a provider you listed now counts for your request, and a query's service is checked by our team. No fields change.
- A listed provider's existing price counts. We don't re-call a provider we already have a recent price for; that price is the answer for them, and the request is billed as a price obtained (within
max_age_daysif you sent it). Prices near your location still have to be quoted on or after the day you asked. servicefrom aqueryis checked by our team before any calls are made, and may be corrected or assigned — so it can change while the request is queued. A request sent withserviceis never changed.
2026-09-29 — Price requests: list the providers to call first
A price request can now name up to 25 providers to call first, and reports a result for each. Additive.
providersonPOST /v1/price-requests: up to 25{ npi, facility_name }. We call those first, then nearby providers. A location is still required. An invalid NPI (check digit) or a repeated one is 400.- On completion each listed provider carries
result,price_idsandpractice_id; the request'sprice_idsstill holds every price. Billing is unchanged: once per request that obtains a price. - Every price request now includes
providers(empty when none were listed).
2026-09-28 — Price requests: one name for each thing
These remove fields from /v1. No live price requests have been made yet, so they ship without the 90-day notice.
idonly.request_idis gone from the price request — it repeatedid, andrequest_idalready names the HTTP call in error responses andmeta.stateonly.statusis gone from the price request, and the list filter is?state=(queued,in_progress,completed,expired,cancelled);?status=is 400invalid_request.expected_byandresolved_atonly.estimated_completion(the same date as a datetime) andcompleted_atare gone;resolved_atis set when a request is completed, expired or cancelled.joined_existingis gone (it was alwaysfalse).callback_urlis refused with 400invalid_request. It was accepted but never called; results come by therequest.completedwebhook or by polling.- Webhooks:
request.completedandrequest.expiredcarry exactly the price request object.observation_idsandreasonare gone (useprice_idsandnote), and the old event nameprice_request.completedcan no longer be subscribed to.
2026-09-25 — Status page measured from outside
The status page now reports the API, webhook delivery and the developer portal as measured by external checks every few minutes, plus incidents our team posts — nothing else.
2026-09-25 — Sorting, page size, summary, per-page Reads
One billing change: each page of /v1/prices results is now a Read. The rest is additive.
sortonGET /v1/prices:price(the default: ascending amount, ties by distance) ordistance(ascending distance, ties by amount). The originalprice_ascstill works asprice; any other value is 400invalid_sort.- Page size is 20 (default and maximum). A larger
limitis 400limit_too_largerather than being quietly reduced. Cursors last 60 minutes and keep the first page's sort and radius. The 100-result depth limit is gone. summaryon every/v1/pricesresponse:count,min_price,median_price,max_priceandnearest_distance_miles, over all results before paging, identical on every page./v1/healthis liveness only.rowsanddata_as_ofare gone from it, andmeta.data_as_ofis gone from every response — data freshness is an internal signal; our promise is that every price was quoted within 364 days.- Reference and guides reorganized into eight groups and four sections. Bulk data is no longer in the public reference; it is available under an enterprise agreement.
- Every page is a Read. A follow-up page used to be free within an hour; now each page that returns prices is one Read.
summary.counttells you how many pages a full pull takes. An identical call within 60 seconds still counts once.
2026-09-24 — API Terms v2.1
The API Terms were updated; an org admin is asked to accept them again in the dashboard (Billing). What changed: 30 days' notice (was 60) before a new or higher fee; don't blend Wellward prices with other sources' prices into one figure (aggregates of Wellward prices alone are fine); stop showing a withdrawn price within 24 hours; the price-request band is "that market or comparable markets"; and §9 now describes exactly what we log.
2026-09-24 — Limits per organization, catalog-only search
Some of these remove things from /v1. There are no live customers yet, so they ship without the 90-day notice.
- Limits are now per organization, not per key, and test and live are counted separately. Prices and benchmarks: 5/second, 5,000/day. Search: 2/second, 1,000/day. Price requests: 2/second, 500/day. Everything else: 20/second, 100,000/day. 1,200 calls a minute overall.
- Price records per day: an org is served at most 10,000 price records in any 24 hours (2,500 for a new account until its first invoice is paid), counting follow-up pages. One query pages through at most 100 results.
- Monthly quotas and tiers are retired.
quota_exceededis no longer returned;/v1/accountshows the new limits. /v1/searchis catalog-only. It returns services, never prices, and is never billed. Thenearparameter is gone; take the slug to/v1/prices./v1/providers/{id}lists what we price at a practice, not the prices.- Price requests are never joined. Each request has its own
expected_byand is billed on its own;joined_existingis alwaysfalse. - The spending cap now also stops new live price requests. New accounts have a lower monthly cap until the first invoice is paid.
- Updates (
POST /v1/prices/{id}/updatesand/disputes) now require a key with thecontributescope. A reported price joins your changes feed and webhooks, so you hear when it changes.
2026-09-24 — API Terms v2 and flat pricing
Nothing existing was removed. One change adds a charge: GET /v1/benchmarks is now billed when it returns a distribution.
- Pricing is flat and per usage: one rate per Read and one per completed price request (see the pricing page).
GET /v1/benchmarksnow counts as a Read when it returns a distribution. Test keys are still free. See Billing. - Updates:
POST /v1/prices/{id}/updatesis the new name for reporting that a provider quoted something different.POST /v1/prices/{id}/disputesstill works, and price records carrylinks.updatealongsidelinks.dispute. - Display: the disclaimer is the only required display element. Attribution is optional, so
attribution.requiredis nowfalse, anddisclosurenow returns the disclaimer text. See Displaying prices. - Versioning: 90 days' notice before a field or endpoint is removed or renamed within a version. A version may be retired six months after its successor is announced.
Fixes
GET /v1/searchwithout prices in the answer is no longer billed as a Read.GET /v1/prices/changesno longer skips events that share a timestamp at a page boundary.- Webhook retries now run the full schedule (about 24 hours); they stopped after about 9.
- With
addons=,amountnow includes the requested add-ons for every service, matchingincluded_in_amount. market=now searches the market's own radius when you don't passradius_miles.GET /v1/price-requests?to=<date>now includes that whole day.- Error responses now carry
Cache-Control: private, no-store, andWellward-Modefor test keys. - At the spend cap, MCP now pauses only the billed tools.
2026-09-23 — Self-serve launch
All additive; nothing existing was renamed or removed.
- Self-serve accounts. Sign up and get a test key immediately. Orgs, team members and roles (admin, developer, billing).
- Test mode.
wl_test_keys read a fixed fictional dataset and never create work;test_scenarioexercises every error. - Keys are now
wl_test_…/wl_live_…, named, and can be rotated with overlap. Existingwwk_keys keep working. Keys may also be sent asX-Api-Key. - Only valid prices. Every price is inside a 364-day freshness window and currently valid. Withdrawn prices answer 410 with
withdrawn_at. - Prices:
as_of, nestedaddons[],service_coverage,attributionanddisclosure;zipas an alias ofnear;radius_miles75 and 100.serviceis now required (amarketwithout one used to return every service's prices together). - Coverage:
services[]with per-service counts andfull/partial/none. - New endpoints:
/v1/meta/attribution,/v1/prices/changes,/v1/usage,/v1/account,GET /v1/price-requests,POST /v1/price-requests/{id}/cancel,POST /v1/webhooks/{id}/test,GET /v1/webhooks/{id}/deliveries. - Price requests:
state,outcome,billable,price_ids;Idempotency-Key;provider_id,variant_key,max_age_days,metadata; 402 / 409 / 422 refusals. - Webhooks:
request.completed,request.expired,price.withdrawn,price.updated,billing.payment_failed,usage.threshold_reached;Wellward-Signature: t=,v1=; retries for about a day. - Rate limits per endpoint class, with
X-RateLimit-*andRetry-Afteron every 429. - Billing: per-Read pricing for live keys; usage, statements, spending caps.
API reference · Dashboard · Questions: hello@wellwardhealth.com