Getting Started with omophub

Introduction

The omophub package provides an R interface to the OMOPHub API for accessing OHDSI ATHENA standardized medical vocabularies. This vignette demonstrates basic usage patterns.

Installation

Install from GitHub:

# install.packages("devtools")
devtools::install_github("omophub/omophub-R")

Authentication

The package requires an API key from OMOPHub.

Set your API key as an environment variable:

Sys.setenv(OMOPHUB_API_KEY = "your_api_key_here")

Or add it to your .Renviron file for persistence:

OMOPHUB_API_KEY=your_api_key_here

Creating a Client

library(omophub)

# Create client (reads API key from environment)
client <- OMOPHubClient$new()

# Or provide API key explicitly
client <- OMOPHubClient$new(api_key = "your_api_key")

# With additional options
client <- OMOPHubClient$new(
  api_key = "your_api_key",
  timeout = 30,
  max_retries = 3,
  vocab_version = "2025.1"
)

Getting Concepts

Retrieve a concept by its OMOP concept ID:

concept <- client$concepts$get(201826)
print(concept$concept_name)
# [1] "Type 2 diabetes mellitus"

Get a concept by vocabulary-specific code:

concept <- client$concepts$get_by_code("SNOMED", "44054006")
print(concept$concept_name)
# [1] "Type 2 diabetes mellitus"

Batch Operations

Retrieve multiple concepts in a single request:

result <- client$concepts$batch(c(201826, 4329847, 1112807))
for (concept in result$concepts) {
  cat(sprintf("%s: %s\n", concept$concept_id, concept$concept_name))
}

Searching Concepts

Basic search:

results <- client$search$basic("diabetes mellitus", page_size = 10)
for (concept in results$data) {
  cat(sprintf("%s: %s\n", concept$concept_id, concept$concept_name))
}

Search with filters:

results <- client$search$basic(
  "heart attack",
  vocabulary_ids = "SNOMED",
  domain_ids = "Condition",
  page_size = 20
)

Autocomplete

Get suggestions for autocomplete:

suggestions <- client$concepts$suggest("diab", page_size = 5)
for (s in suggestions$suggestions) {
  print(s$suggestion)
}

Hierarchy Navigation

Get ancestors (parent concepts):

result <- client$hierarchy$ancestors(201826, max_levels = 3)
for (ancestor in result$ancestors) {
  print(ancestor$concept_name)
}

Get descendants (child concepts):

result <- client$hierarchy$descendants(201826, max_levels = 2)
for (descendant in result$descendants) {
  print(descendant$concept_name)
}

Concept Mappings

Find how a concept maps to other vocabularies:

result <- client$mappings$get(201826)
for (mapping in result$mappings) {
  cat(sprintf("%s: %s\n",
              mapping$target_vocabulary_id,
              mapping$target_concept_name))
}

Map to specific vocabularies:

result <- client$mappings$get(
  201826,
  target_vocabulary = "ICD10CM"
)

Error Handling

Use tryCatch to handle errors:

tryCatch(
  {
    concept <- client$concepts$get(999999999)
  },
  omophub_not_found = function(e) {
    message("Concept not found: ", e$message)
  },
  omophub_api_error = function(e) {
    message("API error: ", e$message)
  }
)

FHIR-to-OMOP Resolution

The FHIR resolver translates FHIR coded values to OMOP standard concepts in a single call — handling URI mapping, code lookup, Maps to traversal, and CDM target table assignment automatically.

Single Coding

result <- client$fhir$resolve(
  system = "http://snomed.info/sct",
  code = "44054006",
  resource_type = "Condition"
)
cat(result$resolution$standard_concept$concept_name)
cat(result$resolution$target_table)   # "condition_occurrence"
cat(result$resolution$mapping_type)   # "direct"

Non-Standard Code (Automatic Maps-to Traversal)

result <- client$fhir$resolve(
  system = "http://hl7.org/fhir/sid/icd-10-cm",
  code = "E11.9"
)
cat(result$resolution$mapping_type)                    # "mapped"
cat(result$resolution$standard_concept$vocabulary_id)  # "SNOMED"

Batch Resolution

batch <- client$fhir$resolve_batch(list(
  list(system = "http://snomed.info/sct", code = "44054006"),
  list(system = "http://loinc.org", code = "2339-0")
))
cat(sprintf("Resolved: %d/%d\n", batch$summary$resolved, batch$summary$total))

CodeableConcept with Vocabulary Preference

result <- client$fhir$resolve_codeable_concept(
  coding = list(
    list(system = "http://snomed.info/sct", code = "44054006"),
    list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "E11.9")
  ),
  resource_type = "Condition"
)
# SNOMED wins over ICD-10-CM per OHDSI preference
cat(result$best_match$resolution$source_concept$vocabulary_id)  # "SNOMED"

Tibble Output for Batch Resolution

For dplyr / tidyr workflows, pass as_tibble = TRUE to get a flat tibble with one row per input coding and columns for the source and standard concepts, target CDM table, mapping type, and resolution status. This is the most ergonomic shape for ETL pipelines processing many codes:

library(dplyr)

tbl <- client$fhir$resolve_batch(
  list(
    list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "E11.9"),
    list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "I10"),
    list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "J45.909")
  ),
  as_tibble = TRUE
)

tbl |>
  filter(status == "resolved") |>
  select(source_code, standard_concept_name, target_table)

Failed rows are kept in-place with status = "failed" and the error text in status_detail — you can filter them out rather than silently dropping them. The batch summary (total / resolved / failed) is attached as an attribute:

attr(tbl, "summary")

The default as_tibble = FALSE still returns the list-shaped list(results, summary), so existing code keeps working unchanged.

Standalone Wrapper Functions

For pipe-friendly workflows, omophub also exports standalone wrapper functions that take the client as their first argument. Both forms are fully supported — pick whichever reads better for the surrounding code:

# Equivalent to client$fhir$resolve()
client |>
  fhir_resolve(
    system = "http://snomed.info/sct",
    code = "44054006",
    resource_type = "Condition"
  )

# Tibble-shaped batch in a pipe
tbl <- client |>
  fhir_resolve_batch(
    codings = list(
      list(system = "http://snomed.info/sct", code = "44054006"),
      list(system = "http://loinc.org", code = "2339-0")
    ),
    as_tibble = TRUE
  )

client |>
  fhir_resolve_codeable_concept(
    coding = list(
      list(system = "http://snomed.info/sct", code = "44054006"),
      list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "E11.9")
    ),
    resource_type = "Condition"
  )

FHIR Client Interop with omophub_fhir_url()

When you need raw FHIR Parameters / Bundle responses instead of the Concept Resolver envelope, omophub_fhir_url() returns the OMOPHub FHIR Terminology Service base URL so you can talk to it directly with httr2 or fhircrackr. Supports FHIR versions "r4" (default), "r4b", "r5", and "r6":

omophub_fhir_url()
#> "https://fhir.omophub.com/fhir/r4"

omophub_fhir_url("r5")
#> "https://fhir.omophub.com/fhir/r5"

Example: call CodeSystem/$lookup directly with httr2:

library(httr2)

resp <- request(omophub_fhir_url()) |>
  req_url_path_append("CodeSystem/$lookup") |>
  req_url_query(
    system = "http://snomed.info/sct",
    code = "44054006"
  ) |>
  req_headers(Authorization = paste("Bearer", Sys.getenv("OMOPHUB_API_KEY"))) |>
  req_perform()

params <- resp_body_json(resp)
# Raw FHIR Parameters resource with the concept display and designations.

Use client$fhir$resolve() (or fhir_resolve()) when you want OMOP-enriched answers (standard concept, CDM target table, mapping quality). Use omophub_fhir_url() + httr2 when you need FHIR-native responses for FHIR-native tooling.

Further Resources