# Search by meaning

URL: https://docs.healthcare.dev/search

Code sets use their own wording. Someone looking for "diabetes with kidney disease" means
**E11.22** _Type 2 diabetes mellitus with diabetic chronic kidney disease_, and "kidney damage from
high blood pressure" means the **I12** hypertensive chronic kidney disease codes, although the
words differ. `GET /v1/search` finds them: it ranks codes and drugs by how close they are in
meaning to your query and by the query words their text contains.

Search by meaning is part of the **Builder** plan and up. On Free it answers
`403 plan-upgrade-required` (see [Plans](/plans)); the wording-based searches
(`/v1/codes/{system}?q=`, `/v1/drugs/search`) are on every plan.

```sh
curl "https://api.healthcare.dev/v1/search?q=diabetes%20with%20kidney%20disease&system=icd10cm" \
  -H "Authorization: Bearer $HDEV_API_KEY"
```

```ts
const { data } = await hdev.search.meaning({
  q: "diabetes with kidney disease",
  system: "icd10cm",
});
```

```json
{
  "data": [
    {
      "system": "icd10cm",
      "code": "E11.22",
      "display": "Type 2 diabetes mellitus with diabetic chronic kidney disease",
      "status": "active",
      "href": "/v1/codes/icd10cm/E11.22",
      "match_score": 0.8,
      "reason": "matches \"diabetes\", \"kidney\", \"disease\" in the description",
      "did_you_mean": [
        {
          "system": "icd10cm",
          "code": "E10.22",
          "display": "Type 1 diabetes mellitus with diabetic chronic kidney disease"
        }
      ],
      "record": { "code": "E11.22", "billable": true, "...": "..." }
    }
  ],
  "meta": { "source": "cms-icd10", "data_version": "fy2027", "...": "..." },
  "links": { "self": "/v1/search?q=...", "next": "/v1/search?q=...&cursor=..." }
}
```

## What it searches

ICD-10-CM, ICD-10-PCS, HCPCS Level II, RxNorm concepts and FDA NDC products, ranked together.
Pass `system` to search some of them: one (`system=icd10cm`) or several, comma-separated
(`system=rxnorm,ndc`). Retired codes are not searched by meaning; look them up by code.

`meta` describes the datasets searched. When there are several, `meta.sources` lists each one's
version, source and license terms.

## `match_score`

A number from 0 to 1, best first:

| Score      | Means                                                                                        |
| ---------- | -------------------------------------------------------------------------------------------- |
| 1          | The query is exactly the code, RxCUI or NDC. Exact matches always rank first.                |
| 0.9        | The query is the start of the code (`E11.2` finds E11.21, E11.22, E11.29).                   |
| 0.5 to 0.9 | The text contains every query word, closer in meaning ranks higher.                          |
| under 0.5  | Close in meaning but sharing few words, or a weak match. Check these before relying on them. |

Closeness in meaning can raise a result's score above its wording alone, never lower it. Scores
compare results within one search; don't compare them across different queries.

## `reason`

A few words on why each result matched:

- `exact code match`, `code starts with "E11.2"`
- `matches "kidney", "disease" in the description` (drugs: "in the name")
- `close in meaning to "high blood pressure"`: the part of your query the text doesn't say in so
  many words, but means
- `loosely related; check it`

## `did_you_mean`

When the top results are close in score but come from different code families (an ICD-10
category such as E10 versus E11, a HCPCS section, or drugs versus codes), the first result carries
`did_you_mean`: the best match of each other family. "Diabetes with kidney disease" doesn't say
type 1 or type 2, so E11.22 comes with E10.22. Ask your user, or add the missing detail to the
query, rather than picking one.

## Paging, views and fields

Results page like every other search (`limit`, default 25, max 100; follow `links.next`), up to the
best 100 matches per system. `?view=compact` returns `system`, `code`, `display`, `match_score`,
`reason` and `did_you_mean`; `?fields=` picks fields; the default includes the full `record`. Each
search is billed as one call.

## How it works

Every record's description is turned into a numeric fingerprint of its meaning when we load a
release, and stored with that release. Your query gets the same treatment on our servers when you
search, and is compared with the stored fingerprints, then with the words of the text. Nothing is
sent to a third party, results are repeatable for the same query and data version, and a new
release is searchable by meaning as soon as it's published.

## In MCP

The `search_by_meaning` tool calls this endpoint with `view=compact` and returns one line per result
with its score and reason, plus the `did_you_mean` alternatives. Use `search_codes` or
`search_drugs` when you know the official wording; see [Tools](/mcp/tools).
