---
name: Omop Hub
description: Use when building FHIR-to-OMOP ETL pipelines, resolving clinical codes to standard OMOP concepts, searching medical vocabularies, mapping between coding systems, or grounding AI agents with verified medical terminology. Essential for healthcare data standardization, phenotype development, and vocabulary-driven clinical applications.
metadata:
    mintlify-proj: omophub
    version: "1.0"
---

# OMOPHub Skill Reference

## Product Summary

OMOPHub is a REST API for OHDSI ATHENA medical vocabularies—11M+ standardized OMOP concepts across SNOMED CT, ICD-10, LOINC, RxNorm, and 100+ terminologies. No local database setup required. Get an API key from [dashboard.omophub.com](https://dashboard.omophub.com) and start querying immediately.

**Key endpoints:**
- REST API: `https://api.omophub.com/v1`
- FHIR Terminology Service: `https://fhir.omophub.com/fhir/r4` (also r4b, r5, r6)
- Authentication: Bearer token in `Authorization` header (`oh_xxxxxxxxx` format)

**SDKs:** Python (`pip install omophub`), R (`install.packages("omophub")`), MCP Server (npm)

**Primary docs:** https://docs.omophub.com

## When to Use

Reach for OMOPHub when:

- **Building FHIR-to-OMOP ETL pipelines** — resolve FHIR CodeableConcepts to OMOP standard concepts and CDM target tables in a single call
- **Mapping source codes to standard vocabularies** — translate ICD-10, local codes, or proprietary systems to SNOMED, LOINC, RxNorm
- **Searching medical vocabularies** — find concepts by keyword, semantic meaning, or code lookup across 100+ terminologies
- **Traversing concept hierarchies** — expand concept sets by walking ancestor/descendant trees for phenotype definitions
- **Integrating with FHIR infrastructure** — HAPI FHIR, EHRbase, or SMART on FHIR apps need a conformant terminology service
- **Grounding AI agents** — eliminate hallucinated medical codes by anchoring LLM outputs to verified OMOP concepts
- **Batch vocabulary operations** — deduplicate and map thousands of codes efficiently with batch endpoints

## Quick Reference

### Authentication

```bash
# Set API key in environment (recommended)
export OMOPHUB_API_KEY="oh_xxxxxxxxx"

# Or pass directly in requests
curl -H "Authorization: Bearer oh_xxxxxxxxx" https://api.omophub.com/v1/vocabularies
```

### Core API Patterns

| Task | Endpoint | Method |
|------|----------|--------|
| Search concepts by keyword | `GET /v1/search/concepts?query=...` | GET |
| Semantic search (by meaning) | `GET /v1/concepts/semantic-search?query=...` | GET |
| Get concept by ID | `GET /v1/concepts/{concept_id}` | GET |
| Get concept by code | `GET /v1/concepts/by-code/{vocabulary}/{code}` | GET |
| Map to target vocabulary | `GET /v1/concepts/{concept_id}/mappings?target_vocabularies=...` | GET |
| Batch map concepts | `POST /v1/concepts/map/batch` | POST |
| Resolve FHIR Coding to OMOP | `POST /v1/fhir/resolve` | POST |
| Batch FHIR resolve | `POST /v1/fhir/resolve/batch` | POST |
| Get ancestors | `GET /v1/concepts/{concept_id}/ancestors?max_levels=...` | GET |
| Get descendants | `GET /v1/concepts/{concept_id}/descendants?max_levels=...` | GET |
| FHIR $lookup | `GET /fhir/r4/CodeSystem/$lookup?system=...&code=...` | GET |
| FHIR $translate | `GET /fhir/r4/ConceptMap/$translate?system=...&code=...&target=...` | GET |

### Python SDK Quick Start

```python
import omophub

client = omophub.OMOPHub()  # reads OMOPHUB_API_KEY from env

# Search
results = client.search.basic("diabetes", page_size=5)
results = client.search.semantic("high blood pressure that comes and goes")

# Get concept
concept = client.concepts.get(201826)

# Map between vocabularies
mappings = client.mappings.get(201826, target_vocabulary="ICD10CM")

# Resolve FHIR code to OMOP
resolved = client.fhir.resolve(
    system="http://snomed.info/sct",
    code="44054006",
    resource_type="Condition"
)

# Navigate hierarchy
ancestors = client.hierarchy.ancestors(201826, max_levels=2)
descendants = client.hierarchy.descendants(201826, max_levels=1)
```

### Vocabulary IDs (Common)

Use exact spelling in API calls:
- `SNOMED` (not SNOMED-CT)
- `ICD10CM` (not ICD-10-CM)
- `LOINC`
- `RXNORM` (not RxNorm)
- `NDC`
- `HCPCS`
- `CVX`
- `ATC`

### Rate Limits & Quotas

- Default: 2 requests/second, monthly quota per plan
- Batch endpoints count as **1 call per batch**, not per item (up to 100 items per batch)
- Response headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
- 429 status = rate limited; respect `Retry-After` header

## Decision Guidance

### When to Use FHIR Resolver vs FHIR Terminology Service

| Scenario | Use | Why |
|----------|-----|-----|
| ETL pipeline: FHIR Coding → OMOP standard concept + CDM table | **FHIR Resolver** (`/v1/fhir/resolve`) | Returns domain, target table, mapping type in one call; OMOPHub JSON envelope |
| HAPI FHIR server needs remote terminology backend | **FHIR Terminology Service** (`/fhir/r4/*`) | Spec-conformant `$lookup`, `$translate`, `$validate-code`, `$expand`; returns FHIR Parameters |
| SMART on FHIR / CDS Hooks app at point of care | **FHIR Resolver** | Faster, returns CDM target table directly |
| Conformance testing or FHIR-native client | **FHIR Terminology Service** | Returns standard FHIR OperationOutcome; client expects FHIR wire format |

### When to Use Search Endpoints

| Query Type | Endpoint | Use When |
|-----------|----------|----------|
| Exact code lookup | `GET /v1/concepts/by-code/{vocab}/{code}` | You know the vocabulary and exact code (e.g., ICD-10 E11.9) |
| Keyword search | `GET /v1/search/concepts?query=...` | User types a term; you want exact keyword matches |
| Semantic search | `GET /v1/concepts/semantic-search?query=...` | User query is natural language, abbreviated, or misspelled |
| Autocomplete | `GET /v1/search/autocomplete?query=...` | Powering a search-as-you-type UI |
| Batch search | `POST /v1/search/bulk` | Multiple independent search queries in one request |

### Batch vs Single Endpoints

| Scenario | Use | Metering |
|----------|-----|----------|
| Resolving 1–2 codes | Single endpoint (`/v1/fhir/resolve`) | 1 call per code |
| Resolving 100+ codes | Batch endpoint (`/v1/fhir/resolve/batch`) | 1 call per batch (up to 100 items) |
| Mapping 1 concept | Single endpoint (`/v1/concepts/{id}/mappings`) | 1 call |
| Mapping 50+ concepts | Batch endpoint (`/v1/concepts/map/batch`) | 1 call per batch |

## Workflow

### Typical FHIR-to-OMOP ETL Task

1. **Extract unique source codes** from your FHIR dataset (deduplicate before mapping)
2. **Check local cache** for previously mapped codes
3. **Batch-resolve cache misses** using `/v1/fhir/resolve/batch` (chunk into groups of 100)
4. **Update cache** with new mappings
5. **Apply cache to full dataset** via local lookup (pandas merge, SQL JOIN, dict lookup)
6. **Flag unmapped codes** for manual review; never silently drop records

### Resolving a Single FHIR Code to OMOP

1. Extract the `system` URI and `code` from the FHIR `Coding`
2. Call `POST /v1/fhir/resolve` with `system`, `code`, and `resource_type`
3. Read `resolution.standard_concept.concept_id` (the OMOP concept to write to CDM)
4. Read `resolution.target_table` (which CDM table: `condition_occurrence`, `measurement`, `drug_exposure`, etc.)
5. Read `resolution.mapping_type` (`direct`, `mapped`, `semantic_match`, `unmapped`) to understand what happened
6. Write the standard concept ID to the appropriate CDM table

### Building a Phenotype Concept Set

1. Start with a seed concept (e.g., "Type 2 diabetes mellitus", concept ID 201826)
2. Call `GET /v1/concepts/{concept_id}/descendants?max_levels=3` to expand the hierarchy
3. Collect all descendant concept IDs
4. Optionally filter by domain, vocabulary, or concept class
5. Use the resulting concept set in OMOP CDM SQL queries

### Mapping Local Codes to Standard Vocabularies

1. Extract unique local codes from your source system
2. For each code, call `GET /v1/search/concepts?query={code}` or `GET /v1/concepts/semantic-search?query={code}`
3. Review top candidates; auto-accept high-confidence matches
4. Flag ambiguous or missing codes for manual review
5. Store mappings in a `source_to_concept_map` file for team sharing

## Common Gotchas

- **FHIR resource type ≠ OMOP domain.** A FHIR `Observation` carrying a blood glucose measurement (LOINC) maps to the `measurement` table, not `observation`. Always use the vocabulary domain from the resolved concept, not the FHIR resource type.

- **Vocabulary ID spelling matters.** Use `SNOMED`, not `SNOMED-CT`; `ICD10CM`, not `ICD-10-CM`. Typos return 404 or empty results.

- **Batch endpoints meter per item, not per request.** A 50-item batch counts as 50 API calls against your quota. Plan accordingly.

- **Never retry 400-level errors (except 429).** A 400 means your request is malformed; retrying produces the same result. A 404 means the concept doesn't exist; flag it for review, don't loop.

- **Semantic search is slower than keyword search.** Use it for natural-language or fuzzy queries, not for exact code lookups. For exact codes, use `by-code` endpoint.

- **Unmapped codes are not errors.** If a code doesn't resolve, the API returns `mapping_type: "unmapped"` with no `standard_concept`. Store the source code in `*_source_value`, set `*_concept_id` to 0, and flag for manual review.

- **Hierarchy depth matters.** SNOMED trees can be 20+ levels deep. Cap `max_levels` at the clinical depth you need (usually 3–5) to avoid slow queries.

- **API key in environment, not code.** Never hardcode API keys. Use `OMOPHUB_API_KEY` environment variable or a secrets manager.

- **Batch endpoints accept up to 100 items.** Larger inputs must be chunked. Each chunk counts as one call.

- **CPT4 and MedDRA are excluded.** OMOPHub does not include CPT4 (AMA licensing) or MedDRA (licensing restrictions). Manage these separately if needed.

- **Read-only API.** You cannot write, update, or delete concepts. All content comes from OHDSI ATHENA releases (updated every 2–3 months).

## Verification Checklist

Before submitting work with OMOPHub:

- [ ] API key is set in environment variable, not hardcoded
- [ ] All vocabulary IDs use correct spelling (`SNOMED`, `ICD10CM`, `LOINC`, etc.)
- [ ] Batch requests chunk into groups of 100 items max
- [ ] Deduplication happens before mapping (not mapping every row individually)
- [ ] Unmapped codes are flagged for review, not silently dropped
- [ ] OMOP domain (not FHIR resource type) determines CDM table assignment
- [ ] Mapping cache is persisted and reused across pipeline runs
- [ ] Rate limit headers are checked; retries use exponential backoff for 5xx only
- [ ] FHIR Resolver is used for ETL (not FHIR Terminology Service for that job)
- [ ] Concept hierarchy depth is capped at clinically relevant levels (max_levels ≤ 5)
- [ ] Source concept IDs are stored in `*_source_concept_id` columns for audit trail
- [ ] Standard concept IDs are stored in `*_concept_id` columns for analysis

## Resources

**Comprehensive navigation:** https://docs.omophub.com/llms.txt

**Critical documentation pages:**
1. [FHIR Integration Guide](https://docs.omophub.com/guides/integration/fhir-integration) — complete FHIR Resolver and Terminology Service reference with use cases
2. [FHIR-to-OMOP Standardization Workflow](https://docs.omophub.com/guides/workflows/fhir-to-omop) — four-step vocabulary resolution pattern for ETL pipelines
3. [Batch & Performance Best Practices](https://docs.omophub.com/guides/production/batch-performance) — deduplication, caching, and optimization patterns for production ETL

---

> For additional documentation and navigation, see: https://docs.omophub.com/llms.txt