Skip to main content

Overview

This endpoint identifies medical concepts that are semantically similar to a provided query or set of criteria. It leverages advanced machine learning models trained on medical terminology to discover related concepts that may not share exact keywords but are clinically relevant and contextually similar.

Request Body

integer
Reference concept to find similar concepts for. Its name becomes the search text, and the concept itself is excluded from the results unless exclude_self is false.
string
Reference concept name, when you have the name but not the ID
string
Free-text query or concept description to find similar concepts for
Exactly one starting point is required: concept_id, concept_name or query. A request with none of them returns 400, and so does a request with more than one — supplying two used to be accepted, with the extra silently discarded. Vocabulary and domain filters narrow a search; they are not starting points, so a request carrying only filters is also a 400.
string[]
Target vocabularies to search within (array of strings)
Examples: ["SNOMED", "ICD10CM"], ["RxNorm", "NDC"]
string[]
Clinical domains to focus the similarity search (array of strings)
Examples: ["Condition", "Procedure"], ["Drug", "Device"]
string[]
Concept classes to include in similarity search (array of strings)
Examples: ["Clinical Finding", "Procedure"], ["Ingredient", "Brand Name"]
number
default:"0.7"
Minimum similarity score threshold (0 to 1.0)
Higher values = More strict similarity matching
0 is a valid value and is honoured: it returns every candidate the retrieval produced, ranked by score.
integer
default:"20"
Number of similar concepts to return per page
Every page size is served from the same fixed candidate pool, so this changes how results are sliced, never how they are scored - see Pagination for the bound on how deep paging can go.
boolean
default:"true"
Include similarity_score on each returned concept. Set false to omit it; ranking is unaffected.
boolean
default:"false"
Add an explanation field to each returned concept describing the evidence behind its score.
For one release each concept also carries a duplicate similarity_explanation field with the same value. It is deprecated and exists only so clients that read the old name keep working; read explanation.
string
Filter by standard concept designation
Options: S (standard), C (classification), N (non-standard)
OMOP stores non-standard concepts with a null standard_concept; N selects those rows.
boolean
default:"false"
Include invalid/deprecated concepts in the similarity search.
Supported only with algorithm=lexical. The embedding index contains valid concepts only, so include_invalid=true with semantic or hybrid returns 400 rather than silently ignoring the filter.
boolean
default:"true"
Exclude the reference concept from its own results. Applies when the request started from a concept_id.
string
default:"semantic"
Similarity algorithm to use
Options:
  • semantic (default) - Neural embedding similarity. Best for finding conceptually similar terms that share no words (e.g. “heart attack” → “Myocardial infarction”). Returns 503 if the embedding service is unavailable.
  • lexical - Text-based Jaccard word and character similarity. Good for fuzzy text matching and typo tolerance, and the only algorithm that can search invalid concepts.
  • hybrid - Runs both retrievals, unions their candidates and scores every one of them by the same rule: 0.7 × semantic + 0.3 × lexical, where a signal neither retrieval established counts as 0. Note that this puts hybrid scores on a different scale from both semantic and lexical scores — a candidate sharing no words with the query keeps only 0.7 × its semantic score, and one the embedding search did not return keeps only 0.3 × its lexical score — so the same similarity_threshold is stricter here than in either single-signal algorithm. See Algorithm Comparison. If the embedding service is unavailable, hybrid serves its lexical half and reports algorithm_used: "lexical" with degraded_from: "hybrid" rather than failing.
integer
default:"1"
Page number (1-based indexing)

Response

object
Request, release, and pagination metadata.
boolean
Indicates if the request was successful
object
Contains the similar concepts search results

Usage Examples

Find concepts similar to a medical condition:

Cross-Vocabulary Similarity

Find similar concepts across multiple vocabularies:

Pharmacological Similarity

Find similar drug concepts with detailed scoring:

Semantic Search for Clinical Terms

Use neural embeddings to find conceptually similar terms (even when words don’t match):
This will find “Myocardial infarction” and related concepts even though the words “heart” and “attack” don’t appear in the medical term.

Algorithm Comparison

hybrid scores are not on the same scale as semantic scores, so the same similarity_threshold is not equivalent between them.Every candidate is scored by the same rule, with a signal neither retrieval established counting as 0. That cuts both ways.A concept that shares no words with the query keeps only 0.7 x its semantic score: “Myocardial infarction” scoring 0.85 semantically against “heart attack” fuses to 0.7 x 0.85 = 0.595 — below the default threshold of 0.7, which semantic would have passed comfortably.A concept the embedding search did not return keeps only 0.3 x its lexical score, so it cannot exceed 0.3 at all. An exact name or code match with no semantic evidence scores 0.3 x 1.0 = 0.3 and is filtered by any threshold above that. Use lexical when you want those matches to rank on their own — hybrid is not a superset of the other two algorithms.When totals_are_lower_bound is true, a hybrid score is itself a lower bound: the semantic retrieval filled its pool, so a candidate it did not return may have had real semantic evidence that was counted as 0. The score is still stable — it does not change with page or page_size — but it can understate.The consequence: at the default threshold, hybrid returns few cross-wording matches, because clearing 0.7 with no lexical overlap requires a semantic score of 1.0. Ranking within a hybrid result is unaffected — semantic ordering is preserved among candidates with equally little overlap — but the absolute scores are compressed. Lower similarity_threshold for hybrid, or use semantic when you want conceptual matches that share no vocabulary.
Counts are pool-relative. total_candidates and pagination.total_items count the concepts that cleared similarity_threshold inside a bounded retrieval pool — not the concepts evaluated, and not the corpus. When search_metadata.totals_are_lower_bound is true, treat them as “at least this many”. has_next remains reliable for paging: it describes the ranked pool being served and goes false at the end of it.

Pagination

Every algorithm ranks a bounded candidate pool and serves pages out of it. The pool is a constant 500 candidates - it depends on neither page nor page_size. Every page of a given request therefore ranks exactly the same candidates, which is what makes consecutive pages disjoint slices of one stable ordering rather than independently-ranked result sets that can repeat or skip concepts. It also means a concept’s similarity_score is a property of the concept and the query: asking for a different page_size cannot change it. Two consequences worth planning for:
  • has_next describes the ranked pool that was served, and nothing beyond it. Paging while has_next is true always terminates.
  • Reachable depth is bounded at roughly 500 / page_size pages. 500 is what the vector index can return in one ordered pass, so the pool cannot be enlarged by asking for a bigger page. Asking for a page past the end returns an empty similar_concepts array with has_next: false — not an error.
When the retrieval filled its pool, there are matching concepts it did not consider, so total_candidates and pagination.total_items count what qualified inside that pool rather than the corpus. search_metadata.totals_are_lower_bound says when that is the case. It is reported separately from has_next on purpose: whether more pages of this ranking exist and whether more matches exist are different questions, and answering the first with the second produced responses that promised a next page forever. To search deeper, narrow the query with filters rather than paging further.