ehcsapi
Async Python client for the eHealth Codierservice API (Austrian eHealth, FHIR R5 ConceptMap).
The service maps free-text diagnoses to coded concepts (SNOMED CT, ICD-10, Orphanet). The typical flow is:
- create a search episode once,
- call
find_diagnoses(...)repeatedly while the user types (type-ahead), - call
get_codes(...)for the selected diagnosis to obtain the SNOMED/ICD-10 codes, - drop the episode.
The client is async-only (built on httpx.AsyncClient) so it fits naturally into a Django ASGI
type-ahead view, where in-flight requests can be cancelled as the user keeps typing.
Installation
uv add ehcsapi # or: pip install ehcsapi
Requires Python ≥ 3.11. Depends on httpx and cryptography.
Authentication (mutual TLS)
The service requires a client certificate. The operator recommends loading the certificate once at
startup and keeping it in memory for the lifetime of the process. This client supports exactly that —
pass a pre-built ssl.SSLContext, or in-memory certificate bytes, or a file path:
import ehcsapi
# Recommended: build the context once at startup (e.g. from your central cert service) and reuse it.
ctx = ehcsapi.build_ssl_context(cert_bytes, password="…") # bytes (PEM or PKCS#12) or a path
client = ehcsapi.AsyncClient("com.example.myapp", ssl_context=ctx)
# Or let the client build it from a path / bytes:
client = ehcsapi.AsyncClient("com.example.myapp", cert="/path/to/client.p12", cert_password="…")
The base URL defaults to production (ehcsapi.PRODUCTION_BASE_URL). For the test system use
ehcsapi.TEST_BASE_URL (https://csapi-t.ehealth.gv.at):
client = ehcsapi.AsyncClient("com.example.myapp", base_url=ehcsapi.TEST_BASE_URL, cert=cert)
Usage
Type-ahead (one episode, many keystrokes)
async with ehcsapi.AsyncClient("com.example.myapp", ssl_context=ctx) as cs:
async with cs.search_episode() as episode: # POST on enter, DELETE on exit
hits = await episode.find_diagnoses("influ") # -> list[Diagnosis]
codes = await episode.get_codes(hits[0]) # search terms reused from the Diagnosis
print(codes.icd10) # [Coding(code='J11.1', display='Grippe …')]
In a stateless Django view you usually create the episode once, keep its id in the session, and reconstruct a handle per request without a new POST:
episode = cs.episode(request.session["episode_id"]) # existing episode, no round-trip
hits = await episode.find_diagnoses(query)
One-shot lookup
async with ehcsapi.AsyncClient("com.example.myapp", ssl_context=ctx) as cs:
hits = await cs.find_diagnoses("influenza") # opens + drops an ad-hoc episode
API surface
| Method | Endpoint |
|---|---|
startup_probe() |
HEAD /startupprobe |
version() / dataset_version() / application_version() |
GET /version[...] |
create_episode() / create_episode_v2() / search_episode() |
POST .../searchepisode |
episode(id) |
resume an existing episode (no request) |
delete_episode(id) / delete_episode_v2(id) |
DELETE .../searchepisode/{id} |
SearchEpisode.find_diagnoses(...) |
GET .../conceptmap?_query=findDiagnoses |
SearchEpisode.get_codes(...) |
GET .../conceptmap?_query=getCodeByElementId |
emit_x(n) / emit_random(n) |
diagnostic GET /emit/... |
Errors raise a CodierserviceError subclass: BadRequestError (400), ForbiddenError (403),
SearchEpisodeExpiredError (410 — create a fresh episode), or TransportError for network failures.
Test run against the test system
A runnable smoke script and an opt-in live test hit csapi-t:
# one-off manual run
CSAPI_CLIENT_TLS_CERT_PATH=/path/to/client.p12 CSAPI_CLIENT_TLS_CERT_PASSWORD=… \
python examples/smoke.py influenza
# opt-in pytest
EHCSAPI_LIVE=1 CSAPI_CLIENT_TLS_CERT_PATH=/path/to/client.p12 CSAPI_CLIENT_TLS_CERT_PASSWORD=… \
uv run pytest -m live
The reference OpenAPI document is in docs/openapi.json.
Development
uv sync
uv run pytest # unit tests, no network
uv run ruff check
uv run mypy src
License
MIT — see LICENSE.
Metadata
Release files for ehcsapi 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ehcsapi-0.2.0.tar.gz | 53.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ehcsapi-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 67.4 kB
Release files / ehcsapi-0.2.0.tar.gz
| Download URL | ehcsapi-0.2.0.tar.gz |
|---|---|
| Size | 53.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09a0a01b201cdd62273ff66a02bd0ad9b5f935261db2edbbc83634eba1c4ed45
|
|
BLAKE2b-256 checksum How to use checksums |
214b3efd28a2b26bf832ac33acd92c49bb86cc2dfc499d5337706174f93ea37f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Release files / ehcsapi-0.2.0-py3-none-any.whl
| Download URL | ehcsapi-0.2.0-py3-none-any.whl |
|---|---|
| Size | 13.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b4ba01c9aaa1ab54d49f7eee665959660a772088bae5727c2bb94d2281ba9901
|
|
BLAKE2b-256 checksum How to use checksums |
219778c11b649186a8dc34467ef5e6cce6fc1e10f93d7571b1ed93f046110f63
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Security for ehcsapi 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.