Production-ready Python client for the NLM UMLS REST APIs.
Project description
UMLS Python Client
Production-ready Python SDK for the National Library of Medicine UMLS Terminology Services APIs.
umls-python-client keeps the original UMLSClient interface for existing
users and adds typed sync, async, export, release, and workflow helpers for new
applications. It is designed for developers and researchers who need a practical
one-stop Python client for UMLS search, concepts, source vocabularies, semantic
types, crosswalks, metadata, and UTS release downloads.
Why This Client
- Current NLM REST coverage, including the May 4, 2026 search filters
semanticTypesandsemanticGroups. - Typed sync and async clients built on
httpx. - Backward-compatible legacy client with the original camelCase namespaces.
- Structured exceptions for modern clients and legacy error payloads for older code.
- JSON, JSONL, CSV, and RDF/Turtle exports.
- Practical helpers for bulk search, bulk crosswalk, concept profiles, source lookup, UMLS URL following, API-key validation, and release downloads.
- CI across Python 3.9 through 3.14.
Installation
pip install umls-python-client
For local development:
pip install ".[dev,docs]"
Authentication
Most UMLS requests require an API key from your UTS profile: https://documentation.uts.nlm.nih.gov/rest/authentication.html
import os
api_key = os.environ["UMLS_API_KEY"]
The metadata sources endpoint is public and does not require authentication, but the client still uses one consistent client configuration.
Typed Sync Quickstart
from umls_python_client import TypedUMLSClient
with TypedUMLSClient(api_key="YOUR_UMLS_API_KEY") as client:
response = client.search_api.search(
"diabetes",
semantic_groups="Disorders",
page_size=25,
)
for result in response.result:
print(result.ui, result.name)
Typed methods return UMLSResponse[T]. Use response.result for parsed
models, response.raw or response.to_dict() for the original UMLS envelope,
and response.to_rdf() for Turtle RDF.
Async Quickstart
from umls_python_client import AsyncUMLSClient
async def main() -> None:
async with AsyncUMLSClient(api_key="YOUR_UMLS_API_KEY") as client:
response = await client.crosswalk_api.get_crosswalk(
source="HPO",
id="HP:0001947",
target_source="SNOMEDCT_US",
)
print(response.result)
Legacy Compatible Usage
UMLSClient keeps existing imports, JSON string defaults, and camelCase
namespaces:
from umls_python_client import UMLSClient
client = UMLSClient(api_key="YOUR_UMLS_API_KEY")
pretty_json = client.searchAPI.search("diabetes")
raw_dict = client.search_api.search("diabetes", return_indented=False)
rdf = client.cui_api.get_cui_info("C0011849", format="rdf")
Existing namespaces are preserved:
client.searchAPIclient.sourceAPIclient.cuiAPIclient.semanticNetworkAPIclient.crosswalkAPIclient.metadataAPIclient.atomAPIclient.authAPIclient.releaseAPI
Modern namespaces are also available:
search_apisource_apicui_apisemantic_network_apicrosswalk_apimetadata_apiatom_apiauth_apirelease_api
Common Recipes
Semantic Search
response = client.search_api.search(
"depression",
semantic_types="T047|T046",
semantic_groups="Disorders",
)
semantic_types supports semantic type TUIs, semantic type names, and semantic
tree number prefixes. Use pipes for multiple values, matching NLM's search
documentation.
Source Code To CUI
response = client.search_api.search(
"9468002",
input_type="sourceUi",
search_type="exact",
sabs="SNOMEDCT_US",
)
HPO To SNOMED CT Crosswalk
response = client.crosswalk_api.get_crosswalk(
source="HPO",
id="HP:0001947",
target_source="SNOMEDCT_US",
)
UMLS CUI crosswalks are useful starting points for mapping work. Review results before using them in clinical or research pipelines.
Concept Profile
profile = client.cui_api.get_concept_profile(
"C0011849",
include_atoms=False,
).result
print(profile.concept.name)
print([definition.value for definition in profile.definitions])
Metadata Lookup
sources = client.metadata_api.find_source(abbreviation="SNOMEDCT_US").result
Bulk Workflows
searches = client.search_api.bulk_search(["diabetes", "asthma"])
crosswalks = client.crosswalk_api.bulk_crosswalk(
["HP:0001947", "HP:0001250"],
source="HPO",
target_source="SNOMEDCT_US",
)
Follow UMLS URLs
Many UMLS payload fields contain URLs to related resources. Follow those URLs with the same authenticated client:
concept = client.cui_api.get_cui_info("C0011849").result
atoms = client.follow_url(concept.raw["atoms"])
Release Downloads
releases = client.release_api.list_releases(
release_type="umls-full-release",
current=True,
)
client.release_api.download_file(
url=releases.result[0].url,
path="downloads",
)
Call list_releases() without a release_type to inspect NLM's release
catalog, or pass a release type such as umls-full-release to discover current
download URLs.
Exports
Typed responses can save themselves:
response = client.search_api.search("fracture", page_size=25)
response.save("exports/search", format="jsonl")
response.save("exports/search.csv", format="csv")
response.save("exports/search.ttl", format="rdf")
Clients can export any response or raw payload:
client.export(response, "exports/search.json", format="json", overwrite=True)
Supported formats:
| Format | Output |
|---|---|
json |
Full UMLS response envelope |
jsonl |
One result record per line |
csv |
Flattened scalar result fields |
rdf |
Turtle RDF |
If the path has a file suffix, the client writes exactly there. If the path is a directory or suffixless path, parent directories are created and a safe filename is generated.
Legacy save_to_file=True still works and now delegates to the same export
implementation.
Endpoint Coverage
| Namespace | Coverage | Pagination behavior |
|---|---|---|
search_api |
/search/{version} with semantic filters |
Sends pageSize; no obsolete pageNumber |
metadata_api |
/metadata/{version}/sources |
No auth required |
cui_api |
CUI detail, atoms, preferred atom, definitions, relations | Pagination where NLM documents it |
atom_api |
AUI detail, parents, children, ancestors, descendants | Sends pageSize; no pageNumber |
source_api |
Source detail, atoms, preferred atom, hierarchy, attributes, relations | Pagination where NLM documents it |
crosswalk_api |
Source identifier crosswalk | Supports documented pagination |
semantic_network_api |
Semantic type lookup | Single resource |
auth_api |
UMLS license/API-key validation | UTS validation endpoint |
release_api |
Release listing and authenticated downloads | UTS release/download endpoints |
Error Handling
Typed clients raise structured exceptions:
UMLSRequestErrorUMLSHTTPErrorUMLSDecodeError
Legacy UMLSClient methods keep returning error payload dictionaries instead
of raising for UMLS request failures.
Configure timeouts and retries at client construction:
client = TypedUMLSClient(
api_key="YOUR_UMLS_API_KEY",
timeout=20.0,
retries=3,
)
Testing
ruff check .
ruff format --check .
mypy src
pytest
mkdocs build --strict
python -m build
Live integration tests are optional and require UMLS_API_KEY. Tests that would
validate user keys or download release files remain opt-in and do not run in
default CI.
Migration Notes
From 1.0.x and 1.1.0:
- Existing imports and
UMLSClientbehavior remain supported. - Prefer
TypedUMLSClientfor new sync code. - Prefer
AsyncUMLSClientfor async applications. - Prefer
response.save(...)orclient.export(...)over new uses ofsave_to_file=True. - Use snake_case namespaces in new code. CamelCase namespaces remain available for compatibility.
Documentation
- NLM UMLS REST docs: https://documentation.uts.nlm.nih.gov/rest/home.html
- NLM auth docs: https://documentation.uts.nlm.nih.gov/rest/authentication.html
- Project source: https://github.com/palasht75/umls-python-client
Releases
This repository uses Release Please and PyPI trusted publishing. Merged commits should use Conventional Commit prefixes:
fix:for patch releasesfeat:for minor releasesfeat!:orfix!:for major releases
Release Please opens a release PR. Merging that release PR creates the GitHub Release, builds distributions, attaches artifacts, and publishes to PyPI.
GitHub Actions must be allowed to create release PRs, or the repository must
provide a RELEASE_PLEASE_TOKEN secret with PR permissions.
License
Apache-2.0. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file umls_python_client-1.2.0.tar.gz.
File metadata
- Download URL: umls_python_client-1.2.0.tar.gz
- Upload date:
- Size: 39.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba86fbb2ba3f70931cfc62295805839a2b2d3771c3c0941c1f38183856587daf
|
|
| MD5 |
055eee355526b74bcc39d0808846deb7
|
|
| BLAKE2b-256 |
48fca024ee2932322a72664b676ec5557edf1fbf3506db6b4908c06dc16c88ce
|
Provenance
The following attestation bundles were made for umls_python_client-1.2.0.tar.gz:
Publisher:
release-please.yml on palasht75/umls-python-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
umls_python_client-1.2.0.tar.gz -
Subject digest:
ba86fbb2ba3f70931cfc62295805839a2b2d3771c3c0941c1f38183856587daf - Sigstore transparency entry: 1647233516
- Sigstore integration time:
-
Permalink:
palasht75/umls-python-client@15afa9146861476c7eed30ee3be333f22102d264 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/palasht75
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@15afa9146861476c7eed30ee3be333f22102d264 -
Trigger Event:
push
-
Statement type:
File details
Details for the file umls_python_client-1.2.0-py3-none-any.whl.
File metadata
- Download URL: umls_python_client-1.2.0-py3-none-any.whl
- Upload date:
- Size: 37.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c782eff4311d7b96c4df51977eff17fa65da81a3bb331c521d50679cacf7dca7
|
|
| MD5 |
f85d7c4eb9726c9b1852bbe8022c5e19
|
|
| BLAKE2b-256 |
f6fd003bebfb60a9b2166e2bc14b316eb6337425bef42ea71105888aa7c1b06e
|
Provenance
The following attestation bundles were made for umls_python_client-1.2.0-py3-none-any.whl:
Publisher:
release-please.yml on palasht75/umls-python-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
umls_python_client-1.2.0-py3-none-any.whl -
Subject digest:
c782eff4311d7b96c4df51977eff17fa65da81a3bb331c521d50679cacf7dca7 - Sigstore transparency entry: 1647233632
- Sigstore integration time:
-
Permalink:
palasht75/umls-python-client@15afa9146861476c7eed30ee3be333f22102d264 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/palasht75
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@15afa9146861476c7eed30ee3be333f22102d264 -
Trigger Event:
push
-
Statement type: