Check coverage before you query
Try it on a test key
curl -s "https://api.wellwardhealth.com/v1/coverage?near=00020" -H "Authorization: Bearer $WELLWARD_KEY"
The pattern that makes the fewest billed calls and the fewest dead ends:
coverage → prices → request
- Ask what we have.
GET /v1/coverage?near=<zip>is free (never a Read) and has its own rate limit. For each service that has prices nearby it returnsprice_count,provider_countandcoverage:full(at least 5 prices from at least 4 practices) orpartial(at least one). A service with nothing nearby is left out, so compare againstGET /v1/services: anything missing from coverage isnone. It never returns prices, practices or dates. - Fetch prices where there are some.
GET /v1/prices?service=<slug>&near=<zip>for services withfullorpartialcoverage. Each call that returns prices is one Read. - Ask us to gather where there are none.
POST /v1/price-requestsfor anone(or thinpartial) service. We contact providers; you hear back by webhook (request.completed) or by polling. You pay only if we obtain a price.
const H = { Authorization: `Bearer ${process.env.WELLWARD_KEY}` };
const base = "https://api.wellwardhealth.com/v1";
// Build the menu from two free calls…
const catalog = await (await fetch(`${base}/services`, { headers: H })).json();
const cov = await (await fetch(`${base}/coverage?near=00020`, { headers: H })).json();
const covered = new Map(cov.data.services.map((s) => [s.service, s]));
const menu = catalog.data.map((svc) => ({ ...svc, coverage: covered.get(svc.slug)?.coverage ?? "none" }));
// …then fetch prices only for the service the user picks — each call that returns prices is a Read.
async function onPick(slug) {
const svc = menu.find((s) => s.slug === slug);
if (svc.coverage !== "none") {
const prices = await (await fetch(`${base}/prices?service=${slug}&near=00020`, { headers: H })).json();
// …show prices.data.prices
} else {
// Nothing nearby: offer to ask for it —
// POST /v1/price-requests with { service: slug, zip: "00020" } and an Idempotency-Key.
}
}
Location works as on /v1/prices: near (a ZIP), lat and lng, or market (a slug from /v1/markets, with that market's own radius unless you pass radius_miles).
Why not just call /v1/prices? An empty /v1/prices result is free too, and returns service_coverage: { level: "none" } with a link to request gathering — so nothing is lost. But /v1/coverage answers for every priced service in one call, which is what you want when building a menu.
A request where coverage is already full is refused with 409 coverage_exists — we already have the prices. If you need fresher quotes, send max_age_days (at least 30) and we will gather anyway.
Idempotency-Key is required on POST /v1/price-requests. Send a fresh unique value per request; for 24 hours, a retry with the same key and body returns the original successful response instead of creating a second request, and the same key with a different body gets 409 idempotency_conflict.
API reference · Dashboard · Questions: hello@wellwardhealth.com