For AI agents and developers
Everything on this site is available as structured data: 1754 priced clinics across 26 markets, each price traced to the page it came from. No key, no scraping. Version 1.0.0.
Entry points
| MCP endpoint | https://toxfinder.com/api/mcp | Streamable HTTP, stateless, no authentication |
| JSON API | https://toxfinder.com/api/v1 | Versioned, read-only, RFC 9457 errors |
| OpenAPI | https://toxfinder.com/api/v1/openapi.json | Every route, parameter and schema |
| llms.txt | https://toxfinder.com/llms.txt | The short version of this page |
| Server card | https://toxfinder.com/.well-known/mcp/server-card.json | MCP discovery metadata (draft SEP-2127) |
| Static export | https://toxfinder.com/data/v1/… | The JSON the site is built from |
Read this before quoting a number
Every clinic has one headline, pricing.normalized_price, in US dollars per Botox-equivalent unit. It is ToxFinder’s normalization of what the clinic published, never a price the clinic advertised: Dysport units convert at 2.5 to 1, per-area prices are divided by a typical dose. The clinic’s own words and figures are only ever in evidence rows under advertised.
pricing.basis_source must travel with the price. stated is a standing per-unit or per-package rate published on the clinic’s site; member/promo is a member-only or promotional rate, real but conditional; derived is an estimate from a flat per-area price with reduced confidence. A headline is the median of a clinic’s prices in its best tier; tiers are never blended.
confidence (0 to 1) multiplies extraction certainty by conversion certainty. Every row carries observed_at; sites are re-read periodically, roughly quarterly. Tell people to confirm with the clinic before booking. See the method for the reasoning.
MCP tools
Protocol revision 2026-07-28; older revisions still work on the same endpoint. Six read-only, idempotent tools, all returning structured content with an output schema, plus two resources (toxfinder://methodology, toxfinder://products).
| list_markets | Every market with clinic count and median | |
| get_market | market | Quartiles, histogram, per-city medians, basis and product breakdown, coverage, freshness |
| compare_markets | markets[] | Two to six markets side by side, with deltas relative to the first |
| search_clinics | query, market, city, state, near, latitude, longitude, radius_miles, product, basis_source, min_price, max_price, min_confidence, observed_after, sort, limit, offset | Deterministic filters on the normalized headline; radius search; summary of the whole match set |
| get_clinic | id | One clinic with every price row, provenance, market rank and nearby clinics |
| get_price_evidence | observation_id or clinic_id | Advertised vs normalized vs provenance for one observation or one clinic |
Client configuration, for hosts that take a URL:
{
"mcpServers": {
"toxfinder": {
"url": "https://toxfinder.com/api/mcp"
}
}
}One raw call, as the transport sends it:
POST /api/mcp HTTP/1.1
Host: toxfinder.com
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search_clinics
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_clinics",
"arguments": {
"near": "Houston",
"max_price": 12,
"basis_source": "stated"
},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": {
"name": "example-client",
"version": "1.0"
},
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}JSON API
| GET /markets | Every market | |
| GET /markets/{market} | One market's statistics | |
| GET /markets/compare | markets=a,b | Comparison |
| GET /clinics | same names as search_clinics | Search; page with limit (1–100) and offset; paging.next_offset |
| GET /clinics/{id} | One clinic with evidence | |
| GET /clinics/{id}/evidence | Evidence rows only | |
| GET /evidence/{observation_id} | One observation's provenance | |
| GET /products | Product vocabulary with conversion ratios | |
| GET /methodology | Basis, confidence and normalization semantics as data |
Examples, all relative to https://toxfinder.com/api/v1:
/clinics?near=Houston&max_price=12&basis_source=stated /clinics?latitude=29.790408799999998&longitude=-95.77855679999999&radius_miles=20&product=dysport&sort=price /clinics?market=houston&city=katy&basis_source=stated /markets/houston /markets/compare?markets=houston,phoenix /clinics/nakedmd-med-spa-katy /clinics/nakedmd-med-spa-katy/evidence
A price row, the shape both surfaces share. Three objects, kept apart on purpose:
{
"clinic": {
"id": "nakedmd-med-spa-katy",
"name": "NakedMD Med Spa Katy",
"market": "houston",
"url": "https://toxfinder.com/houston/nakedmd-med-spa-katy/"
},
"advertised": {
"text": "Dysport $4.70 per unit",
"product": "dysport",
"basis": "per_unit",
"price_min": 4.7,
"price_max": 4.7,
"currency": "USD",
"is_promotional": false
},
"normalized": {
"price_per_botox_equivalent_unit": 11.75,
"normalized_unit": "usd_per_botox_equivalent_unit",
"conversion_path": "perunit:dysportx2.5",
"confidence": 0.92,
"in_headline": true,
"conversion_version": "n1/1.2.0/1.1.0"
},
"provenance": {
"observation_id": "…64 hex…",
"source_url": "https://clinic.example/pricing",
"observed_at": "2026-10-19T12:23:06.6005279+00:00",
"extractor": "llm",
"corroborations": 2
}
}Identifiers, limits, errors
Clinic ids are URL slugs, issued once and never recomputed; the corpus provider id is also accepted. Market ids are metro keys (houston, phoenix, miami, dfw, boston, san_diego, tampa, new_york, chicago, washington_dc, austin, atlanta, los_angeles, seattle, bay_area, north_jersey, san_antonio, nashville, twin_cities, charlotte, baltimore, rhode_island, new_orleans, honolulu, memphis, maui). Observation ids are 64-character hashes, stable across re-crawls.
Rate limit: 60 requests a minute per caller with a burst of 60, shared between the API and MCP; a 429 carries Retry-After. Page size 1 to 100, radius up to 500 miles, requests time out at 10 seconds. Successful answers are cacheable for five minutes and any origin may read them.
A bad filter is a 400 problem document listing every offending field (or an MCP tool result with isError and the same text); an unknown id is a 404 that says where ids come from. Please attribute ToxFinder and link the clinic page when you quote a figure. Questions: support@apextechnologygroup.io.