Methods
Reference for every public method and result type on the Circadify Python SDK client.
Reference for the public API of CircadifyClient. The Python SDK is a full capture-capable client: it captures from a webcam, a video file, or your own frames, runs on-device face detection and ROI extraction, and ships only the cropped tensor to the server for GPU inference — raw video never leaves the device. It also exposes a core-only REST/tensor path for headless and batch work. Every method below lives on a single CircadifyClient instance.
from circadify import CircadifyClient
client = CircadifyClient(api_key="ck_test_your_key_here")
result = client.measure_vitals()
print(result.heart_rate, result.confidence)pythonBy default the SDK never fabricates vitals. On any failure it raises the real CircadifyError and returns nothing. Pass allow_fallback_vitals=True only if you explicitly want synthetic vitals (with is_fallback=True) on transient infrastructure failures.
CircadifyClient(api_key, ...)
Construct the client with your API key. Use a ck_test_ key for the free sandbox (zero quota, deterministic synthetic vitals) or a ck_live_ key for metered production. Never hardcode a key — read it from the environment.
import os
from circadify import CircadifyClient
client = CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"])
print(client.key_environment) # "test" or "live"pythonA malformed key raises InvalidApiKeyError; an empty key raises MissingApiKeyError. The constructor takes many keyword-only options (timeouts, polling, callbacks, fallback policy). See Configuration for the full parameter list.
Parameters
| Parameter | Type | Description |
|---|---|---|
api_key | str | Required. A ck_test_ or ck_live_ key. |
* | — | All remaining parameters are keyword-only — see Configuration. |
measure_vitals(demographics=None, camera_index=0, cancel_event=None)
Opens the webcam, runs the readiness gate, captures the measurement window, and returns vital signs. Requires the [camera] extra.
from circadify import CircadifyClient, Demographics
with CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"]) as client:
result = client.measure_vitals(
demographics=Demographics(age=35, sex="M", fitzpatrick=3),
)
print(result.heart_rate, result.confidence)pythonParameters
| Parameter | Type | Description |
|---|---|---|
demographics | Demographics | dict | None | Optional age/sex/Fitzpatrick for improved accuracy. Accepts a Demographics, a dict, or None. |
camera_index | int | OpenCV camera index. Defaults to 0. |
cancel_event | threading.Event | None | Set to abort the scan from another thread. |
Returns: VitalSigns.
Errors: CameraError, MissingDependencyError (if [camera] is not installed), CaptureFailedError, ScanTimeoutError, ScanCancelledError, or any CircadifyError subclass on the wire.
measure_from_video(path, demographics=None, cancel_event=None)
Runs the full measurement flow against a video file using native Python decoding. Requires the [camera] extra. The same on-device extraction applies — only the tensor is uploaded.
result = client.measure_from_video("subject.mp4")
print(result.heart_rate)pythonParameters
| Parameter | Type | Description |
|---|---|---|
path | str | Required. Path to a video file. |
demographics | Demographics | dict | None | Optional demographics. |
cancel_event | threading.Event | None | Set to abort the scan from another thread. |
Returns: VitalSigns.
Errors: MissingDependencyError, CaptureFailedError, ScanTimeoutError, ScanCancelledError, or any CircadifyError subclass.
measure_from_frames(frames, fps=30, demographics=None, assume_bgr=False, cancel_event=None)
Measures from an iterable of [H, W, 3] images you already hold in memory. Requires the [camera] extra. Set assume_bgr=True when the frames come from OpenCV (BGR order).
result = client.measure_from_frames(my_frames, fps=30, assume_bgr=True)
print(result.heart_rate)pythonParameters
| Parameter | Type | Description |
|---|---|---|
frames | Iterable[ndarray] | Required. Iterable of [H, W, 3] images. |
fps | float | Frame rate of the source. Defaults to 30.0 (config.TARGET_FPS). |
demographics | Demographics | dict | None | Optional demographics. |
assume_bgr | bool | True if frames are BGR (OpenCV). Defaults to False (RGB). |
cancel_event | threading.Event | None | Set to abort the scan from another thread. |
Returns: VitalSigns.
Errors: MissingDependencyError, CaptureFailedError, ScanTimeoutError, ScanCancelledError, or any CircadifyError subclass.
scan_tensor(tensor_bytes, demographics=None, cancel_event=None)
Runs the full wire flow for an already-encoded tensor. This is the core-only path: it needs only the base requests + numpy dependencies — no camera, no [camera] extra — so it works headless and against the free sandbox.
from circadify import CircadifyClient
from circadify.tensor import placeholder_tensor
with CircadifyClient(api_key="ck_test_your_key_here") as client:
result = client.scan_tensor(placeholder_tensor(frame_count=150))
print(result.heart_rate, result.is_fallback)pythonParameters
| Parameter | Type | Description |
|---|---|---|
tensor_bytes | bytes | Required. An encoded 12-channel tensor payload. |
demographics | Demographics | dict | None | Optional demographics. |
cancel_event | threading.Event | None | Set to abort from another thread. |
Returns: VitalSigns.
Errors: ProcessingFailedError, ScanTimeoutError, ScanCancelledError, or any CircadifyError subclass.
Raw REST methods
start_session, upload, complete_upload, and poll_result expose the four underlying REST calls on the same client, for when you need to drive the protocol step by step. There is no separate REST client class — these live on CircadifyClient.
session = client.start_session(demographics={"age": 35, "sex": "M"})
client.upload(session.upload_url, tensor_bytes)
client.complete_upload(session.session_id)
result = client.poll_result(session.session_id)pythonParameters
| Method | Signature | Returns |
|---|---|---|
start_session | start_session(demographics=None) | Session |
upload | upload(upload_url, data) | None |
complete_upload | complete_upload(session_id) | Optional[VitalSigns] |
poll_result | poll_result(session_id, cancel_event=None) | VitalSigns |
Errors: SessionNotFoundError, SessionExpiredError, UploadFailedError, ProcessingFailedError, plus the shared CircadifyError subclasses. See the REST Client page for the full step-by-step flow.
check_camera_access(camera_index=0)
Probes whether a camera is available and openable without starting a scan. Requires the [camera] extra.
if client.check_camera_access():
result = client.measure_vitals()pythonParameters
| Parameter | Type | Description |
|---|---|---|
camera_index | int | OpenCV camera index to probe. Defaults to 0. |
Returns: bool — True if the camera can be opened.
cancel() and reset_cancel()
cancel() signals an in-progress measurement to abort; the running call raises ScanCancelledError. reset_cancel() clears the flag so the client can be reused for the next scan.
import threading
from circadify import CircadifyClient, ScanCancelledError
client = CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"])
threading.Timer(5.0, client.cancel).start() # cancel after 5s
try:
result = client.measure_vitals()
except ScanCancelledError:
print("Scan cancelled")
client.reset_cancel() # ready for another scanpythonReturns: None for both.
For multi-threaded apps, prefer passing a cancel_event to the measurement method — it scopes cancellation to that single call. Use cancel() / reset_cancel() for simple single-scan flows.
close() and the context-manager form
close() releases the client's network resources. CircadifyClient is also a context manager, so the cleanest pattern is a with block that closes automatically on exit.
# explicit
client = CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"])
try:
result = client.measure_vitals()
finally:
client.close()
# preferred — context manager
with CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"]) as client:
result = client.measure_vitals()pythonReturns: None.
last_rate_limit
After any call, client.last_rate_limit holds a RateLimitInfo describing the server's rate-limit headers from the most recent request, or None if no request has been made yet.
result = client.measure_vitals()
info = client.last_rate_limit
if info is not None:
print(info) # remaining / limit / reset for the last requestpythonReturns: RateLimitInfo | None.
VitalSigns
The result dataclass returned by every measurement method. Fields use snake_case. heart_rate and confidence are always present; the remaining clinical fields are optional and may be None depending on configuration.
| Field | Type | Unit / Notes |
|---|---|---|
heart_rate | float | bpm — always present. |
confidence | float | 0–1 reliability — always present. |
respiratory_rate | Optional[float] | breaths/min. |
hrv | Optional[float] | ms (SDNN). |
spo2 | Optional[float] | %. |
systolic_bp | Optional[float] | mmHg. |
diastolic_bp | Optional[float] | mmHg. |
session_id | Optional[str] | Server session identifier. |
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. |
Use .to_dict() to serialize the result (for logging, JSON responses, or storage):
result = client.measure_vitals()
payload = result.to_dict() # plain dict, ready for json.dumpspythonA confidence above 0.7 indicates a reliable measurement. Below 0.4 suggests quality issues — prompt the user to retry under better lighting and with less motion.
Next Steps
- Configuration — Constructor options, timeouts, and callbacks.
- Camera & Input — Webcam, video, and frame capture modes.
- Face-Glow Overlay — The optional
render_face_glowcamera/glow pipeline. - Error Handling — Catch and recover from every
CircadifyError.