Quickstart
Capture contactless vital signs in five lines with the Circadify Python SDK, from a webcam, a video file, your own frames, or the sandbox.
The shortest path to a VitalSigns result: construct a CircadifyClient with a sandbox key, call measure_vitals(), and read heart_rate and confidence. The Python SDK is a full capture-capable client — it opens the camera, runs on-device face detection and ROI extraction, and ships only a cropped tensor to the server. Raw video never leaves the device.
Prerequisites
- The camera extra installed:
pip install "circadify[camera]". This pulls inopencv-python+mediapipe, whichmeasure_vitals,measure_from_video, andmeasure_from_framesrequire. The corepip install circadify(justrequests+numpy) is enough for the no-camera sandbox path below. - A sandbox API key of the form
ck_test_.... Sandbox keys are zero-quota and return deterministic synthetic vitals — ideal for a first run. Read it from the environment; never hardcode a key. - A webcam, or a sample video file, or your own frames. None of these are needed for the sandbox
scan_tensorpath.
Your first scan
This is the complete webcam-to-vitals flow. It opens the camera, runs a readiness gate, captures, and returns a VitalSigns.
from circadify import CircadifyClient
client = CircadifyClient(api_key="ck_test_...")
result = client.measure_vitals() # opens the webcam, captures, returns vitals
print(result.heart_rate, result.confidence)pythonPass your key in from the environment rather than inlining it: CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"]). client.key_environment reports "test" or "live" so you can confirm which environment you are in.
Other capture sources
The same client measures from a webcam, a video file, an iterable of frames you supply, or — with the core install only — a synthetic tensor against the sandbox. Each call returns a VitalSigns.
Capture live from the default camera. Requires the [camera] extra.
result = client.measure_vitals(camera_index=0)pythonFace-glow overlay
The SDK ships the camera / face-glow pipeline — the thermal face-mesh glow that makes a Circadify scan recognizable — and lets you build the window around it. Draw the glow on each frame with render_face_glow, fed by the on_frame callback:
import cv2
from circadify import CircadifyClient
from circadify.ui import render_face_glow
def on_frame(ev):
bgr = cv2.cvtColor(ev.rgb, cv2.COLOR_RGB2BGR) # on_frame gives RGB
render_face_glow(bgr, ev.landmarks, t=ev.collected / 30.0)
cv2.imshow("scan", bgr)
cv2.waitKey(1)
result = CircadifyClient(api_key="ck_test_...", on_frame=on_frame).measure_vitals()pythonrender_face_glow needs the [camera] extra (for OpenCV) but is a pure draw call — it works in a desktop window, Streamlit, a notebook, or a web stream. A complete example window (camera + glow + vitals sidebar) is in demo_gui.py in the test app. See Face-Glow Overlay for themes and the full pattern.
Reading the result
Every method returns a VitalSigns dataclass. heart_rate and confidence are always present; the remaining fields are optional and may be None depending on configuration. Call .to_dict() for a serializable view.
| Field | Type | Description |
|---|---|---|
heart_rate | float | Heart rate in bpm — always present. |
confidence | float | Measurement confidence, 0–1 — always present. |
respiratory_rate | Optional[float] | Breaths per minute. |
hrv | Optional[float] | Heart-rate variability (SDNN) in ms. |
spo2 | Optional[float] | Blood oxygen saturation in %. |
systolic_bp | Optional[float] | Systolic blood pressure in mmHg. |
diastolic_bp | Optional[float] | Diastolic blood pressure in mmHg. |
session_id | Optional[str] | Server session identifier for this scan. |
timestamp | float | Unix timestamp of the result. |
is_fallback | bool | True only if synthetic fallback vitals were returned. |
processing_time_ms | Optional[float] | Server-side inference time in ms. |
Treat confidence as a gate: above 0.7 the reading is reliable, and below 0.4 you should retry the scan with better lighting and less motion.
With demographics
Pass demographics to improve estimates. Methods accept a Demographics instance, a plain dict, or None.
from circadify import CircadifyClient, Demographics
client = CircadifyClient(api_key="ck_test_...")
# As a dict
result = client.measure_vitals(demographics={"age": 34, "sex": "F", "fitzpatrick": 3})
# As a Demographics object
result = client.measure_vitals(
demographics=Demographics(age=34, sex="F", fitzpatrick=3)
)pythonsex is "M" or "F" (or Sex.MALE / Sex.FEMALE), and fitzpatrick is a skin-tone scale from 1 to 6. All three fields are optional.
By default the SDK raises the real error and never fabricates vitals — a failed scan surfaces as a CircadifyError subclass, not a fake reading. Returning synthetic vitals on transient infra failures is strictly opt-in via allow_fallback_vitals=True, and those results carry is_fallback=True.
Next Steps
- Methods — Every capture and REST method on
CircadifyClient - Camera & Input — Webcam, video, frames, and tensor sources in depth
- Face-Glow Overlay —
render_face_glow, themes, and building your own scan UI - Error Handling — Catch
CircadifyError, retry on transient failures