Documentation Index
Fetch the complete documentation index at: https://docs.omophub.com/llms.txt
Use this file to discover all available pages before exploring further.
Basic Search
Search for concepts by text. The SDK normalises three possible server response shapes ({ concepts: [...] }, legacy { data: [...] }, bare Concept[]) into a stable SearchResult - data.concepts is always a Concept[]:
const { data, error } = await client.search.basic('diabetes mellitus', {
pageSize: 20,
});
if (error) throw new Error(error.message);
for (const concept of data.concepts) {
console.log(`${concept.concept_id}: ${concept.concept_name}`);
}
Parameters
| Parameter | Type | Default | Description |
|---|
query (positional) | string | required | Search query string |
vocabularyIds | string[] | - | Filter by vocabulary IDs |
domainIds | string[] | - | Filter by domain IDs |
conceptClassIds | string[] | - | Filter by concept class IDs |
standardConcept | 'S' | 'C' | 'N' | - | Filter by standard concept flag |
includeSynonyms | boolean | false | Search synonyms too |
includeInvalid | boolean | false | Include invalid concepts |
minScore | number | - | Minimum relevance score (0.0–1.0) |
exactMatch | boolean | false | Require exact match |
page | number | 1 | Page number (1-based) |
pageSize | number | 20 | Results per page |
sortBy | 'relevance' | 'name' | 'code' | 'date' | - | Sort field |
sortOrder | 'asc' | 'desc' | - | Sort order |
Filter Combinations
// By vocabulary
await client.search.basic('heart attack', {
vocabularyIds: ['SNOMED', 'ICD10CM'],
});
// By domain
await client.search.basic('aspirin', {
domainIds: ['Drug'],
pageSize: 10,
});
// Standard concepts only
await client.search.basic('diabetes', {
standardConcept: 'S',
vocabularyIds: ['SNOMED'],
});
// Combined
await client.search.basic('myocardial infarction', {
vocabularyIds: ['SNOMED'],
domainIds: ['Condition'],
standardConcept: 'S',
includeSynonyms: true,
minScore: 0.5,
pageSize: 20,
});
Autocomplete
Get suggestions as the user types. The response is { query, suggestions: [...] } - each entry nests the concept under suggestion:
const { data } = await client.search.autocomplete('diab', { pageSize: 10 });
console.log(data?.query); // "diab" (echoed)
for (const entry of data?.suggestions ?? []) {
console.log(entry.suggestion.concept_name);
console.log(entry.match_score); // optional scoring field
}
const first = await client.search.basic('diabetes', { page: 1, pageSize: 50 });
const pagination = first.meta?.pagination;
console.log(`Total: ${pagination?.total_items}`);
console.log(`Pages: ${pagination?.total_pages}`);
console.log(`Has next: ${pagination?.has_next}`);
if (pagination?.has_next) {
const second = await client.search.basic('diabetes', { page: 2, pageSize: 50 });
}
basicIter is an async generator that walks every page and yields one concept at a time:
let count = 0;
for await (const concept of client.search.basicIter('diabetes', { pageSize: 100 })) {
console.log(concept.concept_name);
count++;
if (count >= 500) break;
}
Async iterators throw OMOPHubIteratorError on page failure (generators can’t gracefully yield discriminated errors). Use the eager basicAll(...) variant if you prefer accumulating errors as values. See Error Handling.
Eager Collect
const { data, errors, pagesFetched } = await client.search.basicAll('diabetes', {
pageSize: 100,
maxPages: 10,
});
console.log(`Collected ${data.length} concepts across ${pagesFetched} pages`);
if (errors.length > 0) console.warn('Partial result:', errors);
Advanced Search
POST-based search with relationship filters:
const { data } = await client.search.advanced('diabetes', {
vocabularyIds: ['SNOMED', 'ICD10CM'],
domainIds: ['Condition'],
standardConceptsOnly: true,
pageSize: 50,
});
for (const concept of data?.concepts ?? []) {
console.log(`${concept.concept_id}: ${concept.concept_name}`);
}
Advanced Search Parameters
| Parameter | Type | Default | Description |
|---|
query (positional) | string | required | Search query string |
vocabularyIds | string[] | - | Filter by vocabulary IDs |
domainIds | string[] | - | Filter by domain IDs |
conceptClassIds | string[] | - | Filter by concept class IDs |
standardConceptsOnly | boolean | false | Only standard concepts |
includeInvalid | boolean | false | Include invalid concepts |
relationshipFilters | RelationshipFilter[] | - | Relationship-based filters |
page | number | 1 | Page number |
pageSize | number | 20 | Results per page |
Relationship Filters
await client.search.advanced('diabetes', {
relationshipFilters: [
{ relationshipId: 'Is a', targetConceptId: 4116142 },
],
standardConceptsOnly: true,
});
Semantic Search
Embedding-based natural-language search. The SDK normalises the response so data.results is always a populated array:
const { data } = await client.search.semantic('heart attack', { pageSize: 10 });
for (const concept of data?.results ?? []) {
console.log(`${concept.concept_name}: ${concept.similarity_score.toFixed(2)}`);
}
With filters:
const filtered = await client.search.semantic('diabetes mellitus', {
vocabularyIds: ['SNOMED'],
domainIds: ['Condition'],
threshold: 0.5,
pageSize: 20,
});
Semantic Search Parameters
| Parameter | Type | Default | Description |
|---|
query (positional) | string | required | Natural language query |
vocabularyIds | string[] | - | Filter by vocabularies |
domainIds | string[] | - | Filter by domains |
standardConcept | 'S' | 'C' | 'N' | - | Filter by standard flag |
conceptClassId | string | - | Filter by concept class |
threshold | number | 0.5 | Minimum similarity (0–1) |
page | number | 1 | Page number |
pageSize | number | 20 | Results per page (max 100) |
Semantic Search Iterator
for await (const concept of client.search.semanticIter('diabetes', { pageSize: 50 })) {
console.log(concept.concept_name);
}
// Collect with bounds
const { data } = await client.search.semanticAll('heart failure', {
vocabularyIds: ['SNOMED'],
threshold: 0.4,
maxPages: 5,
});
Bulk Lexical Search
Run up to 50 keyword searches in a single API call:
The per-search entries (BulkBasicSearchInput) use snake_case keys (search_id, vocabulary_ids, domain_ids, page_size) — they’re a direct pass-through of the API payload, not a camelCase options object. The outer defaults object follows the same convention. This is the only place in the SDK where snake_case appears at the TypeScript surface.
const { data } = await client.search.bulkBasic(
[
{ search_id: 'q1', query: 'diabetes mellitus' },
{ search_id: 'q2', query: 'hypertension' },
{ search_id: 'q3', query: 'aspirin' },
],
{ defaults: { vocabulary_ids: ['SNOMED'], page_size: 5 } },
);
for (const item of data?.results ?? []) {
console.log(`${item.search_id}: ${item.results.length} results (${item.status})`);
}
Bulk Basic Parameters
| Parameter | Type | Description |
|---|
searches (positional) | BulkBasicSearchInput[] | 1–50 search inputs |
searches[].search_id | string | Unique correlation ID echoed in results |
searches[].query | string | Search query |
searches[].vocabulary_ids | string[] | Per-search vocabulary filter |
searches[].domain_ids | string[] | Per-search domain filter |
searches[].page_size | number | Per-search result limit (1–100) |
defaults | BulkSearchDefaults | Shared defaults - per-search values override |
Bulk Semantic Search
Up to 25 semantic searches per call:
const { data } = await client.search.bulkSemantic(
[
{ search_id: 's1', query: 'heart failure treatment options' },
{ search_id: 's2', query: 'type 2 diabetes medication' },
{ search_id: 's3', query: 'elevated blood pressure' },
],
{ defaults: { threshold: 0.5, page_size: 10 } },
);
for (const item of data?.results ?? []) {
console.log(`${item.search_id}: ${item.results.length} results`);
for (const concept of item.results) {
console.log(` ${concept.concept_name} (${concept.similarity_score.toFixed(2)})`);
}
}
Find Similar Concepts
Find concepts similar to a reference. Provide exactly one of conceptId, conceptName, or query. The discriminated union enforces this at the type level; a runtime check defends against JS callers:
// By concept ID
const byId = await client.search.similar({ conceptId: 4329847 }); // MI
// By concept name
const byName = await client.search.similar({ conceptName: 'Type 2 diabetes mellitus' });
// By natural language query
const byQuery = await client.search.similar({ query: 'elevated blood sugar' });
// With all options
const { data } = await client.search.similar({
conceptId: 4329847,
algorithm: 'semantic',
similarityThreshold: 0.7,
pageSize: 50,
vocabularyIds: ['SNOMED'],
domainIds: ['Condition'],
includeScores: true,
includeExplanations: true,
});
for (const concept of data?.similar_concepts ?? []) {
console.log(`${concept.concept_name}: ${concept.similarity_score.toFixed(2)}`);
}
similar() uses a two-arg signature similar(options, requestOptions?) - its query XOR variant would otherwise collide with PerCallOptions.query (the escape-hatch params record). Pass signal, headers, or idempotencyKey via the second argument.
Similar Concepts Parameters
| Parameter | Type | Default | Description |
|---|
conceptId | number | - | Source concept ID (XOR with conceptName/query) |
conceptName | string | - | Source concept name |
query | string | - | Natural-language query |
algorithm | 'semantic' | 'lexical' | 'hybrid' | 'hybrid' | Matching algorithm |
similarityThreshold | number | 0.7 | Minimum similarity (0–1) |
pageSize | number | 20 | Max results (server caps at 1000) |
vocabularyIds | string[] | - | Filter by vocabulary |
domainIds | string[] | - | Filter by domain |
standardConcept | 'S' | 'C' | 'N' | - | Standard concept filter |
includeInvalid | boolean | - | Include invalid/deprecated |
includeScores | boolean | - | Include detailed scoring |
includeExplanations | boolean | - | Include similarity explanations |
Algorithm Comparison
| Algorithm | Best For | Speed |
|---|
semantic | Meaning-based similarity | Slower |
lexical | Text/string similarity | Faster |
hybrid | Balanced approach | Medium |