Skip to content

Repository files navigation

OMOPHub Python SDK

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.

PyPI versionPython VersionsCodecovLicense: MITDownloads

Documentation · API Reference · Examples


Why OMOPHub?

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 ApproachWith OMOPHub
Download 5GB+ ATHENA vocabulary filespip install omophub
Set up and maintain databaseOne API call
Write complex SQL with multiple JOINsSimple Python methods
Manually update vocabularies quarterlyAlways current data
Local infrastructure requiredWorks anywhere Python runs

Installation

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.resources

Quick Start

fromomophubimportOMOPHub# 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)

FHIR-to-OMOP Resolution

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.

Type Interoperability

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.

FHIR Client Interop

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.

Semantic Search

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

Bulk Search

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})

Similarity Search

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,
)

Async Support

importasynciofromomophubimportAsyncOMOPHubasyncdefmain():
asyncwithAsyncOMOPHub() asclient:
concept=awaitclient.concepts.get(201826)
print(concept["concept_name"])
asyncio.run(main())

Use Cases

ETL & Data Pipelines

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"]

Data Quality Checks

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!")

Phenotype Development

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")

Clinical Applications

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", ...]

API Resources

ResourceDescriptionKey Methods
conceptsConcept lookup and batch operationsget(), get_by_code(), batch(), suggest()
searchFull-text and semantic searchbasic(), advanced(), semantic(), similar(), bulk_basic(), bulk_semantic()
hierarchyNavigate concept relationshipsancestors(), descendants()
mappingsCross-vocabulary mappingsget(), get_iter(), map()
vocabulariesVocabulary metadatalist(), get(), stats()
domainsDomain informationlist(), get(), concepts()
fhirFHIR-to-OMOP resolutionresolve(), resolve_batch(), resolve_codeable_concept()

Configuration

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
)

Error Handling

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

Type Safety

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"] # str

Integration Examples

With Pandas

importpandasaspd# 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())

In Jupyter Notebooks

# Iterate through all results with auto-paginationforconceptinclient.search.basic_iter("diabetes", page_size=100):
process_concept(concept)

Compared to Alternatives

FeatureOMOPHub SDKATHENA DownloadOHDSI WebAPI
Setup time1 minuteHoursHours
InfrastructureNoneDatabase requiredFull OHDSI stack
UpdatesAutomaticManual downloadManual
Programmatic accessNative PythonSQL queriesREST API

Best for: Teams who need quick, programmatic access to OMOP vocabularies without infrastructure overhead.

Documentation

Contributing

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

Support

License

MIT License - see LICENSE for details.


Built for the OHDSI community

Releases

Packages

Used by

Contributors

Languages