Query millions standardized medical concepts via simple Python API
Access SNOMED CT, ICD-10, RxNorm, LOINC, and 90+ OHDSI ATHENA vocabularies without downloading, installing, or maintaining local databases.
Documentation · API Reference · Examples
Working with OHDSI ATHENA vocabularies traditionally requires downloading multi-gigabyte files, setting up a database instance, and writing complex SQL queries. OMOPHub eliminates this friction.
| Traditional Approach | With OMOPHub |
|---|---|
| Download 5GB+ ATHENA vocabulary files | pip install omophub |
| Set up and maintain database | One API call |
| Write complex SQL with multiple JOINs | Simple Python methods |
| Manually update vocabularies quarterly | Always current data |
| Local infrastructure required | Works anywhere Python runs |
pip install omophub
# Optional extras for FHIR client interop
pip install omophub[fhirpy] # Pre-wired fhirpy client
pip install omophub[fhir-resources] # Install marker for fhir.resourcesfromomophubimportOMOPHub# Initialize client (uses OMOPHUB_API_KEY env variable, or pass api_key="...")client=OMOPHub()
# Get a concept by IDconcept=client.concepts.get(201826)
print(concept["concept_name"]) # "Type 2 diabetes mellitus"# Search for concepts across vocabulariesresults=client.search.basic("metformin", vocabulary_ids=["RxNorm"], domain_ids=["Drug"])
forcinresults["concepts"]:
print(f"{c['concept_id']}: {c['concept_name']}")
# Map an ICD-10 code to SNOMED: look the code up, then map its concept.# (`Maps to` points at *standard* concepts, so SNOMED is a valid target here# while the reverse, SNOMED -> ICD10CM, would return nothing.)icd=client.concepts.get_by_code("ICD10CM", "E11.9")
mappings=client.mappings.get(icd["concept_id"], target_vocabulary="SNOMED")
# Navigate concept hierarchyancestors=client.hierarchy.ancestors(201826, max_levels=3)Resolve FHIR coded values to OMOP standard concepts in one call:
# Single FHIR Coding → OMOP concept + CDM target tableresult=client.fhir.resolve(
system="http://snomed.info/sct",
code="44054006",
resource_type="Condition",
)
print(result["resolution"]["target_table"]) # "condition_occurrence"print(result["resolution"]["mapping_type"]) # "direct"# ICD-10-CM → traverses "Maps to" automaticallyresult=client.fhir.resolve(
system="http://hl7.org/fhir/sid/icd-10-cm",
code="E11.9",
)
print(result["resolution"]["standard_concept"]["vocabulary_id"]) # "SNOMED"# Batch resolve up to 100 codingsbatch=client.fhir.resolve_batch([
{"system": "http://snomed.info/sct", "code": "44054006"},
{"system": "http://loinc.org", "code": "2339-0"},
{"system": "http://www.nlm.nih.gov/research/umls/rxnorm", "code": "197696"},
])
print(f"Resolved {batch['summary']['resolved']}/{batch['summary']['total']}")
# CodeableConcept with vocabulary preference (SNOMED wins over ICD-10)result=client.fhir.resolve_codeable_concept(
coding=[
{"system": "http://snomed.info/sct", "code": "44054006"},
{"system": "http://hl7.org/fhir/sid/icd-10-cm", "code": "E11.9"},
],
resource_type="Condition",
)
print(result["best_match"]["resolution"]["source_concept"]["vocabulary_id"]) # "SNOMED"The resolver also follows the HL7 FHIR-to-OMOP IG: it resolves FHIR administrative codes via the IG ConceptMaps, decomposes composite concepts (Maps to value), honors Coding.userSelected, and can return a concept_id 0 sentinel instead of a 404.
# Administrative gender → person.gender_concept_id (via IG ConceptMap)client.fhir.resolve(system="http://hl7.org/fhir/administrative-gender", code="male")
# A user-selected coding wins over vocabulary preferenceclient.fhir.resolve_codeable_concept(coding=[
{"system": "http://snomed.info/sct", "code": "44054006"},
{"system": "http://hl7.org/fhir/sid/icd-10-cm", "code": "E11.9", "user_selected": True},
])
# on_unmapped="sentinel" → a concept_id 0 record instead of a 404 (one row per input for ETL)client.fhir.resolve(system="http://snomed.info/sct", code="00000000", on_unmapped="sentinel")Composite concepts (e.g. "Allergy to penicillin") additionally surface resolution["value_as_concept"] (the IG Value-as-Concept pattern). on_unmapped is accepted by resolve(), resolve_batch(), and resolve_codeable_concept() on both the sync and async clients.
The resolver accepts any Coding-like input via duck typing - a plain dict, omophub's lightweight Coding TypedDict, or any object with .system / .code attributes (e.g. fhir.resources.Coding, fhirpy codings).
fromomophub.types.fhirimportCoding# omophub's TypedDict - IDE autocomplete, no extra depscoding: Coding= {"system": "http://snomed.info/sct", "code": "44054006"}
result=client.fhir.resolve(coding=coding)
# fhir.resources objects work via duck typing - no conversion neededfromfhir.resources.R4B.codingimportCodingasFhirCodingfhir_coding=FhirCoding(system="http://snomed.info/sct", code="44054006")
result=client.fhir.resolve(coding=fhir_coding)
# Mixed shapes in a single batch callresult=client.fhir.resolve_batch([
{"system": "http://snomed.info/sct", "code": "44054006"}, # dictFhirCoding(system="http://loinc.org", code="2339-0"), # fhir.resources
])fhir.resources is never a required dependency. See examples/fhir_interop.py for the full set of supported input shapes.
Point external FHIR client libraries at OMOPHub's FHIR Terminology Service directly - useful when you need raw FHIR Parameters / Bundle responses instead of the Concept Resolver envelope.
fromomophubimportOMOPHub, get_fhir_server_urlclient=OMOPHub(api_key="oh_xxx")
# Property on the client returns the R4 base URLprint(client.fhir_server_url)
# "https://fhir.omophub.com/fhir/r4"# Helper for other FHIR versionsprint(get_fhir_server_url("r5"))
# "https://fhir.omophub.com/fhir/r5"For fhirpy, install the optional extra and use the pre-wired client:
pip install omophub[fhirpy]fromomophubimportget_fhirpy_clientfhir=get_fhirpy_client("oh_xxx")
# Call CodeSystem/$lookup directly via fhirpyparams=fhir.execute(
"CodeSystem/$lookup",
method="GET",
params={"system": "http://snomed.info/sct", "code": "44054006"},
)When to use which: the Concept Resolver (client.fhir.resolve) gives you OMOP-enriched answers - standard concept ID, CDM target table, mapping quality. Use fhirpy via get_fhirpy_client() when you need raw FHIR responses for FHIR-native tooling.
Use natural language queries to find concepts using neural embeddings:
# Natural language search - understands clinical intentresults=client.search.semantic("high blood sugar levels")
forrinresults["results"]:
print(f"{r['concept_name']} (similarity: {r['similarity_score']:.2f})")
# Filter by vocabulary and set minimum similarity thresholdresults=client.search.semantic(
"heart attack",
vocabulary_ids=["SNOMED"],
domain_ids=["Condition"],
threshold=0.5
)
# Iterate through all results with auto-paginationforresultinclient.search.semantic_iter("chronic kidney disease", page_size=50):
print(f"{result['concept_id']}: {result['concept_name']}")Search for multiple terms in a single API call — much faster than individual requests:
# Bulk lexical search (up to 50 queries)results=client.search.bulk_basic([
{"search_id": "q1", "query": "diabetes mellitus"},
{"search_id": "q2", "query": "hypertension"},
{"search_id": "q3", "query": "aspirin"},
], defaults={"vocabulary_ids": ["SNOMED"], "page_size": 5})
foriteminresults["results"]:
print(f"{item['search_id']}: {len(item['results'])} results")
# Bulk semantic search (up to 25 queries)results=client.search.bulk_semantic([
{"search_id": "s1", "query": "heart failure treatment options"},
{"search_id": "s2", "query": "type 2 diabetes medication"},
], defaults={"threshold": 0.5, "page_size": 10})Find concepts similar to a known concept or natural language query:
# Find concepts similar to a known conceptresults=client.search.similar(concept_id=201826, algorithm="hybrid")
forrinresults["results"]:
print(f"{r['concept_name']} (score: {r['similarity_score']:.2f})")
# Find similar concepts using a natural language queryresults=client.search.similar(
query="medications for high blood pressure",
algorithm="semantic",
similarity_threshold=0.6,
vocabulary_ids=["RxNorm"],
include_scores=True,
)importasynciofromomophubimportAsyncOMOPHubasyncdefmain():
asyncwithAsyncOMOPHub() asclient:
concept=awaitclient.concepts.get(201826)
print(concept["concept_name"])
asyncio.run(main())Validate and map clinical codes during OMOP CDM transformations:
# Validate that a source code exists and find its standard equivalentdefvalidate_and_map(source_vocab, source_code):
concept=client.concepts.get_by_code(source_vocab, source_code)
ifconcept["standard_concept"] !="S":
mappings=client.mappings.get(concept["concept_id"],
target_vocabulary="SNOMED")
returnmappings["mappings"][0]["target_concept_id"]
returnconcept["concept_id"]Verify codes exist and are valid standard concepts:
# Check if all your condition codes are validcondition_codes= ["E11.9", "I10", "J44.9"] # ICD-10 codesforcodeincondition_codes:
try:
concept=client.concepts.get_by_code("ICD10CM", code)
print(f"OK {code}: {concept['concept_name']}")
exceptomophub.NotFoundError:
print(f"ERROR {code}: Invalid code!")Explore hierarchies to build comprehensive concept sets:
# Get all descendants of "Type 2 diabetes mellitus" for phenotypedescendants=client.hierarchy.descendants(201826, max_levels=5)
concept_set= [d["concept_id"] fordindescendants["concepts"]]
print(f"Found {len(concept_set)} concepts for T2DM phenotype")Build terminology lookups into healthcare applications:
# Autocomplete for clinical coding interfacesuggestions=client.concepts.suggest("diab", vocabulary_ids=["SNOMED"], page_size=10)
# Returns: ["Diabetes mellitus", "Diabetic nephropathy", "Diabetic retinopathy", ...]| Resource | Description | Key Methods |
|---|---|---|
concepts | Concept lookup and batch operations | get(), get_by_code(), batch(), suggest() |
search | Full-text and semantic search | basic(), advanced(), semantic(), similar(), bulk_basic(), bulk_semantic() |
hierarchy | Navigate concept relationships | ancestors(), descendants() |
mappings | Cross-vocabulary mappings | get(), get_iter(), map() |
vocabularies | Vocabulary metadata | list(), get(), stats() |
domains | Domain information | list(), get(), concepts() |
fhir | FHIR-to-OMOP resolution | resolve(), resolve_batch(), resolve_codeable_concept() |
client=OMOPHub(
api_key="oh_xxx", # Or set OMOPHUB_API_KEY env varbase_url="https://api.omophub.com/v1", # API endpointtimeout=30.0, # Request timeout (seconds)max_retries=3, # Retry attemptsvocab_version="2025.2", # Specific vocabulary version
)importomophubtry:
concept=client.concepts.get(999999999)
exceptomophub.NotFoundErrorase:
print(f"Concept not found: {e.message}")
exceptomophub.AuthenticationErrorase:
print(f"Check your API key: {e.message}")
exceptomophub.RateLimitErrorase:
print(f"Rate limited. Retry after {e.retry_after} seconds")
exceptomophub.APIErrorase:
print(f"API error {e.status_code}: {e.message}")The SDK is fully typed with TypedDict definitions for IDE autocomplete:
fromomophubimportOMOPHub, Conceptclient=OMOPHub()
concept: Concept=client.concepts.get(201826)
# IDE autocomplete works for all fieldsconcept["concept_id"] # intconcept["concept_name"] # strconcept["vocabulary_id"] # strconcept["domain_id"] # strconcept["concept_class_id"] # strimportpandasaspd# Search and load into DataFrameresults=client.search.basic("hypertension", page_size=100)
df=pd.DataFrame(results["concepts"])
print(df[["concept_id", "concept_name", "vocabulary_id"]].head())# Iterate through all results with auto-paginationforconceptinclient.search.basic_iter("diabetes", page_size=100):
process_concept(concept)| Feature | OMOPHub SDK | ATHENA Download | OHDSI WebAPI |
|---|---|---|---|
| Setup time | 1 minute | Hours | Hours |
| Infrastructure | None | Database required | Full OHDSI stack |
| Updates | Automatic | Manual download | Manual |
| Programmatic access | Native Python | SQL queries | REST API |
Best for: Teams who need quick, programmatic access to OMOP vocabularies without infrastructure overhead.
We welcome contributions! Please see our Contributing Guide for details.
# Clone and install for development
git clone https://github.com/omopHub/omophub-python.git
cd omophub-python
pip install -e ".[dev]"# Run tests
pytest- GitHub Issues
- GitHub Discussions
- Email: support@omophub.com
- Website: omophub.com
MIT License - see LICENSE for details.
Built for the OHDSI community