Circadify

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)
python
Caution

By 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"
python

A 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

ParameterTypeDescription
api_keystrRequired. 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)
python

Parameters

ParameterTypeDescription
demographicsDemographics | dict | NoneOptional age/sex/Fitzpatrick for improved accuracy. Accepts a Demographics, a dict, or None.
camera_indexintOpenCV camera index. Defaults to 0.
cancel_eventthreading.Event | NoneSet 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)
python

Parameters

ParameterTypeDescription
pathstrRequired. Path to a video file.
demographicsDemographics | dict | NoneOptional demographics.
cancel_eventthreading.Event | NoneSet 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)
python

Parameters

ParameterTypeDescription
framesIterable[ndarray]Required. Iterable of [H, W, 3] images.
fpsfloatFrame rate of the source. Defaults to 30.0 (config.TARGET_FPS).
demographicsDemographics | dict | NoneOptional demographics.
assume_bgrboolTrue if frames are BGR (OpenCV). Defaults to False (RGB).
cancel_eventthreading.Event | NoneSet 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)
python

Parameters

ParameterTypeDescription
tensor_bytesbytesRequired. An encoded 12-channel tensor payload.
demographicsDemographics | dict | NoneOptional demographics.
cancel_eventthreading.Event | NoneSet 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)
python

Parameters

MethodSignatureReturns
start_sessionstart_session(demographics=None)Session
uploadupload(upload_url, data)None
complete_uploadcomplete_upload(session_id)Optional[VitalSigns]
poll_resultpoll_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()
python

Parameters

ParameterTypeDescription
camera_indexintOpenCV camera index to probe. Defaults to 0.

Returns: boolTrue 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 scan
python

Returns: None for both.

Tip

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()
python

Returns: 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 request
python

Returns: 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.

FieldTypeUnit / Notes
heart_ratefloatbpm — always present.
confidencefloat0–1 reliability — always present.
respiratory_rateOptional[float]breaths/min.
hrvOptional[float]ms (SDNN).
spo2Optional[float]%.
systolic_bpOptional[float]mmHg.
diastolic_bpOptional[float]mmHg.
session_idOptional[str]Server session identifier.
timestampfloatUnix timestamp of the result.
is_fallbackboolTrue only if synthetic fallback vitals were returned.
processing_time_msOptional[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.dumps
python
Tip

A 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