Circadify

Configuration

Configure the Circadify Python SDK with API keys, callbacks, capture options, and demographics.

Create one CircadifyClient per process or measurement flow. Only api_key is required — everything else has a sensible default. Never hardcode a key; read it from the environment.

Initialization

The minimal form takes only an API key:

import os
from circadify import CircadifyClient
 
client = CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"])
python

For full control, pass any of the keyword-only options. All arguments after api_key are keyword-only:

import os
from circadify import CircadifyClient
 
client = CircadifyClient(
    api_key=os.environ["CIRCADIFY_API_KEY"],  # "ck_test_..." or "ck_live_..."
    base_url="https://api.circadify.com",
    timeout=30.0,
    poll_interval=2.0,
    poll_timeout=120.0,
    measurement_duration=20.0,
    allow_fallback_vitals=False,
    on_progress=lambda p: print(p.phase, p.percent),
    on_landmarks=None,
    on_quality=None,
    on_frame=None,
    model_asset_path=None,
    debug=False,
)
python

CircadifyClient is a context manager, so prefer with to release the camera and HTTP session deterministically:

with CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"]) as client:
    result = client.measure_vitals()
    print(result.heart_rate, result.confidence)
python

A ck_test_* key targets the free sandbox (zero quota, deterministic synthetic vitals); a ck_live_* key is metered. Read client.key_environment ("test" or "live") to confirm. A malformed key raises InvalidApiKeyError; an empty key raises MissingApiKeyError.

Configuration reference

OptionTypeDefaultDescription
api_keystrRequired. Your API key, ck_test_* (sandbox) or ck_live_* (metered). Never hardcode it.
base_urlstr"https://api.circadify.com"API base URL.
timeoutfloat30.0Per-request HTTP timeout in seconds.
poll_intervalfloat2.0Seconds between result polls while the server runs inference.
poll_timeoutfloat120.0Maximum seconds to wait for a result before raising ScanTimeoutError.
measurement_durationfloat20.0Target capture window in seconds. Also sets the capture watchdog (measurement_duration + 10s).
allow_fallback_vitalsboolFalseOpt-in. When True, transient infra failures return synthetic vitals with is_fallback=True instead of raising. By default the SDK raises the real error and never fabricates vitals.
model_asset_pathstr | NonePath to a local MediaPipe face-landmarker .task asset. Leave unset to auto-download the default model on first use and cache it locally (e.g. for air-gapped installs, point this at a vendored copy).
debugboolFalseEnables verbose SDK (Python) logging. Does not silence MediaPipe/TFLite/OpenGL native logs — see Quieting native logs.

After any call, client.last_rate_limit holds a RateLimitInfo or None.

Quieting native logs

On import and during a scan, MediaPipe / TFLite / OpenGL emit C++ banner lines to the console (e.g. Created TensorFlow Lite XNNPACK delegate for CPU, a GL version … line, init-domain / fiber-init lines). These come from the native libraries, not the SDK's Python logger — so debug=False does not silence them. Set these environment variables before importing circadify (or cv2 / mediapipe), since the native libraries read them at load time:

import os
os.environ["GLOG_minloglevel"] = "2"      # MediaPipe / abseil C++ logs (0=info … 3=fatal)
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"  # TFLite / XNNPACK delegate logs
 
from circadify import CircadifyClient  # import AFTER setting the vars above
python

Callbacks

Pass callbacks to drive a scan UI around the capture engine. All four are optional.

Caution

Callbacks fire on the capture worker thread, not the main thread. Marshal back to your UI thread before touching UI state. The SDK swallows any exception raised inside a callback, so a callback bug fails silently rather than aborting the scan.

on_progress — ScanProgress

FieldTypeDescription
phasePhaseCurrent capture phase.
percentfloatOverall progress, 0–100.
elapsedfloatSeconds elapsed in the current capture.
remainingfloatEstimated seconds remaining.
messagestrHuman-readable status string.

on_landmarks

Receives a list of (x, y) face-landmark pixel coordinates per frame. Use it to render a custom overlay.

on_quality — QualityState

FieldTypeDescription
face_detectedboolWhether a face is present in the frame.
brightnessfloatEstimated frame brightness.
motionfloatEstimated subject/camera motion.
yaw_degfloatHead yaw in degrees.
pitch_degfloatHead pitch in degrees.
is_goodboolWhether the frame meets quality gates.
reasonstuple[str, ...]Failing reasons, e.g. no_face, face_out_of_frame, too_dark, too_bright, too_much_motion, face_turned, face_tilted.

on_frame — FrameEvent

FieldTypeDescription
rgbnumpy.ndarrayThe current [H, W, 3] RGB frame.
phasePhaseCurrent capture phase.
landmarkslist(x, y) landmark coordinates for this frame.
qualityQualityStateQuality assessment for this frame.
collectedintFrames accepted so far.
targetintTarget frame count for this capture.

Phase enum

PhaseDescription
INITIALIZINGCreating a session and preparing capture.
READINESSWaiting for a scan-ready face and acceptable quality.
CAPTURINGCapturing the measurement.
UPLOADINGUploading the cropped tensor.
PROCESSINGWaiting for server inference.
DONEResult is ready.

Capture policy

The frame-rate target, minimum frame count, readiness gate, and watchdogs are fixed and tuned for measurement quality. They are not constructor options.

Note

Capture policy is fixed internal behavior, not constructor arguments. Some values are constants in circadify.config (the ~30 fps capture target TARGET_FPS, and MIN_CAPTURE_FRAMES=150); others are literals in the capture loop (a readiness gate of ≤30s before capture begins, a capture watchdog of measurement_duration + 10s, and a 3s frame-stall watchdog). Adjust the capture window via the measurement_duration constructor option; the rest are not user-configurable.

Demographics

Demographics are optional but can improve measurement accuracy when provided. Every measurement method accepts a Demographics, a plain dict, or None.

from circadify import Demographics, Sex
 
demographics = Demographics(age=35, sex=Sex.MALE, fitzpatrick=3)
result = client.measure_vitals(demographics=demographics)
python
FieldTypeNotes
ageint | NoneUser age in years.
sexstr | None"M" or "F" (or Sex.MALE / Sex.FEMALE).
fitzpatrickint | NoneFitzpatrick skin type, 16.

You can also pass the dict form directly:

result = client.measure_vitals(
    demographics={"age": 35, "sex": "M", "fitzpatrick": 3},
)
python
Tip

Keep timeout, poll_interval, poll_timeout, and measurement_duration at their defaults unless you have validated different values against your own latency and accuracy targets. The defaults are tuned for the standard capture flow.

Next Steps

  • Methods — every measurement and REST method on CircadifyClient.
  • Face-Glow Overlay — the optional render_face_glow camera/glow pipeline you compose your own UI around.
  • Camera & Input — webcam, video file, and your-own-frames input modes.
  • Error Handling — catch CircadifyError, inspect .code, and retry transient failures.