Back to Blog

OMOP ETL Vocabulary Mapping: A Practical End-to-End Guide

Dr. Lisa MartinezDr. Lisa Martinez
August 8, 2026
14 min read
OMOP ETL Vocabulary Mapping: A Practical End-to-End Guide

You can get a CDM schema stood up, staging tables loaded, and validation mostly green, then still find the project stuck on one ugly reality. Every source code has to become a standard concept_id before the dataset is usable, and the messy part isn't the load, it's the vocabulary work that follows.

That's where a lot of OMOP ETL projects slow down. Teams treat mapping like a cleanup step, but the mapping layer is what decides whether your cohorts are reproducible, your domain tables are coherent, and your reruns survive the next vocabulary release. OMOP's own ETL guidance makes the point plainly, vocabulary mapping is not side work, it's part of the design pattern for making source data computable across domains and releases, with direct matching only when source formatting aligns and explicit normalization when it doesn't (OMOP CDM ETL Tutorial).

A diagram illustrating the complex process of mapping various medical source vocabularies into a single standardized OMOP concept ID.

Why OMOP ETL Vocabulary Mapping Is the Hard Part

The failure usually shows up in a mundane review. The load finished, the CDM tables are in place, and someone asks why a diagnosis table still carries source codes that do not line up with the phenotype logic. At that point the issue is clear. OMOP ETL vocabulary mapping is the load-bearing layer, not a cleanup task you handle after the main work is done.

The failure mode is usually not the obvious one

The common mistakes are rarely dramatic. A code string includes extra punctuation, a local value is built from multiple fields, or a free-text field looks like a code but is not one. OHDSI's source vocabulary mapping guidance calls out this kind of ambiguity, including the need to define codes from concatenated fields, exclude junk or meaningless terms, and run source-consistency testing before automated matching (Zhuk source vocabulary mapping).

Practical rule: if the source value has not been normalized, lookup quality is already compromised before the first mapping rule runs.

That matters because downstream cohort logic assumes source-to-standard translation stays stable. Source systems drift, feeds get reformatted, and last year's spreadsheet often becomes this year's silent failure. When that happens, the breakage shows up in the wrong place, a missing SOURCE_CONCEPT_ID, an unmapped code in a high-volume table, or a mapping that lands in the wrong domain after a vocabulary refresh.

The three pillars that keep the work sane

Three core components have to work together. The ATHENA vocabularies serve as the reference set, a durable persistence layer such as SOURCE_TO_CONCEPT_MAP holds mappings for codes outside the standard set, and a programmatic interface keeps the work from depending on one analyst clicking through a spreadsheet. The OMOP conventions treat those pieces as core infrastructure, not optional extras, and the ETL tutorial shows how they fit into the pipeline design (OMOP CDM v5.3.1 conventions, OMOP CDM ETL Tutorial).

That is why teams that try to finish mapping later usually pay twice. They build the ETL once, then they rebuild the vocabulary layer after users find missing concepts or inconsistent semantics. A spreadsheet can help during review, but it does not carry version history, release drift checks, or repeatable audit logic. A versioned mapping workflow does.

Scoping Your Vocabulary Mapping Project

The scoping mistake is easy to spot after the fact. Teams start mapping before they know which code systems are present, which domains matter, and which fields are codes versus loosely structured text. A one-page scope document prevents that mess because it forces the team to inventory source systems before they open Usagi or write the first SQL join.

A four-step infographic illustrating the process for scoping a clinical vocabulary mapping project for data standardization.

Start with source profiling, not mapping

Inventory every distinct vocabulary that appears in staging. That includes standard terminologies like ICD-10-CM, SNOMED CT, LOINC, RxNorm, HCPCS, and NDC, plus local codes and any free-text fields that will need manual curation or NLP. If your staging tables contain mixed formatting, do not assume the source value can be matched directly. OMOP conventions only allow direct matching when the source format aligns with the distributed vocabulary, otherwise the ETL has to normalize it first.

A good scoping pass answers four questions:

  • What source systems exist? List each feed, file type, and upstream application.
  • Which code systems appear? Separate standardized vocabularies from local or proprietary values.
  • Which clinical domains are in play? Conditions, drugs, measurements, procedures, visits, and observations do not fail in the same way.
  • Which fields are not really codes? Free text, comments, and concatenated strings need a different path.

If a field mixes structured codes with narrative text, treat it as a data-quality problem first and a vocabulary problem second.

Know where standard concepts belong

The OMOP convention is specific here. When a source value references a standardized coding scheme, ETL should find the corresponding CONCEPT_IDs and store them in SOURCE_CONCEPT_ID. If the source format differs from the distributed vocabulary, transformation logic belongs in ETL. If the source value sits outside the Standardized Vocabularies, it should be mapped directly to standard concept IDs and stored in SOURCE_TO_CONCEPT_MAP.

That distinction changes ownership. Some mappings are operational lookups, some are durable transformation rules, and some are governance decisions that need review. If you write those cases into one bucket, the table becomes impossible to reason about later.

A clean scoping document should end with a decision on each source column. Match directly, normalize then match, send to manual review, or route to a persistent source-to-concept table. That is the project plan, not a vague promise to standardize codes.

Source-to-Target Mapping Strategies That Scale

The fastest teams do not start by inventing mappings from scratch. They first ask whether OHDSI already did the work, because the vocabulary ecosystem already contains a lot of reusable structure. OHDSI's FAQ says mappings for supported source terminologies are available in Concept_relationship, and that source terminology values get non-standard concept IDs before being mapped to standard ones, with mappings already done for data using any of the 55 supported source vocabularies (OHDSI FAQ).

A diagram illustrating three strategies for source-to-target data mapping, ranging from direct matching to expert review.

Use the simplest path that preserves meaning

Direct matching is the cleanest case. If your source code formatting lines up with the distributed vocabulary, normalize the text and join it directly. That is the only case where a deterministic lookup should feel boring, and boring is good.

The next tier is pattern-based transformation. A local lab code, a delimiter-heavy claim code, or a source system that pads values inconsistently often needs a rule before it can be matched. The ETL logic matters more than the vocabulary browser in this scenario, because the transformation is really about source normalization, not lookup.

Treat ambiguous mappings as clinical work

The long tail belongs in expert review. Usagi is useful here because it supports a workflow where source codes are loaded, suggested mappings are reviewed by people with medical expertise, and the results are exported into SOURCE_TO_CONCEPT_MAP as a reusable artifact. That makes the output auditable instead of ephemeral.

OHDSI's mapping semantics also matter. The GIS vocabulary guidance defines mapping records with relationship types such as Maps to or Is a, SSSOM-aligned predicates like skos:exactMatch, skos:narrowMatch, skos:broadMatch, and skos:relatedMatch, plus an author-assigned confidence score from 0.0 to 1.0 (OHDSI GIS vocabulary). Those choices are not cosmetic, because exactMatch and broadMatch do very different things to phenotype sensitivity.

Do not let a loose relationship type into a production phenotype. If the semantics widen, the cohort changes.

A practical decision tree is straightforward. Query Concept_relationship first, normalize the source value if needed, send uncertain rows to expert review, and persist any out-of-vocabulary value in SOURCE_TO_CONCEPT_MAP with relationship metadata and reviewer notes. That gives the ETL a predictable fallback path instead of a pile of ad hoc exceptions. For teams that want to avoid brittle, local vocabulary setup, the OMOPHub ATHENA comparison note shows why an API-first vocabulary workflow can cut setup time without giving up auditability.

ATHENA Vocabularies, OMOPHub, and the Hybrid Pattern

ATHENA is the vocabulary backbone most OMOP teams already know. It's the standardized vocabulary package OHDSI ships, and the conventional self-hosted path means downloading the vocabulary files, loading them locally, and repeating that maintenance whenever a new release lands. The trade-off is obvious, the local setup gives you full control, but it also gives you more moving parts to maintain.

Self-hosted or API-first changes the whole workflow

The self-hosted route still makes sense in regulated or air-gapped environments. It also makes sense if you need custom proprietary extensions that will never exist in the shared vocabulary set. But for most engineering teams, the friction isn't philosophical, it's operational, because you end up spending time on re-downloads, reloads, and local search tooling instead of mapping clinical data. For a practical overview of that trade-off, see OMOPHub's ATHENA comparison note.

OMOPHub takes the opposite approach and exposes the vocabulary set through REST, FHIR, Python, R, and MCP interfaces. That means the mapping workflow can be programmatic from the beginning, rather than anchored to a local database and a batch refresh schedule. It's the kind of setup that lets ETL developers prototype quickly, then decide what to cache locally for production.

A simple comparison helps the architecture decision

CapabilitySelf-hosted ATHENAAPI-first workflow
SetupLocal download and loader workAPI key and immediate access
UpdatesManual reloadsSynchronized with release updates
SearchYou build itAvailable through API surfaces
FHIR terminology supportYou wire it togetherExposed as a service
MaintenanceOngoing local opsMuch lighter operational burden

That table is not about hype. It's about choosing where you want your team to carry complexity.

The hybrid pattern is what I usually recommend. Develop and validate mappings against an API-backed vocabulary service so analysts and engineers can move fast, then cache the approved outputs locally for production ETL and auditability. That gives you the speed of a managed service without giving up the reproducibility you need once the pipeline is live.

Programmatic Mapping With Python, R, and SQL

Once the scope is clear, the workflow should stop feeling artisanal. Mapping needs to become a repeatable data operation, not a batch of hand-edited notes in a shared drive. A clean implementation starts with one request that resolves a source code to the OMOP standard concept and the target CDM table, then expands from there to batch processing and durable lineage storage.

Resolve one code, then build the batch path

A SNOMED Condition code can be resolved with a single REST call, including the FHIR system URI, the code, and the resource type:

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"}'

That same workflow is also exposed through the Python SDK, the R SDK, and the MCP server, which is useful when you want ETL automation in one stack and analyst-friendly exploration in another. The repository entry points are the Python SDK, the R SDK, and the MCP server, with the broader vocabulary mapping guidance documented in OMOPHub's vocabulary SDK note.

For batch work, keep the request shape consistent and cap each payload to the service limit. Use single-code requests for QA, then move to batch resolution once you trust the source normalization rules. FHIR CodeableConcept resolution is especially useful in EHR pipelines where the source record carries more context than a bare code string.

Store lineage like you expect to debug it later

This is the part teams skip and later regret. Every mapping row should capture the original system, code, display text, target concept_id, mapping status, vocabulary version, and a short transformation note. That way a later rerun does not require archeology.

A lightweight SQL pattern looks like this:

INSERT INTO source_to_concept_map_work
    (source_system, source_code, source_display, concept_id,
     mapping_status, vocabulary_version, mapping_note)
SELECT
    s.system,
    s.code,
    s.display_text,
    r.concept_id,
    r.status,
    r.vocabulary_version,
    r.note
FROM staging_codes s
JOIN cached_vocab_responses r
  ON s.system = r.system
 AND s.code = r.code;

The important part is not the syntax, it's the discipline. If a vocabulary release changes a mapping, the table should already tell you what changed, when, and why.

Make the mapping table explain itself. Future you should be able to rerun ETL without guessing which spreadsheet row mattered.

Versioning, Audit Trails, and Release Drift

Vocabulary mapping falls apart the moment teams treat it like a one-time spreadsheet task. It is a versioned data product, and the version belongs in the ETL manifest beside the source extract date and the CDM build number. Rerun the same pipeline months later against a newer vocabulary without noticing, and you can change concept IDs, break reproducibility, and invalidate study logic.

A timeline diagram illustrating the lifecycle of ETL versioning, audit trails, and managing potential release drift.

Pin the vocabulary, then compare against the next release

The safest rule is direct. Pin the ATHENA version in the ETL manifest and keep it fixed for the build. If you want to know whether a newer release changes the mapping layer, compare it on purpose instead of finding out during a production rerun. OMOPHub's FHIR $diff operation is built for release comparison, so teams can check local mappings against the latest vocabulary release before they ship a rerun.

A small CI job can do most of the work. It can diff the cached mapping table against the newest vocabulary release, flag concept changes, and surface stale rows for review. That catches release drift before it reaches downstream analysis, where cleanup is slower and more expensive.

Audit fields are part of the product, not the paperwork

Every durable mapping row should carry the vocabulary version, mapping date, reviewer, confidence score, and predicate type. The original OMOP vocabulary tables structure helps here, because the audit record should line up with the vocabulary objects you depend on. A row that only says “mapped” is not enough when you need to explain why a concept changed or why a reviewer accepted a borderline match.

A useful audit shape is easy to define:

  • Vocabulary version: the exact release used for the mapping.
  • Mapping date: when the decision was finalized.
  • Reviewer: who approved the row.
  • Confidence score: recorded in the OHDSI GIS style from 0.0 to 1.0.
  • Predicate type: the semantic relationship, not a generic mapped flag.

That same audit discipline should support the release workflow. In OHDSI practice, GIS vocabulary records carry validation metadata, status, reviewer feedback, confidence score, and predicate type, which makes the mapping traceable instead of opaque. See the OMOP CDM vocabulary tables overview when you need to line up those fields with the underlying tables, and keep the rest of the provenance in the manifest and mapping table. When the pipeline knows its version, the build is reproducible. When it does not, the best you can do is reconstruct a story after the fact.

QA, Validation, and the Source Ambiguity Trap

The hardest QA bugs are the ones that look like mapping problems but start as source problems. A source feed with concatenated fields, junk values, or free-text codes that only resemble real terminology can defeat direct matching long before the vocabulary logic is wrong. That's why source ambiguity has to be treated as a first-class ETL risk.

Put source-consistency checks before approval

OHDSI's mapping guidance already points to the right habit, define codes from concatenated fields, filter out meaningless terms, and validate source consistency before automated matching (Zhuk source vocabulary mapping). In practice, that means the review queue should not only answer “what concept is this,” but also “is this value even a valid code?”

Runtime checks should be strict. A required row should not pass with a null CONCEPT_ID, a non-standard source code should not masquerade as a standard one, and each domain should have its own mapping expectations. If a source table is supposed to be clinically structured, an explosion in unmapped rows is a release problem, not an analyst inconvenience.

Use the dashboard as a gate, not a report

A real QA dashboard should show a few blunt things and nothing ornamental:

  • Mapping rate by domain so the highest-risk tables stand out.
  • Top unmapped codes so reviewers know where the waste is.
  • Stale mappings older than the latest vocabulary release.
  • Reviewer backlog so the queue doesn't disappear into email.

That dashboard should stop a release, not decorate a steering committee slide. If the queue is full of ambiguous source rows, the problem is still open, no matter how polished the ETL summary looks.

A good QA process doesn't prove the mapping is perfect. It proves the team knows where the weak spots are before they ship.


If you want to make OMOP ETL vocabulary mapping faster without losing auditability, use OMOPHub as the vocabulary layer you query, review, and version against, then cache the approved mappings in your own pipeline. Visit OMOPHub and use it as the working surface for code resolution, vocabulary lookups, and release comparison while you keep your ETL controls local.

Share: