OHDSI Vocabulary API: Complete Reference Guide

You're usually under pressure when the vocabulary work starts. A source system is already live, the ETL queue is backing up, and someone wants a clean OMOP mapping before the next data refresh. That's the moment the OHDSI vocabulary API stops being a convenience and starts being the difference between building mappings and babysitting infrastructure.
The old workflow is familiar to anyone who has loaded ATHENA locally. Download the files, stand up PostgreSQL, rebuild tables, and then repeat the whole routine when vocabulary content changes. The OHDSI vocabulary ecosystem is large enough that this becomes operational drag fast, and as of March 2023 it contained 8,761,976 valid concepts and 10,574,359 total concepts across 136 vocabularies PMC source. That scale is exactly why programmatic access matters, because teams need the vocabulary service to stay out of the way while pipelines, researchers, and point-of-care applications keep moving.
Why Healthcare Teams Are Moving to Vocabulary APIs
The painful part of self-managed vocabulary work isn't the terminology itself. It's the choreography around it, the downloads, reloads, update checks, and the constant question of whether your local copy still matches the version everyone else is using.
A managed OHDSI vocabulary API shifts that burden into a service boundary. Instead of treating vocabularies as a static dataset, teams query them in real time, which fits ETL, analytics, and clinical applications better than periodic file handling. That matters because OHDSI's standardized vocabularies sit at the reference layer inside OMOP, and the ecosystem spans a very large concept space across many terminologies.
What changes operationally
The shift is practical, not philosophical. A data engineer can test a source code, resolve it to OMOP, and keep the pipeline moving without maintaining a local vocabulary database. That is especially useful when source data arrives continuously, because the vocabulary service becomes part of the workflow rather than a separate maintenance project.
Practical rule: if your team spends more time refreshing vocabulary tables than validating mappings, the process is backwards.
API-first access also helps outside ETL. Researchers can explore concept sets interactively, product teams can resolve codes at runtime, and AI workflows can ground candidate codes against the OMOP source of truth instead of relying on brittle string matching. The result is less duplicated logic, fewer local copies drifting apart, and a clearer way to handle the ambiguity that shows up when one source term can map to several valid concepts.
Understanding the OMOP Vocabulary Data Model
A vocabulary lookup in OMOP looks straightforward until you have to make it work in a pipeline. Each concept carries a meaningless integer concept_id that becomes the primary key in event tables, while the source code itself is not the identifier analytics relies on. The Book of OHDSI lays out that separation clearly. Any vocabulary service has to return both the human-readable code and the surrogate identifier, or ETL logic quickly turns vague at the point where it needs to stay precise.

The tables that matter in practice
A vocabulary API has to carry more than display text. It needs to surface vocabulary_id, domain_id, and relationship metadata so ETL and cohort logic can behave deterministically. In production, a single lookup often depends on concept relationships and context, not just a direct code match.
The model also depends on the split between standard and source concepts. Standard concepts are the analytic target, while source concepts represent what arrived from the source system. The bridge between them lives in relationship data, which is why a vocabulary API that only returns names and codes leaves too much unresolved for serious OMOP work.
| Core object | Why it matters |
|---|---|
| Concept | Holds the code, name, domain, and surrogate key used by OMOP |
| Concept relationship | Connects source and standard concepts, and supports mapping logic |
| Vocabulary | Identifies the terminology family and gives mapping context |
That model creates a production trade-off. A code may look unambiguous in the source system, then map to several valid OMOP concepts once vocabulary context is applied. I treat the surrogate identifier as the stable analytics anchor and the source code as an input token. That mental model avoids a lot of failures where the value looks correct in a UI, then breaks once the ETL has to commit to a concept.
Authentication and Initial Setup
Teams often overcomplicate the first hour and underthink the first test call. Start with a bearer token, a known base URL, and one lookup that proves the service is reachable before you wire it into an application.
A vocabulary API surface like OMOPHub's uses a simple request pattern. The documentation shows a Bearer API key for the REST surface, and the FHIR terminology service accepts OAuth2 client_credentials for Spring Security clients such as HAPI FHIR and EHRbase. The same documentation shows that a single call can resolve a FHIR system URI plus code, or a full CodeableConcept, into the OMOP standard concept and related metadata.
First call setup
A basic connectivity test should be boring. If the service responds cleanly, you know the auth path, the endpoint, and the network path are all working.
curl -X POST "https://api.omophub.com/v1/fhir/resolve" \
-H "Authorization: Bearer oh_your_api_key" \
-H "Content-Type: application/json" \
-d '{"system":"http://snomed.info/sct","code":"44054006","resource_type":"Condition"}'
For FHIR terminology work, the same service family exposes versioned endpoints under /fhir/{r4,r5,r6}/, and the docs also note support for R4, R4B, R5, and R6 on the same endpoint. That matters in mixed enterprise environments where different products do not upgrade at the same pace.
If you use SDKs, keep initialization minimal at first. The Python and R clients are useful when you want to wrap repeated lookups inside ETL jobs, and the MCP server is useful when an AI client needs vocabulary grounding. The point is to make the first successful request easy to reproduce, then verify how the service behaves with the specific code systems and code shapes your pipeline will see in production.
Search and Concept Lookup Patterns
Keyword search is only one way to find a concept, and it is rarely enough on its own. In production, the search path usually needs exact lookup, faceted narrowing, fuzzy tolerance, and a semantic fallback when users only know the clinical idea, not the canonical terminology.
Match the search mode to the job
Use autocomplete in interactive tooling while the user is still typing. Use faceted filters when you already know the vocabulary family or domain and want to cut down noise. Use fuzzy search for typos, and reserve semantic search for cases where the input is closer to a clinical idea than a terminology label.
The OHDSI vocabulary ecosystem is centered on a small set of high-value vocabularies that dominate real workflows, including SNOMED, CPT4, HCPCS, LOINC, and RxNorm. That means search systems need to do more than free-text matching, because the same clinical idea often appears under different naming conventions depending on source context.
Search works best when the interface helps the user narrow the vocabulary family before it tries to be clever.
For hands-on exploration, the concept lookup tool at OMOPHub concept lookup is useful because you can test how a term behaves before you wire it into code. That is the fastest way to see whether you need exact code lookup, broader semantic matching, or a relationship-aware translation path.
Pagination matters once you are dealing with broad searches. Keep result sets small enough to review, and do not fetch more context than the user asked for. In a production UI, the right search pattern reduces both latency and operator fatigue.
Navigating Cross Vocabulary Mapping Ambiguity
Teams usually underestimate this part until the first production mapping review. A source code often has more than one plausible OMOP target, and the work is deciding which relationship fits the analytic goal.
The hard cases show up in the usual places, duplicate names, different mapping granularity, and crosswalks for vocabularies such as RxNorm, LOINC, and SNOMED. That pattern comes up repeatedly in the OHDSI forum, which is a good reminder that the problem is not access to terminology, it is choosing a mapping that can stand up in a cohort definition, an exposure table, or a QA review.

How to handle ambiguity without guessing
Start with the source code, then inspect the candidate targets by domain and relationship type. If a code can map to multiple concepts, keep both in view until you know whether the workflow needs a broad analytic bucket or a narrow source-specific target. That choice changes cohort logic, exposure definitions, and quality checks, so the lookup result alone is never enough.
The server-side path matters when you are mapping at scale. The OMOPHub vocabulary service can resolve FHIR-to-OMOP mappings in one call, including Maps to traversal on the server side, and its API supports single, batch, and CodeableConcept variants with vocabulary-preference ranking. The practical pattern is to batch the straightforward cases and send the ambiguous ones through an exception path. For the implementation details behind that flow, see the FHIR terminology server API guide.
Practical rule: when two mappings both look plausible, let the downstream cohort definition decide which one wins, not the first result returned by the lookup.
At scale, API-first access surfaces edge cases faster than manual review. That is irritating during rollout, but it helps because duplicate names and hierarchy mismatches appear before they contaminate ETL logic.
FHIR Terminology Service Operations
FHIR integrators need a terminology service that behaves like a terminology service, not just a code search box. In practice, that means the API has to support the operations teams already expect from FHIR tooling, while still fitting OMOP vocabulary behavior underneath. OMOPHub's FHIR surface exposes $lookup, $validate-code, $translate, $expand, $subsumes, $find-matches, $closure, and the OMOP-specific $diff for release comparison FHIR terminology overview. The same surface is available under /fhir/{r4,r5,r6}/, which matters if your environment has to work across mixed FHIR versions without forcing every client into a separate integration path.
| Operation | Purpose | Common use case |
|---|---|---|
$lookup | Return concept details | Show code metadata to users |
$validate-code | Test membership | Check whether a code belongs in a value set |
$translate | Map between codes | Convert source terminology to OMOP standard concepts |
$expand | Expand a value set | Build an explicit concept list |
$subsumes | Test hierarchy relation | Confirm parent-child relationships |
$find-matches | Profile matching | Support terminology-aware matching workflows |
$closure | Compute transitive closure | Precompute relationship reachability |
$diff | Compare releases | See what changed between vocabulary versions |
FHIR to OMOP resolution in one call
The operational detail that matters most is server-side traversal. The service handles Maps to traversal on the server side, so the client does not need to rebuild terminology logic just to move from a FHIR coding to an OMOP standard concept. That lowers client complexity, but it also changes how you should design your exception handling. A clean implementation sends straightforward cases through the API and routes ambiguous cases into a review path, because a single returned concept is not always the right concept for downstream analytics.
For teams that need the request and response patterns spelled out, the OMOP terminology server patterns guide gives a practical companion view of how to shape those calls. Keep request formats versioned, and test the exact FHIR wire version your client emits, because mismatched assumptions here usually show up as integrations that work in sandbox and fail once production data brings in edge cases.
Traversing Concept Hierarchies and Relationships
Hierarchy traversal is where vocabulary work becomes analytical rather than clerical. Once you're building phenotype definitions or cohort logic, the question is no longer “what is this code?” but “what else belongs with it?”
Ancestors, descendants, and the non-obvious links
Ancestor and descendant traversal are useful when you need broad coverage. Drug safety teams use that to catch all formulations of a medication, while quality teams use it to avoid missing related procedure codes. But the non-hierarchical links matter too, because relationships like Has ingredient or Has procedure context can surface clinically relevant concepts that a simple tree walk would miss.
The challenge is that deep relationship traversal can expand quickly, so you need to define what kind of completeness you want. Inclusive cohorts benefit from broader traversal. Narrow safety checks often need tighter constraints and more explicit relationship filters.
A practical way to keep hierarchy queries sane
Cache relationship data if your use case repeats the same traversals. That's especially useful for phenotype libraries and reusable concept sets, where the same node gets queried over and over during design and review. The main failure mode is letting every request re-discover the same hierarchy, which wastes time and makes latency unpredictable.
If you need a more detailed walkthrough of hierarchy behavior, the companion reference at OMOP concept hierarchy patterns is a useful companion. The working rule is simple, broad traversal finds more codes, but only curated traversal finds the right ones.
Self Hosted ATHENA Versus API Based Access
Self-hosting ATHENA still has a place, but the trade-off is blunt. You get local control, yet you also inherit the setup, the refresh process, and the operational maintenance that come with managing a vocabulary store yourself.
Where the managed model wins
For a typical team, managed access removes the heaviest parts of the workflow. The reference comparison says setup drops from 1–2 days to 5 minutes, vocabulary updates move from manual re-downloads every ~6 months to automatic syncing, and infrastructure cost shifts from $150–400/month for database and compute to a free tier with paid volume tiers OMOPHub ATHENA comparison. Those numbers line up with what most platform teams feel in practice, even before they count engineer time.

The best fit for self-hosting is narrower. Air-gapped environments, proprietary vocabulary extensions, or strict rules against external calls can make local control the only acceptable path. In those cases, a hybrid model works well, develop against an API service, then cache the results for local production use.
Practical rule: self-host when governance forces it, not because the build looks familiar.
For teams that want API access without building the service layer themselves, OMOPHub is one managed option to evaluate alongside a local deployment. The right answer depends on your compliance constraints, your refresh cadence, and how often your analysts need to touch vocabularies directly.
Performance Optimization and Batch Processing
Vocabulary APIs slow down when teams use them like a document store. They stay fast when the client treats them as a low-latency resolution layer, with caching, batching, and a clear fallback path.
Cache the stable, batch the variable
Cache common concepts aggressively, especially when the same source codes show up across ETL jobs or application sessions. Batch requests help when you are resolving many codes at once, and the OMOPHub product docs support batch operations up to 100 codes per request. That is enough to cut chatter without turning every request into a large payload.
Connection pooling and request coalescing matter in production. If a pipeline resolves the same code repeatedly, route the lookup through cache first, then the API, then the fallback path. The usual failure mode is N+1 vocabulary calls hidden inside row-by-row transforms, which makes an otherwise fast service feel slow and noisy under load.
A practical batch strategy also needs to account for ambiguity. One code can map cleanly in one vocabulary and return several plausible candidates in another, so the client should batch for throughput without assuming every response is a final answer. I have seen teams get better results by separating exact lookups from fuzzy search and by logging which branch produced the match, especially when cross-vocabulary mapping changes after a refresh.
What to monitor
Watch repeated misses, not just latency. A high miss rate usually means the source vocabulary is drifting, the mapping rules are too broad, or the client is asking the wrong question. Pagination also matters for concept search, because broad searches can return more candidates than an application can safely display.
Track cache hit rate, response distribution, and the share of queries that fall back to manual review. Those signals tell you whether the integration is serving stable lookups or forcing users to work through ambiguity at query time. For broader API hardening patterns that sit alongside this operational work, how to secure your Hono API is a useful reference.
The goal is throughput with discipline. Push the obvious lookups through cache, reserve live calls for true resolution work, and keep client-side observability in place so you can see when vocabulary behavior changes after a release.
Security and Compliance for Healthcare APIs
Vocabulary lookup services are easier to govern than clinical data services because they handle terminology codes and concept IDs, not Protected Health Information. That still doesn't make security optional, but it does simplify the risk profile significantly.
Authentication should stay tight. Use Bearer API keys for standard access, and use OAuth2 client_credentials where a FHIR client expects that flow. Transport should stay on TLS 1.2+, and keys should be scopeable and revocable so access can be rotated without touching every application.
Operational controls that actually help
Keep API keys out of source code and rotate them on a schedule your team can enforce. Record access in audit logs, and tie service usage back to workload identity where possible so troubleshooting doesn't turn into detective work. If you need a broader view of API hardening practices, how to secure your Hono API is a useful general reference for the control patterns that also apply here.
Compliance leaders usually worry about what crosses the wire. In this case, the vocabulary service pattern keeps the payload limited to codes, concept IDs, and search terms, which makes it much easier to align with internal security review than a system that processes clinical narratives or patient records.
Quick Reference and Common Integration Patterns
A vocabulary API earns its keep when the team can answer a simple question fast, then move on to the harder one. The easy part is finding the code. The hard part is deciding which vocabulary, mapping, or relationship is defensible in a production pipeline when the same source code can land in more than one standard path.

Common integration patterns
- Standard API calls: use them for source-to-standard resolution in ETL and application services.
- REST endpoints: use them when you want simple, debuggable request flows and SDK-friendly wrappers.
- Authentication setup: keep the bearer token path and secret storage clean from day one.
- Troubleshooting: check the vocabulary ID, the code system, and the request shape before escalating.
In practice, the cleanest integrations separate lookup from decisioning. The API can return several plausible matches, especially where cross-vocabulary mappings overlap or where a local source code has been modeled differently across feeds, so the application still needs a rule for which result wins. I usually keep that rule in the ETL layer or a dedicated terminology service, not inside scattered application code.
Endpoints worth memorizing
| Endpoint | What it's for |
|---|---|
/concept | Fetch concept details |
/concept/relationship | Inspect mapping or hierarchy relationships |
/vocabulary | Review vocabulary metadata |
/concept/search | Find concepts by name, code, or relevance |
These are the calls that show up most often during implementation reviews. /concept helps validate what the pipeline resolved, /concept/relationship is the one to check when a mapping looks too broad or too narrow, and /vocabulary is where you confirm which vocabulary family a code belongs to before you chase a phantom data issue.
What usually breaks first
- Check API key: expired or mis-scoped keys are a common first failure.
- Use the correct
vocabulary_id: a code in the wrong vocabulary often looks like a missing record. - Handle rate limits: batch where you can, cache where you should, and retry carefully.
The failures are usually boring, which is why they waste time. A lookup that returns nothing is often a vocabulary mismatch, not a broken endpoint. A mapping that returns something plausible can be worse, because it may pass validation and still send downstream analytics down the wrong branch.
For SDK work, the Python client is available at OMOPHub Python SDK, the R client at OMOPHub R SDK, and the MCP server at OMOPHub MCP Server. When you want to sanity check a code before it lands in ETL, the concept lookup tool is a practical fallback.


