Official Python client for the Medsplain TX medical text simplification API
Project description
Medsplain TX — Python SDK
Official Python client for the Medsplain TX medical text simplification API. Send clinical / medical text and get back a plain-language explanation.
The SDK ships three clients, one per credential type:
| Client | Credential | Use it to… |
|---|---|---|
Medsplain |
mds_live_… API key (x-api-key) |
Simplify medical text (translate, health check). |
OrganizationClient |
mds_org_secret_… secret key (org admin) |
Manage your organization and its API keys. |
AdminClient |
Cognito JWT access token (Authorization: Bearer) |
Platform admin: organizations, payments, admins, config. |
Installation
pip install medsplain
Until this is published to PyPI, install from source — see Local development.
Quick start
from medsplain import Medsplain
client = Medsplain(api_key="mds_live_...")
result = client.translate("Patient presents with acute myocardial infarction.")
print(result.simplified_text)
print(f"{result.source_chars} chars in, {result.output_chars} chars out")
The client is a context manager, which closes the connection pool for you:
with Medsplain(api_key="mds_live_...") as client:
result = client.translate("CBC shows leukocytosis with left shift.")
print(result.simplified_text)
Configuration
| Argument | Env var | Default | Notes |
|---|---|---|---|
api_key |
MEDSPLAIN_API_KEY |
— | Required. Your mds_live_ key. |
base_url |
MEDSPLAIN_BASE_URL |
http://localhost:8000 |
API root, without the /v1 prefix. |
timeout |
— | 30.0 |
Per-request timeout in seconds. |
max_retries |
— | 2 |
Retries on connection errors, 429, and 5xx. |
import os
client = Medsplain(
api_key=os.environ["MEDSPLAIN_API_KEY"],
base_url="https://your-medsplain-host.example.com",
timeout=60.0,
)
API
translate(text, *, model=None) -> TranslationResult
Simplify medical text into plain language. text must be non-empty and at most
5000 characters.
Automatically retries against a secondary backend if the primary is unreachable, erroring server-side, or rate-limited — no configuration needed. Errors that would fail identically against either backend (bad input, an invalid key, no active plan) are raised immediately without a fallback attempt.
health_check() -> HealthStatus
Verify the API is reachable (GET /health-check).
TranslationResult
| Field | Type | Description |
|---|---|---|
original_text |
str |
Cleaned input text the server processed. |
simplified_text |
str |
The plain-language simplification. |
source_chars |
int |
Character count of the input. |
output_chars |
int |
Character count of the output. |
is_medical_text |
bool |
Whether the input was detected as medical. |
is_conversation |
bool |
Whether the input was detected as conversational. |
raw |
dict |
Full, unmodified response payload. |
Error handling
Every error inherits from MedsplainError. HTTP failures raise an APIStatusError
subclass chosen by status code:
from medsplain import (
Medsplain, AuthenticationError, RateLimitError,
PermissionDeniedError, APIStatusError,
)
client = Medsplain(api_key="mds_live_...")
try:
result = client.translate("...")
except AuthenticationError:
... # 401 — key missing/invalid/expired/revoked
except RateLimitError as e:
print("retry after", e.retry_after) # 429 — rate limit or token quota
except PermissionDeniedError:
... # 403 — no active plan / subscription
except APIStatusError as e:
print(e.status_code, e.code, e.message)
| Exception | Status | Meaning |
|---|---|---|
BadRequestError |
400 | Malformed request / validation error. |
AuthenticationError |
401 | API key missing, invalid, expired, or revoked. |
PermissionDeniedError |
403 | No active plan or subscription. |
NotFoundError |
404 | Resource not found. |
RateLimitError |
429 | Rate limit hit or token quota exhausted. |
ServerError |
5xx | Server-side failure. |
APIConnectionError |
— | The request never reached the server. |
Organization management
OrganizationClient manages your organization and the API keys your applications use.
It authenticates with your organization secret key (mds_org_secret_…) — a different
credential from the mds_live_ key above — sent in the X-Organization-Secret-Key header.
from medsplain import OrganizationClient
with OrganizationClient(
org_id="org_000000000001",
secret_key="mds_org_secret_...",
) as org:
# Inspect the organization
info = org.get_organization()
print(info.plan_type, info.total_tokens_used)
# Create a key — the full mds_live_ value is returned ONLY here
created = org.create_api_key(name="production", description="prod server")
print(created.api_key) # store this securely; never shown again
# List, fetch, disable, delete
keys = org.list_api_keys(is_active=True)
one = org.get_api_key(created.id)
org.set_api_key_status(created.id, is_active=False)
org.delete_api_key(created.id)
Configuration
| Argument | Env var | Default | Notes |
|---|---|---|---|
org_id |
MEDSPLAIN_ORG_ID |
— | Required. e.g. org_000000000001. |
secret_key |
MEDSPLAIN_ORG_SECRET_KEY |
— | Required. Your mds_org_secret_ key. |
base_url |
MEDSPLAIN_BASE_URL |
http://localhost:8000 |
API root, without the /v1 prefix. |
timeout and max_retries work exactly as on Medsplain. With both env vars set you can
construct it with no arguments: OrganizationClient(). (The SDK does not load .env files —
export the variables yourself.)
Methods
| Method | API call | Returns |
|---|---|---|
get_organization() |
GET /v1/admin/organizations/{org_id} |
Organization |
create_api_key(name, *, description=None, expires_at=None) |
POST …/api-keys |
ApiKeyWithSecret |
list_api_keys(*, is_active=None, limit=50, offset=0) |
GET …/api-keys |
ApiKeyList |
get_api_key(key_id) |
GET …/api-keys/{key_id} |
ApiKey |
set_api_key_status(key_id, *, is_active) |
PATCH …/api-keys/{key_id}/status |
ApiKey |
delete_api_key(key_id) |
DELETE …/api-keys/{key_id} |
dict |
expires_at accepts a datetime or an ISO-8601 string. Errors map to the same exception
hierarchy as Medsplain (e.g. an invalid secret key raises AuthenticationError, an
organization with no active plan raises PermissionDeniedError).
Result models
Organization — id, name, owner_email, plan_type, token_limit (int,
"Unlimited", or None), total_tokens_used, max_api_keys, api_keys_created,
subscription_start_date, subscription_end_date, is_active, created_at,
updated_at, raw.
ApiKey — id, organization_id, key_prefix, name, description,
total_tokens_used, last_used_at, is_active, expires_at, created_at, raw.
ApiKeyWithSecret — all ApiKey fields plus api_key (the full mds_live_ key, returned
only by create_api_key).
ApiKeyList — api_keys (list of ApiKey), total, raw.
Datetime fields are returned as raw ISO-8601 strings (or
None), and every model keeps the untouched payload inraw.
Platform administration
AdminClient manages the platform itself — organizations, payments, admin users, and
system config. It authenticates with a Cognito JWT access token for an admin user
(Authorization: Bearer …). A few operations require super-admin privileges; the
server enforces the role and returns 403 (PermissionDeniedError) otherwise.
from medsplain import AdminClient
# Option 1 — log in (calls POST /v1/login)
admin = AdminClient.login("admin@example.com", "password", base_url="https://host")
print(admin.tokens["refresh_token"]) # full Cognito token set is on .tokens
# Option 2 — bring your own token
admin = AdminClient(access_token="eyJ...", base_url="https://host")
with admin:
# Organizations
org = admin.create_organization(name="Acme Health", owner_email="ops@acme.com")
print(org.secret_key) # mds_org_secret_... — give this to the org owner
admin.list_organizations(is_active=True)
admin.update_organization(org.id, name="Acme Health Inc")
admin.rotate_organization_secret(org.id)
# Payments — status="paid" activates the org and applies the plan
pay = admin.create_payment(org.id, amount=499, currency="usd",
status="paid", plan_type="pro")
admin.list_payments(organization_id=org.id)
admin.update_payment(org.id, pay.id, status="refunded")
# Admin users (super-admin) and config
admin.list_admins()
admin.create_admin("newadmin@example.com") # super-admin only
admin.list_config()
admin.update_config("rate_limit_api_key_max_requests", "120") # super-admin only
admin.delete_organization(org.id)
Configuration
| Argument | Env var | Default | Notes |
|---|---|---|---|
access_token |
MEDSPLAIN_ADMIN_TOKEN |
— | Required. Admin Cognito JWT access token. |
base_url |
MEDSPLAIN_BASE_URL |
http://localhost:8000 |
API root, without the /v1 prefix. |
AdminClient.login(username, password, *, base_url=…) returns a ready client and stores the
full token set (access_token, refresh_token, id_token, is_verified) on .tokens.
Cognito access tokens expire and there is no refresh endpoint — log in again (or pass a fresh
token) when you get a 401.
Methods
| Method | API call | Role | Returns |
|---|---|---|---|
login(username, password, *, base_url=…) (classmethod) |
POST /v1/login |
— | AdminClient |
create_organization(*, name, owner_email) |
POST /v1/admin/organizations |
admin | OrganizationWithSecret |
list_organizations(*, is_active=None, limit=50, offset=0) |
GET /v1/admin/organizations |
admin | OrganizationList |
update_organization(org_id, *, name=None, owner_email=None) |
PATCH /v1/admin/organizations/{org_id} |
admin | Organization |
delete_organization(org_id) |
DELETE /v1/admin/organizations/{org_id} |
admin | dict |
rotate_organization_secret(org_id) |
POST …/{org_id}/rotate-secret |
admin | RotateSecretResult |
create_payment(org_id, *, amount, currency, status, plan_type, …) |
POST …/{org_id}/payments |
admin | Payment |
update_payment(org_id, payment_id, *, status=None, …) |
PATCH …/{org_id}/payments/{payment_id} |
admin | Payment |
list_payments(*, organization_id=None, payment_status=None, limit=50, offset=0) |
GET /v1/admin/payments |
admin | PaymentList |
list_admins(*, limit=50, offset=0) |
GET /v1/admin/admins |
admin | AdminUserList |
create_admin(email) |
POST /v1/admin/admins |
super-admin | AdminUser |
delete_admin(email) |
DELETE /v1/admin/admins |
super-admin | dict |
list_config() |
GET /v1/admin/config |
admin | ConfigList |
update_config(key, value) |
PATCH /v1/admin/config |
super-admin | ConfigEntry |
create_payment accepts status ∈ pending|paid|failed|refunded, plan_type ∈
basic|pro|enterprise, and paid_at as a datetime or ISO-8601 string. The new models
(OrganizationWithSecret, OrganizationList, RotateSecretResult, Payment, PaymentList,
AdminUser, AdminUserList, ConfigEntry, ConfigList) follow the same dataclass style —
raw ISO-8601 datetimes and a raw payload field.
Local development
cd sdk
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest # run tests
ruff check . # lint
mypy src/ # type-check
python -m build # build wheel + sdist into dist/
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
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 medsplain-1.1.1.tar.gz.
File metadata
- Download URL: medsplain-1.1.1.tar.gz
- Upload date:
- Size: 16.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
578ef14472be2dad4014c7f30eaa82ee66923accfbd5430855320d4d33183bd9
|
|
| MD5 |
8c1f06a125dac15f49555b6aa90ae029
|
|
| BLAKE2b-256 |
21ed4de2a34a715c8e68862964bb59dd12baf3b853a61a7afecb240c9b734be3
|
File details
Details for the file medsplain-1.1.1-py3-none-any.whl.
File metadata
- Download URL: medsplain-1.1.1-py3-none-any.whl
- Upload date:
- Size: 21.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ed939a2ad9c84654cb9408e11ffadc18fa8b668f345aa62c6ec33f752041cfe
|
|
| MD5 |
7e7ad026b130f8eda0c8df3de5341123
|
|
| BLAKE2b-256 |
7d3401d726dce99eb6bc85f25c6376a782d911f6ffdd81ced094d842c1618248
|