Circadify

REST Client

Drive the raw REST session lifecycle from Python using CircadifyClient's low-level methods for headless, server, and batch workloads.

Most integrations call measure_vitals, measure_from_video, or measure_from_frames and let the SDK run the whole flow. This page is the escape hatch: drive the raw REST session lifecycle yourself when you have no camera, run on a server, batch many payloads, or supply your own transport. These methods live directly on CircadifyClient — there is no separate RestClient class — and they work on the core install with no [camera] extra.

When to use

Reach for the low-level path when the convenience methods do not fit:

  • Headless servers and batch jobs — process pre-encoded tensors with no webcam attached.
  • Custom transport or scheduling — manage the upload, retries, or polling cadence yourself.
  • Owning the session yourself — interleave the four REST calls with your own pipeline.

The full wire protocol — including the free sandbox — runs on pip install circadify (deps: requests + numpy). You only need pip install "circadify[camera]" for the capture methods (measure_vitals / measure_from_video / measure_from_frames) and the circadify.ui window.

The low-level methods

scan_tensor is the one-call path: hand it an encoded tensor and it runs start → upload → complete → poll for you, returning a VitalSigns.

import os
from circadify import CircadifyClient
from circadify.tensor import placeholder_tensor
 
client = CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"])
 
result = client.scan_tensor(placeholder_tensor())
print(result.heart_rate, result.confidence)
python

If you need to own each step, the four raw calls map one-to-one onto the REST endpoints:

MethodREST endpointReturns
start_session(demographics=None)POST /sdk/session/startSession (carries session_id and the presigned upload_url)
upload(upload_url, data)PUT to the presigned upload_urlNone
complete_upload(session_id)POST /sdk/session/upload-completeOptional[VitalSigns]
poll_result(session_id, cancel_event=None)GET /sdk/session/{id}/resultVitalSigns

complete_upload may return a VitalSigns immediately when the result is ready; otherwise it returns None and you call poll_result to block until processing finishes. Pass a threading.Event as cancel_event to abort a long poll. Catch CircadifyError around all of these — see Error Handling.

Preparing the payload

The upload payload is the SDK-prepared 12-channel tensor, not raw video — raw video never leaves the device. Build it with circadify.tensor, whose byte layout (a 36-byte little-endian header plus a 12-channel 72×72 planar-RGB body, optionally gzipped) is byte-exact across SDK versions.

FunctionPurpose
encode_tensor(...)Serialize prepared frames into the byte-exact upload payload.
decode_tensor(...)Round-trip a payload back into its array form.
pack_frame(...)Pack a single frame into the planar-RGB tensor layout.
placeholder_tensor(frame_count=1)Build a throwaway payload for sandbox testing (pass a larger frame_count, e.g. 150, to mimic a real capture).
Note

placeholder_tensor produces a valid payload with no camera or GPU. The sandbox ignores tensor content, so it is the fastest way to exercise the full wire flow end to end against a ck_test_ key.

Full lifecycle example

This drives all four raw calls explicitly. The same flow is what scan_tensor runs for you.

  1. Build and encode the tensor. Use placeholder_tensor for sandbox, or encode_tensor over your own prepared frames in production.

    import os
    from circadify import CircadifyClient
    from circadify.tensor import placeholder_tensor
     
    client = CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"])
    tensor_bytes = placeholder_tensor()
    python
  2. Start the session. start_session returns a Session carrying the session_id and the presigned upload_url.

    session = client.start_session()
    python
  3. Upload the payload. PUT the tensor bytes to the presigned URL.

    client.upload(session.upload_url, tensor_bytes)
    python
  4. Complete the upload. This may return a VitalSigns immediately, or None if processing is still running.

    result = client.complete_upload(session.session_id)
    python
  5. Poll for the result. If complete_upload returned None, block until processing finishes.

    if result is None:
        result = client.poll_result(session.session_id)
     
    print(result.heart_rate, result.confidence)
    client.close()
    python

CircadifyClient is a context manager, so you can let with close it for you instead of calling client.close():

import os
from circadify import CircadifyClient
from circadify.tensor import placeholder_tensor
 
with CircadifyClient(api_key=os.environ["CIRCADIFY_API_KEY"]) as client:
    result = client.scan_tensor(placeholder_tensor())
    print(result.to_dict())
python

Sandbox

A ck_test_ key plus placeholder_tensor() returns deterministic synthetic vitals with no camera and no GPU. It is free, zero quota, and ideal for CI or wiring up your transport before you have real capture in place. client.key_environment reports "test" or "live" so you can assert which path you are on.

import os
from circadify import CircadifyClient
from circadify.tensor import placeholder_tensor
 
client = CircadifyClient(api_key="ck_test_your_key_here")  # never hardcode in real code
assert client.key_environment == "test"
 
result = client.scan_tensor(placeholder_tensor())
print(result.is_fallback, result.heart_rate)
python

Authentication

Authenticate every call with your ck_* API key, sent as the X-API-Key header (the SDK adds it for you). Read it from the environment rather than hardcoding it.

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

A malformed key raises InvalidApiKeyError; an empty key raises MissingApiKeyError. After any call, client.last_rate_limit holds a RateLimitInfo or None. See Authentication for key types and rotation.

Caution

POST /sdk/session/start consumes 1 scan of quota per call on a live (ck_live_) key — start_session and scan_tensor both hit it. Never loop these to probe the API. Use a ck_test_ key for any testing: the sandbox is free and zero quota.

Next Steps

  • REST API Overview — the wire protocol these methods speak.
  • Sessions — the four endpoints in full HTTP detail.
  • Methods — the higher-level capture methods most integrations use.
  • Error Handling — catch CircadifyError and read .code / .retryable.