Quickstart

This guide will walk you through the basic, recommended workflow for using pymedplum, which leverages Pydantic models for a type-safe and intuitive experience.

Initializing the Client

First, create an instance of the MedplumClient or AsyncMedplumClient. Constructor arguments (other than base_url) are keyword-only; unknown kwargs raise TypeError at construction time.

from pymedplum import MedplumClient

client = MedplumClient(
    base_url="https://api.medplum.com/",
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
)

If you already have a bearer token, pass access_token= instead of the client-credentials pair.

HTTPS is required by default

PyMedplum enforces https:// on the base_url unless the host is a loopback address (127.0.0.1, ::1, localhost). If you’re running Medplum locally via Docker, http://localhost:8103/ works with no extra flag. Any other http:// URL raises InsecureTransportError unless you explicitly pass allow_insecure_http=True (not recommended in production; logs a WARNING when enabled).

Creating a Resource with Pydantic

The best way to create a resource is to instantiate its Pydantic model. This gives you autocompletion and compile-time validation in your editor.

from pymedplum.fhir import Patient

new_patient = Patient(
    name=[{"given": ["Jane"], "family": "Pydantic"}],
    gender="female",
    birth_date="1990-05-20",
)

created_patient = client.create_resource(new_patient, as_fhir=Patient)

print(f"Created patient {created_patient.id} for {created_patient.name[0].given[0]}")
assert isinstance(created_patient, Patient)
The client accepts the model instance and returns a new instance representing the resource as it was stored on the server, now including its server-assigned id.

Reading a Resource into a Model

When you read a resource, you should read it directly into its corresponding Pydantic model using the as_fhir parameter. This gives you a strongly-typed object to work with.

from pymedplum.fhir import Patient

patient = client.read_resource("Patient", "some-patient-id", as_fhir=Patient)

print(f"Patient's Gender: {patient.gender}")
if patient.birth_date:
    print(f"Birth Date: {patient.birth_date}")

Updating a Resource

update_resource auto-attaches an If-Match header from the resource’s meta.versionId by default, giving you optimistic concurrency control out of the box.

patient = client.read_resource("Patient", "some-patient-id", as_fhir=Patient)
patient.active = False

# Default: If-Match derived from meta.versionId.
updated = client.update_resource(patient)

# Opt out for last-write-wins behavior.
updated = client.update_resource(patient, if_match=False)

# Or pass a custom value verbatim.
updated = client.update_resource(patient, if_match='W/"5"')

If the server’s current version has moved on, the default path raises PreconditionFailedError rather than silently overwriting a newer revision.

Searching for Resources

The recommended way to search is to use the search_resource_pages iterator, which handles pagination for you. You can get results as dicts or as typed Pydantic models.

from pymedplum.fhir import Observation

for observation in client.search_resource_pages(
    "Observation",
    {"subject": "Patient/some-patient-id", "category": "vital-signs"},
    as_fhir=Observation,
):
    print(f"Found Observation {observation.id} with status: {observation.status}")

for observation in client.search_resource_pages(
    "Observation",
    {"subject": "Patient/some-patient-id", "category": "vital-signs"},
):
    print(f"Found Observation {observation['id']} with status: {observation['status']}")

The type-safe approach with Pydantic models provides the best developer experience with full IDE autocomplete and validation.


For more in-depth examples, see the Advanced Usage guide. To learn more about the Pydantic models and client design, see the Core Concepts section. For PHI-access audit hooks, see Audit Logging.