modelhealth
Model Health Python SDK.
Biomechanical analysis from smartphone videos.
Example:
from modelhealth import ModelHealthClient, SubjectParameters client = ModelHealthClient("your_api_key") sessions = client.session_list() subjects = client.subjects.list().all()
Client for the Model Health biomechanical analysis platform.
Enables you to measure and analyze human movement from smartphone videos. Provides a complete workflow for:
- Initialization: Create a client with your API key
- Session Creation: Create a session
- Camera Calibration: Calibrate cameras using a checkerboard pattern
- Subject Creation: Create a subject profile with anthropometric measurements
- Subject Calibration: Calibrate subject using videos of a neutral pose
- Movement Recording: Record movement activities (squats, jumps, etc.)
- Movement Analysis: Trigger advanced analysis and fetch processed biomechanical data
All methods are synchronous and block until the underlying operation completes. Each instance owns a dedicated single-threaded async runtime so instances may be used across threads, but a single instance should not be called concurrently from multiple threads.
Attributes:
- activities: Filtered access to activities — see
ActivitiesResource. - subjects: Filtered access to subjects — see
SubjectsResource. - sessions: Filtered access to sessions — see
SessionsResource. - groups: Filtered access to subject groups — see
GroupsResource.
Example:
from modelhealth import ( ModelHealthClient, SubjectParameters, CheckerboardDetails, CheckerboardPlacement, ActivityType, ) client = ModelHealthClient("your_api_key") # Create session and calibrate cameras session = client.create_session() details = CheckerboardDetails(rows=4, columns=5, square_size=35, placement=CheckerboardPlacement.perpendicular) client.calibrate_camera(session, details, lambda s: print(s)) # Create subject and calibrate params = SubjectParameters(name="Jane Doe", weight=65.0, height=170.0, birth_year=1990) subject = client.create_subject(params) client.calibrate_subject(subject, session, lambda s: print(s)) # Record a movement activity activity = client.start_recording("cmj", session) # Subject performs movement... client.stop_recording(session)
Initializes the client.
Arguments:
- api_key: Your Model Health API key, available in the dashboard at modelhealth.io.
- timeout: Per-request timeout in seconds for JSON API calls. Bulk transfers (archive downloads, video uploads) are not bounded by it. If omitted, the build's default is used.
- max_retries: Number of retries on transient network or request failures. If omitted, the build's default is used.
Raises:
- AuthenticationError: If the API key is not valid.
Example:
from modelhealth import ModelHealthClient client = ModelHealthClient("your_api_key") # List sessions and subjects sessions = client.session_list() subjects = client.subjects.list().all()
Verifies the API key and returns information about the authenticated account.
A cheap way to check that the API key is valid without performing a domain operation.
Returns:
An
AccountInfowith the authenticated account's identity and licensing details.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
Example:
info = client.account_info() print(f"Authenticated as {info.email}")
Returns the current billing/quota state for the authenticated account.
Use this to show usage bars and plan limits, and to block recording in your own UI when the quota is exhausted or the subscription has lapsed.
reason explains why recording_allowed is False, and is only set
in that case. Most other detail fields (reason, activities_used,
activities_max, period_end, plan_name, reset_period) are
None when there is no active plan. An activities_max of None
while a plan is active means the plan is unlimited.
Returns:
A
UsageInfodescribing the account's current billing and quota state.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
Example:
usage = client.usage() if not usage.recording_allowed: print(f"Recording blocked: {usage.reason}")
Retrieves all sessions for the account associated with the API key.
Use this to list existing sessions before creating a new one, or to resume a previous capture workflow.
To connect a device to a specific session, use the session qrcode
URL to download the QR code image data, then display it in your app
to be captured by the ModelHealth mobile app.
Returns:
A list of
Sessionobjects, or an empty list if none exist.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
import urllib.request sessions = client.session_list() print(f"Found {len(sessions)} sessions") if sessions: first = sessions[0] with urllib.request.urlopen(first.qrcode) as resp: qr_image_data = resp.read() # Display qr_image_data in your app so the mobile app can scan it
Retrieve a specific session by ID with all activities populated.
Arguments:
- session_id: The
idof the session to retrieve.
Returns:
A
Sessionwithactivitiespopulated.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NotFoundError: If the session does not exist.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
session = client.get_session("abc123") for activity in session.activities: print(f"{activity.name or activity.id}")
Points the cameras at the session to record subject in.
Use this to move between subjects in any order, including going back to
someone recorded earlier. The session already recording that subject is
reused when its camera calibration and static pose are both still good,
so the pose does not have to be repeated. Otherwise a new session is
started from session, exactly as
new_session_from_session() would.
Arguments:
- subject: The subject to record next.
- session: The session the cameras are pointed at now.
Returns:
The
Sessionto record in. May be one that already existed, the one passed in, or a new one.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NotFoundError: If the session or subject does not exist.
- UnsupportedOperationError: If the API version in use cannot hand the cameras back to an existing session.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
# Everyone does exercise 1, then everyone does exercise 2 for exercise in ("CMJ", "Squat"): for subject in subjects: session = client.switch_subject(subject, session) activity = client.start_recording(exercise, session) client.stop_recording(session)
Creates a new session from a previous session, inheriting its calibration setup.
Use this to move directly to calibrating a new subject without repeating camera connection or checkerboard calibration.
Arguments:
- session: The previous, already-calibrated session.
Returns:
A new
Sessionready for a new subject.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NotFoundError: If the session does not exist.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
new_session = client.new_session_from_session(previous_session)
Creates a session.
A session is the parent container for a movement capture workflow. It links related entities such as activities and subjects and provides the context used by subsequent operations.
Default session settings are applied automatically. Call
configure_session() afterwards to override specific settings.
Returns:
A new
Sessionwith a unique identifier.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
session = client.create_session()
Applies settings to an existing session.
Call this after create_session() and before calibration to
override any default settings. If not called, the session retains the
defaults applied during creation.
All parameters are optional — omit any to keep its default.
Arguments:
- session: The session to configure.
- framerate: Camera frame rate. Default:
SessionFramerate.fps_120. - opensim_model: OpenSim model. Default:
SessionOpenSimModel.lai_uhlrich_2022. - scaling_setup: Scaling pose. Default:
SessionScalingSetup.upright_standing_pose. - core_engine: Core engine version. Default:
SessionCoreEngine.v1_0. - filter_frequency: Low-pass filter frequency. Default:
FilterFrequencyDefault(). - data_sharing: Data-sharing preference. Default:
SessionDataSharing.share_processed_data_and_identified_videos.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
session = client.create_session() client.configure_session( session, framerate=SessionFramerate.fps_60, data_sharing=SessionDataSharing.share_no_data, )
Retrieves a subject by its ID.
Use this to resolve a subject ID (e.g. from Session.subject)
into full subject details without fetching the entire subject list.
Arguments:
- subject_id: The unique identifier of the subject.
Returns:
The
Subjectwith its current details.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NotFoundError: If the subject does not exist.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
subject = client.fetch_subject(session.subject) print(f"Subject: {subject.name}")
Creates a subject profile.
Height and weight are required for biomechanical analysis. Once
created, the subject can be calibrated using
calibrate_subject().
Arguments:
- parameters: The subject's profile details including name and anthropometrics.
Returns:
The newly created
Subjectwith its assigned ID.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On validation failure, request failure or an unexpected response.
Example:
from modelhealth import SubjectParameters params = SubjectParameters( name="John Smith", weight=75.0, height=180.0, birth_year=1990, ) subject = client.create_subject(params) print(f"Created subject with ID: {subject.id}") # Use the subject for calibration client.calibrate_subject(subject, session, lambda s: print(s))
Retrieves an activity by its ID.
Use this to fetch the latest state of an activity, including its videos, results and current processing status.
Arguments:
- activity_id: The unique identifier of the activity.
Returns:
The
Activitywith its current details.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NotFoundError: If the activity does not exist.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
activity = client.fetch_activity("abc123") print(f"Activity: {activity.name or 'Unnamed'}") print(f"Status: {activity.status}")
Updates an activity.
Only mutable fields (such as name) are applied.
Use the returned object rather than the input going forward.
Arguments:
- activity: The
Activityto update, with modified properties. - config: Optional config to apply alongside the update. Supports
add_tags/remove_tagsto modify tags andnameto rename.
Returns:
The updated
Activityas stored.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NotFoundError: If the activity does not exist.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Deletes an activity.
Permanently removes the activity and all associated videos, results, and metadata.
Arguments:
- activity: The
Activityto delete.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NotFoundError: If the activity does not exist.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
client.delete_activity(activity)
Warning:
This operation is irreversible.
Retrieves all movement activities associated with a session.
Activities represent individual recording trials and contain references to captured videos and results. Use this to review past data or fetch results for completed activities.
Arguments:
- session: The session whose activities to retrieve.
Returns:
A list of
Activityobjects, or an empty list if none exist.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
activities = client.activity_list(session) for activity in activities: print(f"{activity.name or activity.id}") print(f" Videos: {len(activity.videos)}") print(f" Results: {len(activity.results)}")
Uploads external files (e.g. sensor data) to an activity.
Posts each file as a separate result entry and returns a refreshed activity reflecting the new results. All files are uploaded in parallel.
Arguments:
- activity: The
Activityto attach external files to. Must have completed processing (ActivityStatus.ready). - files: One or more
ExternalResultFileinstances, with tag, file extension and data.
Returns:
A refreshed
Activitycontaining the new result entries.
Raises:
- ModelHealthError: If any tag is reserved, empty or duplicated, or if any file exceeds the 100 MB size limit.
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
from modelhealth import ExternalResultFile with open("sensor_data.csv", "rb") as f: csv_data = f.read() file = ExternalResultFile("acme-sensor", "csv", csv_data) activity = client.add_motion_data_to_activity(activity, [file])
Downloads video data for a specific activity.
Fetches all videos associated with the activity that match the specified version. Videos with invalid URLs or failed downloads are silently excluded from the result.
Arguments:
- activity: The
Activitywhose videos to download. - version:
VideoVersion.rawfor original per-camera recordings, orVideoVersion.syncedfor temporally-synchronised output.
Returns:
A list of raw video bytes (one per camera). Write each element to a
.mp4file for playback. May be empty if no videos are available or all downloads fail.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
videos = client.videos_for_activity(activity, VideoVersion.synced) for i, video in enumerate(videos): with open(f"video_{i}.mp4", "wb") as f: f.write(video)
Downloads motion data from a processed activity.
Use this after an activity reaches ActivityStatus.ready to
retrieve biomechanical result files such as kinematics, marker
data, or an OpenSim model. Failed downloads are silently excluded
from results.
Arguments:
- activity: The
Activitywhose results to download. Must have completed processing. - data_types: Motion data types to fetch. Valid values:
"animation","kinematics_mot","kinematics_csv","markers_trc","markers_csv","model".
Returns:
A list of
MotionDataobjects, one per successfully downloaded type. May be empty if no results are available or all downloads fail.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
results = client.motion_data_for_activity( activity, [MotionDataType.kinematics_mot, MotionDataType.animation] ) for r in results: if r.type == "kinematics_mot": with open("kinematics.mot", "wb") as f: f.write(r.data) # MOT format elif r.type == "animation": import json transforms = json.loads(r.data) # JSON format elif r.type == "markers_trc": with open("markers.trc", "wb") as f: f.write(r.data) # TRC format elif r.type == "model": with open("model.osim", "wb") as f: f.write(r.data) # OSim format
Downloads result data for an activity with a completed analysis.
Call this after analysis_status() returns
AnalysisStatus.completed to retrieve metrics, a report, or raw
data. Failed downloads are silently excluded from results.
Arguments:
- activity: The
Activitywhose analysis results to download. Must have a completed analysis. - data_types: Analysis result types to fetch. Valid values:
"metrics"(JSON),"report"(PDF),"data"(ZIP).
Returns:
A list of
AnalysisDataobjects, one per successfully downloaded type. May be empty if no results are available or all downloads fail.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
import json results = client.analysis_data_for_activity( activity, [AnalysisDataType.metrics, AnalysisDataType.report, AnalysisDataType.data] ) for r in results: if r.type == "metrics": metrics = json.loads(r.data) elif r.type == "report": with open("report.pdf", "wb") as f: f.write(r.data) elif r.type == "data": with open("data.zip", "wb") as f: f.write(r.data)
Retrieves the current processing status of an activity.
Poll this method after recording stops to track upload and processing progress.
Returns ActivityStatusUploading when videos are uploading,
ActivityStatusAnalyzing (with the analysis task) when automatic
analysis is running, or an ActivityStatus enum value otherwise.
Arguments:
- activity: The
Activityto check status for.
Returns:
The current status as
ActivityStatusUploading,ActivityStatusAnalyzing, orActivityStatus.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
status = client.activity_status(activity) if isinstance(status, ActivityStatusUploading): print(f"Uploading: {status.uploaded}/{status.total}") elif status == ActivityStatus.processing: print("Still processing...") elif isinstance(status, ActivityStatusAnalyzing): analysis_status = client.analysis_status(status.task) elif status == ActivityStatus.ready: print("Activity ready for analysis") elif status == ActivityStatus.failed: print("Processing failed")
Starts an analysis task for an activity that is ready for analysis.
Call this after activity_status() returns
ActivityStatus.ready. Use the returned Analysis with
analysis_status() to poll progress.
Arguments:
- activity_type: The analysis algorithm to run.
Use a
ActivityTypemember. - activity: The
Activityto analyze. - session: The
Sessioncontaining the activity.
Returns:
An
Analysisfor tracking analysis progress.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If the activity is not ready or the request fails.
Example:
task = client.start_analysis( ActivityType.counter_movement_jump, activity, session ) status = client.analysis_status(task)
Retrieves the current status of an analysis task.
Poll this method after start_analysis() to monitor progress.
When status reaches AnalysisStatus.completed, download results
with analysis_data_for_activity().
Arguments:
- task: The
Analysistask returned bystart_analysis().
Returns:
The current
AnalysisStatus.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
import time status = client.analysis_status(task) while status == AnalysisStatus.processing: time.sleep(10) status = client.analysis_status(task) if status == AnalysisStatus.completed: results = client.analysis_data_for_activity( activity, [AnalysisDataType.metrics, AnalysisDataType.report] ) elif status == AnalysisStatus.failed: print("Analysis failed")
Creates an activity and starts recording a dynamic movement trial.
Must be called after both camera and subject calibration are complete.
Call stop_recording() when the subject has finished the movement.
Arguments:
- name: A descriptive name for this activity (e.g.
"cmj","squat"). - session: The session this activity is associated with.
- config: Optional recording configuration.
Returns:
The newly created
Activity.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If recording cannot start (e.g. missing calibration).
Example:
activity = client.start_recording("cmj", session, ActivityConfig(ActivityType.counter_movement_jump)) # Subject performs movement... client.stop_recording(session)
Stops the active recording for a movement trial.
Call this after the subject has completed the movement. Once stopped,
the recorded videos begin uploading and can be tracked with
activity_status().
Arguments:
- session: The session context to stop recording in.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If there is no active recording or the request fails.
Example:
client.stop_recording(session)
Calibrates cameras using a checkerboard pattern.
Determines each camera's position and orientation in 3D space (extrinsics), enabling reconstruction of real-world movement from multiple 2D video feeds. Required once per session setup — recalibrate only if cameras are moved.
Note:
rowsandcolumnsrefer to internal corners, not squares. A 5×6 board has 4 internal corner rows and 5 internal corner columns.
This method blocks until calibration completes. The status_callback
is called synchronously on each status update during the operation.
Arguments:
- session: The session context in which calibration is performed.
- checkerboard: The checkerboard dimensions and placement.
- status_callback: Callable receiving a
CalibrationStatus,CalibrationStatusUploadingorCalibrationStatusProcessingon each update.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If calibration fails (e.g. pattern not detected).
Example:
from modelhealth import CheckerboardDetails, CheckerboardPlacement details = CheckerboardDetails( rows=4, # Internal corners, not squares (for 5×6 board) columns=5, # Internal corners, not squares (for 5×6 board) square_size=35, # Measured in millimeters placement=CheckerboardPlacement.perpendicular, ) client.calibrate_camera(session, details, lambda s: print(s))
Calibrates a subject by recording a neutral standing pose.
Scales the 3D biomechanical model to the subject's body size. Must be run after camera calibration and requires the subject profile to include height and weight.
Important:
The subject must stand upright, feet pointing forward, completely still and fully visible to all cameras for the duration of the recording.
This method blocks until calibration completes. The status_callback
is called synchronously on each status update during the operation.
Arguments:
- subject: The subject to calibrate.
- session: The session context in which calibration is performed.
- status_callback: Callable receiving a
CalibrationStatus,CalibrationStatusUploadingorCalibrationStatusProcessingon each update.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If calibration fails.
Example:
client.calibrate_subject(subject, session, lambda s: print(s))
Starts preparing a session archive for download.
Packaging the session data into a ZIP file starts in the background.
Poll archive_status() until status reaches
ArchiveStatus.ready, then call archive_data() to download
the ZIP file.
Arguments:
- session: The
Sessionto archive. - with_videos: If
True, include raw video files in the archive. Defaults toFalse.
Returns:
An
Archivefor tracking preparation progress.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
import time archive = client.prepare_archive(session) status = client.archive_status(archive) while status == ArchiveStatus.processing: time.sleep(5) status = client.archive_status(archive) if status == ArchiveStatus.ready: data = client.archive_data(archive) with open("session.zip", "wb") as f: f.write(data)
Retrieves the current preparation status of an archive task.
Poll this method after prepare_archive() to monitor progress.
When status reaches ArchiveStatus.ready, download the archive
with archive_data().
Arguments:
- archive: The
Archivetask returned byprepare_archive().
Returns:
The current
ArchiveStatus.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
status = client.archive_status(archive) if status == ArchiveStatus.processing: print("Archive being prepared...") elif status == ArchiveStatus.ready: print("Archive ready to download") elif status == ArchiveStatus.failed: print("Archive preparation failed")
Imports a set of activities into Model Health.
Performs the full import workflow: session creation, configuration, subject association, activity creation, video download and upload, processing trigger and polling until complete for each activity.
Arguments:
- activities_json: Raw JSON string of a JSON array of activity objects.
- subject: Subject to associate with the session.
- session_config: Session settings.
Noneuses defaults. - session_meta_json: Optional JSON string of the source session's
metafield. When provided, the new session is patched with this metadata before settings are applied, preserving source data (e.g.sessionWithCalibration) needed for processing. - status_callback: Optional callable invoked at each step with a
status value. Receives one of:
"creating_session",ImportStatusCreatedSession,ImportStatusUploading, or"processing".
Returns:
The
Sessioncontaining all imported activities.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure, invalid JSON or processing failure.
Example:
import json activities_json = json.dumps([{...}, {...}]) # list of activity dicts def on_status(status): if isinstance(status, ImportStatusUploading): print(f"[{status.trial}] Uploading {status.uploaded}/{status.total}") else: print(status) session = client.import_session( activities_json, subject, status_callback=on_status, )
Downloads the prepared session archive as a ZIP file.
Call this after archive_status() returns
ArchiveStatus.ready.
Arguments:
- archive: The
Archivetask returned byprepare_archive().
Returns:
Raw ZIP bytes. Write directly to a
.zipfile.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If the archive is not ready or the download fails.
Example:
data = client.archive_data(archive) with open("session.zip", "wb") as f: f.write(data)
Returns metric values for a single activity.
Fetches the full set of biomechanical metric values computed for the given activity, grouped by category.
Arguments:
- activity_id: UUID of the activity.
Returns:
An
ActivityMetricsinstance containing all metric groups and their computed values, orNoneif the activity has not been analysed yet.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NotFoundError: If no activity with
activity_idexists. - NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
metrics = client.activity_metrics("a1b2c3d4-...") if metrics is None: print("No metrics yet — this activity has not been analysed.") else: for group in metrics.groups: print(group.name) for m in group.metrics: print(f" {m.name}: {m.value}")
Returns metric values for all of a subject's activities.
Fetches metric values across every activity belonging to the subject, optionally filtered to a date range.
Arguments:
- subject_id: Numeric ID of the subject.
- start: Optional ISO date string (
YYYY-MM-DD) for the start of the date range (inclusive). - end: Optional ISO date string (
YYYY-MM-DD) for the end of the date range (inclusive).
Returns:
A list of
ActivityMetricsinstances, one per activity.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NotFoundError: If no subject with
subject_idexists. - NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
all_metrics = client.subject_metrics(42, start="2025-01-01", end="2025-06-30") for activity_metrics in all_metrics: print(activity_metrics.activity_id)
Sets the video upload mode
Arguments:
- mode: The desired video upload mode.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: On request failure or an unexpected response.
Example:
client.set_video_upload_mode(VideoUploadMode.disabled)
Adjusts the log level without touching the registered handler.
Arguments:
- level: How verbose the log event stream should be.
Example:
client.set_log_level(LogLevel.warn)
Registers or clears the persistent log handler.
Passing None for handler clears it regardless of level.
Arguments:
- handler: Called with a
LogEventfor each event at or belowlevel. PassNoneto stop receiving events. - level: How verbose the log event stream should be. Ignored when
handlerisNone.
Example:
def handle_log(event: LogEvent) -> None: print(f"[modelhealth] {event.code}: {event.message}") client.set_log_handler(handle_log) # Stop receiving events client.set_log_handler(None)
Deprecated alias for ModelHealthClient.
Deprecated since version :
Use ModelHealthClient instead. Kept for backward compatibility;
will be removed no sooner than two minor releases and six months after
this deprecation.
Initializes the client.
Arguments:
- api_key: Your Model Health API key, available in the dashboard at modelhealth.io.
- timeout: Per-request timeout in seconds for JSON API calls. Bulk transfers (archive downloads, video uploads) are not bounded by it. If omitted, the build's default is used.
- max_retries: Number of retries on transient network or request failures. If omitted, the build's default is used.
Raises:
- AuthenticationError: If the API key is not valid.
Example:
from modelhealth import ModelHealthClient client = ModelHealthClient("your_api_key") # List sessions and subjects sessions = client.session_list() subjects = client.subjects.list().all()
Inherited Members
- ModelHealthClient
- activities
- subjects
- sessions
- groups
- activity_types
- account_info
- usage
- session_list
- get_session
- switch_subject
- new_session_from_session
- create_session
- configure_session
- fetch_subject
- create_subject
- fetch_activity
- update_activity
- delete_activity
- activity_list
- add_motion_data_to_activity
- videos_for_activity
- motion_data_for_activity
- analysis_data_for_activity
- activity_status
- start_analysis
- analysis_status
- start_recording
- stop_recording
- calibrate_camera
- calibrate_subject
- prepare_archive
- archive_status
- import_session
- archive_data
- activity_metrics
- subject_metrics
- set_video_upload_mode
- set_log_level
- set_log_handler
Filtered access to activities.
Returned by ModelHealthClient.activities — do not construct
directly.
Lazily lists activities matching the given filters.
Arguments:
- subject: Only activities belonging to this subject. Accepts a
Subjector its id. - calibration_session: Only activities recorded under this
calibration session. Accepts a
Sessionor its id. - activity_type: Only activities of this type.
- exclude_calibration: Excludes calibration and neutral-pose
activities. Default
True, since these are rarely what you want when browsing a subject's movement history. - created_after: Only activities created on or after this date
(inclusive). Accepts a
datetime.dateor an ISOYYYY-MM-DDstring. Adatetime.datetimecounts as the day it falls on in its own zone, or in the local one if it carries none. - created_before: Only activities created on or before this date
(inclusive). As
created_after. - search: Free-text search against the activity's name.
- tags: Only activities carrying every tag in this list.
- created_by: Only activities belonging to one of these accounts, by numeric account id.
- only_completed: Only activities whose analysis has finished.
- exclude_analysis_error: Excludes activities whose analysis failed.
- order_by: Field to sort by — one of
"created_at","status","created_by","activity_type"— optionally prefixed with"-"for descending. Defaults to"-created_at". - limit: Caps the total number of activities the stream yields across the whole iteration. Unset iterates every match.
Returns:
An
ActivityStreamyou iterate directly.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If
order_byoractivity_typeisn't recognized, or on request failure or an unexpected response.
Example:
activities = client.activities.list( subject=subject, activity_type=ActivityType.squats, created_after=date(2026, 1, 1), order_by="-created_at", ) for activity in activities: print(activity.name) total = client.activities.list(tags=["study-a"]).total all_activities = client.activities.list(tags=["study-a"]).all()
Inherited Members
An iterable sequence of activities matching a filter.
Returned by ActivitiesResource.list(). Read total for the number of
activities matching the filter, or call all() to collect what is left.
Inherited Members
Filtered access to subjects.
Returned by ModelHealthClient.subjects — do not construct
directly.
Lazily lists subjects matching the given filters.
Arguments:
- search: Free-text search against the subject's name.
- created_after: Only subjects created on or after this date
(inclusive). Accepts a
datetime.dateor an ISOYYYY-MM-DDstring. Adatetime.datetimecounts as the day it falls on in its own zone, or in the local one if it carries none. - created_before: Only subjects created on or before this date
(inclusive). As
created_after. - groups: Only subjects belonging to one of these subject groups.
- tags: Only subjects carrying every tag in this list.
- created_by: Only subjects belonging to one of these accounts, by numeric account id.
- activity_type: Only subjects with at least one activity of this type.
- activity_complete: Only subjects whose activities have (
True) or have not (False) all finished analysis. - session: Only the subject calibrated under this session. Accepts
a
Sessionor its id. - order_by: Field to sort by — one of
"name","created_at","updated_at"— optionally prefixed with"-"for descending. Defaults to"name". - limit: Caps the total number of subjects the stream yields across the whole iteration. Unset iterates every match.
Returns:
A
SubjectStreamyou iterate directly.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If
order_byoractivity_typeisn't recognized, or on request failure or an unexpected response.
Example:
subjects = client.subjects.list(search="Falisse", tags=["study-a"]) for subject in subjects: print(subject.name) total = client.subjects.list(tags=["study-a"]).total all_subjects = client.subjects.list(tags=["study-a"]).all()
Inherited Members
An iterable sequence of subjects matching a filter.
Returned by SubjectsResource.list(). Read total for the number of
subjects matching the filter, or call all() to collect what is left.
Inherited Members
Filtered access to sessions.
Returned by ModelHealthClient.sessions — do not construct
directly.
Lazily lists sessions matching the given filters.
Arguments:
- subject: Only the session(s) belonging to this subject. Accepts
a
Subjector its id. - order_by: Field to sort by — one of
"name","created_at"— optionally prefixed with"-"for descending. Defaults to"-created_at". - limit: Caps the total number of sessions the stream yields across the whole iteration. Unset iterates every match.
Returns:
A
SessionStreamyou iterate directly.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If
order_byisn't recognized, or on request failure or an unexpected response.
Example:
for session in client.sessions.list(subject=subject): print(session.id) total = client.sessions.list(subject=subject).total
Inherited Members
An iterable sequence of sessions matching a filter.
Returned by SessionsResource.list(). Read total for the number of
sessions matching the filter, or call all() to collect what is left.
Inherited Members
Filtered access to subject groups.
Returned by ModelHealthClient.groups — do not construct directly.
Lazily lists subject groups matching the given filters.
Arguments:
- search: Free-text search against the group's name.
- order_by: Field to sort by — currently only
"name"— optionally prefixed with"-"for descending. Defaults to"name". - limit: Caps the total number of groups the stream yields across the whole iteration. Unset iterates every match.
Returns:
A
GroupStreamyou iterate directly.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
- ModelHealthError: If
order_byisn't recognized, or on request failure or an unexpected response.
Example:
for group in client.groups.list(search="cohort"): print(group.name) total = client.groups.list().total
Inherited Members
An iterable sequence of subject groups matching a filter.
Returned by GroupsResource.list(). Read total for the number of
subject groups matching the filter, or call all() to collect what is left.
Inherited Members
A named collection of subjects.
Base exception for all Model Health SDK errors.
Raised when the API key is missing, invalid or expired.
Raised when a requested resource does not exist.
Raised on connection failure, timeout or DNS error.
Raised when the operation is not supported by this client configuration.
Identity information for the authenticated account.
Returned by ModelHealthClient.account_info().
Current billing/quota state for the authenticated account.
Returned by ModelHealthClient.usage().
Whether the account is on a free trial, or None when there is no
organization, or no plan history at all.
Activity cap for the current period, or None when there is no active
plan, or the plan is unlimited.
Whether the plan will renew automatically at period_end, or None when
there is no active plan or no subscription to renew.
A parent container for a movement capture workflow.
ID of the subject associated with this session, or None if not set.
A bare ID, not a Subject object.
A movement recording trial with associated videos and results.
The activity type as reported, or None if unset.
Carries {id, name, display_name} through unchanged — including a type this
SDK build doesn't recognize.
A processed result file associated with an activity.
A recorded video file from an activity.
An individual being monitored or assessed.
Number of activities recorded for this subject, excluding calibration and
neutral trials, or None if not reported.
Timestamp of the subject's most recent activity, or None if they have
none, or if not reported.
Sex assigned at birth as reported, or None if the subject declined to
answer or no value is available.
Parameters for creating a new subject.
A tag that can be applied to activities for categorization.
Use tags to organize and filter activities by type or condition
(for example, "cmj", "squat", "baseline").
The activity type as reported, carrying {id, name, display_name} through
unchanged — including a type this SDK build doesn't otherwise recognize.
Freeform description, when the type carries one. None for a type reported
alongside an activity, which carries only the three fields above.
Whether the type belongs to the account rather than being one everybody has.
None for a type reported alongside an activity.
Human-readable label. Not always the same as name — one type reads
"Range of Motion (ROM)" and shows as "Custom Activity".
Machine-readable key, e.g. "squat_exercise". What to branch on: it is
derived from the name once and then stays put, while name and
display_name can both be renamed. Empty when none was reported.
The activity types this account can use.
Returned by ModelHealthClient.activity_types — do not construct directly.
The types everybody has, plus any this account or its organisation added.
The list is the account's own and grows, which is why a list filters by a type taken from here rather than by a name known in advance.
Returns:
Every activity type available to this account.
Raises:
- AuthenticationError: If the API key is invalid or expired.
- NetworkError: On connection failure or timeout.
Example:
types = client.activity_types.list() squats = next(t for t in types if t.display_name == "Squat Exercise") for activity in client.activities.list(activity_type=squats): print(activity.name)
Inherited Members
Analysis result data downloaded from an activity with a completed analysis.
An active analysis task returned by start_analysis.
Pass to get_analysis_status to poll for completion.
An active archive preparation task.
An external file to attach to an activity via ModelHealthClient.add_motion_data_to_activity().
Create instances with the tagged() factory method.
A single log event delivered to a handler registered via
ModelHealthClient.set_log_handler().
Motion data types available for download from a processed activity.
Pass one or more values to ModelHealthClient.motion_data_for_activity().
Use tagged() to download an externally attached file by tag.
Example:
results = client.motion_data_for_activity( activity, [MotionDataType.kinematics_mot, MotionDataType.animation], ) # Download an externally attached file by tag results = client.motion_data_for_activity( activity, [MotionDataType.tagged("acme-sensor")] ) data = results[0].data if results else None
Inverse-kinematics results. MOT format.
Inverse-kinematics results. CSV format.
Return a tagged motion data descriptor for downloading an externally attached file.
Arguments:
- tag: The tag string used when the file was uploaded.
Returns:
A
TaggedMotionDataTypethat can be passed toModelHealthClient.motion_data_for_activity().
Analysis result types available for download after a completed analysis.
Pass one or more values to ModelHealthClient.analysis_data_for_activity().
Example:
results = client.analysis_data_for_activity( activity, [AnalysisDataType.metrics, AnalysisDataType.report], )
Video version to download for an activity.
Controls whether a user's devices upload recorded video.
Re-enables uploads, and uploads any videos that were queued locally while disabled.
Analysis algorithm to run on an activity.
Pass to ModelHealthClient.start_analysis().
Example:
task = client.start_analysis( ActivityType.counter_movement_jump, activity, session )
Gender identity.
Subject.gender is a plain str for backward compatibility, but its
value is always one of these — use this enum instead of a literal string to get
autocomplete and typo-checking.
The subject declined to answer, or no value is available at all — the two are not currently distinguishable through this attribute.
Sex assigned at birth.
Subject.sex_at_birth is a plain str for backward compatibility, but
its value is always one of these — use this enum instead of a literal string to
get autocomplete and typo-checking.
The subject declined to answer, or no value is available at all — the two are not currently distinguishable through this attribute.
Why UsageInfo's recording_allowed is False.
No plan is configured for this account.
The current billing period has ended.
The activity cap for the current period has been reached.
The most recent payment attempt failed.
How often UsageInfo's activities_used resets.
Configuration for starting or updating a recording.
Attributes:
- activity_type: The type of activity being recorded. When set, the corresponding analysis starts automatically once the recording is processed.
- config: Optional per-recording settings that override the session-level config.
- add_tags: Tags to add to the activity. Merged with existing tags.
- remove_tags: Tags to remove from the activity.
- name: New display name to set on the activity.
Per-recording settings that override session-level config.
All fields are optional — omitted fields fall back to the session's configured values.
Attributes:
- framerate: Camera frame rate override.
- filter_frequency: Low-pass filter frequency override.
Example:
from modelhealth import ActivityConfig, RecordingConfig, SessionFramerate config = ActivityConfig( ActivityType.counter_movement_jump, config=RecordingConfig(framerate=SessionFramerate.fps_60), ) activity = client.start_recording("cmj", session, config)
Orientation of the calibration checkerboard relative to the ground.
Pass to CheckerboardDetails.
Checkerboard upright, plane perpendicular to the ground.
Checkerboard flat on the floor, plane parallel to the ground.
How verbose a log event stream should be.
Pass to ModelHealthClient.set_log_handler() or ModelHealthClient.set_log_level().
Each level includes every level above it: warn includes error, info
includes both.
Which part of a session's lifecycle a log event describes.
See LogEvent.
Account and profile events, such as fetching account info or changing settings.
Activity lifecycle events, such as fetching, updating, or deleting one.
Subject lifecycle events, such as fetching or creating one.
The processing state of an activity (non-uploading variants).
Activities must reach ready before analysis can begin.
When videos are still uploading, ModelHealthClient.activity_status()
returns ActivityStatusUploading instead.
Poll ModelHealthClient.activity_status() after recording stops to
track upload and processing progress. Once ready, pass the activity to
ModelHealthClient.start_analysis().
Example:
import time status = client.activity_status(activity) while status == ActivityStatus.processing: time.sleep(5) status = client.activity_status(activity) if status == ActivityStatus.ready: task = client.start_analysis(ActivityType.counter_movement_jump, activity, session) elif status == ActivityStatus.failed: print("Processing failed")
Videos have been uploaded and are being processed.
Videos are being uploaded from cameras.
Returned by ModelHealthClient.activity_status() when upload is in
progress. uploaded and total track progress.
Example:
status = client.activity_status(activity) if isinstance(status, ActivityStatusUploading): print(f"Uploading: {status.uploaded}/{status.total}")
Analysis has been triggered and is in progress.
Returned by ModelHealthClient.activity_status() after processing
completes when an activity type was set at recording time. Pass task
to ModelHealthClient.analysis_status() to track analysis progress.
Example:
status = client.activity_status(activity) if isinstance(status, ActivityStatusAnalyzing): analysis_status = client.analysis_status(status.task)
The current state of an analysis task.
Poll ModelHealthClient.analysis_status() after calling
ModelHealthClient.start_analysis() to monitor progress.
When status reaches completed, download results with
ModelHealthClient.analysis_data_for_activity().
Example:
import time status = client.analysis_status(task) while status == AnalysisStatus.processing: time.sleep(10) status = client.analysis_status(task) if status == AnalysisStatus.completed: results = client.analysis_data_for_activity(activity, [AnalysisDataType.metrics])
The current state of an archive preparation task.
Poll ModelHealthClient.archive_status() after calling
ModelHealthClient.prepare_archive() to monitor progress.
When status reaches ready, download the archive with
ModelHealthClient.archive_data().
Example:
import time archive = client.prepare_archive(session) status = client.archive_status(archive) while status == ArchiveStatus.processing: time.sleep(5) status = client.archive_status(archive) if status == ArchiveStatus.ready: data = client.archive_data(archive)
The current state of a calibration process (non-uploading, non-processing variants).
When videos are uploading, ModelHealthClient.calibrate_camera() and
ModelHealthClient.calibrate_subject() pass CalibrationStatusUploading
to the callback. When the videos are being processed, CalibrationStatusProcessing
is passed instead.
All connected cameras are actively recording.
The uploaded videos are being processed.
Passed to the status callback while processing is underway.
percent is the completion percentage, or None if unavailable.
Configuration for a calibration checkerboard pattern.
Settings to apply to a session before recording.
All fields have sensible defaults. Pass to
ModelHealthClient.import_session() (or call
ModelHealthClient.configure_session() directly) to override.
Example:
from modelhealth import SessionConfig, SessionFramerate, SessionDataSharing config = SessionConfig( framerate=SessionFramerate.fps_60, data_sharing=SessionDataSharing.share_no_data, ) session = client.import_session(activities_json, subject, session_config=config)
Scaling pose. Default: upright_standing_pose.
Low-pass filter frequency. Default: chosen automatically.
Data-sharing preference. Default: share_processed_data_and_identified_videos.
Camera frame rate for a recording session.
Higher framerates capture fast movements more accurately but increase processing time proportionally. Collect only as much footage as needed and keep recordings under the suggested durations.
60 frames per second. Suggested maximum recording duration: 60 s.
120 frames per second (default). Suggested maximum recording duration: 30 s.
240 frames per second. Suggested maximum recording duration: 15 s.
OpenSim musculoskeletal model used for biomechanical analysis.
Full-body model with 33 degrees of freedom plus a 6-DoF shoulder complex with a scapulothoracic body and a glenohumeral joint using the ISB-recommended Y-X-Y rotation sequence.
Same full-body model as lai_uhlrich_2022_shoulder without the
ISB shoulder complex. Default.
Pose used for subject scaling during calibration.
Subject stands straight with feet pointing forward and no bending or rotation at the hips, knees, or ankles. Default.
No posture assumptions. Use when the subject cannot adopt the upright standing pose. Requires all body segments to be visible by at least two cameras.
Core processing engine version.
Data-sharing preference for a session.
Session data and videos are uploaded securely to Model Health for processing. This setting controls what Model Health can use for internal development. Identified videos contain original footage with faces unblurred; de-identified videos have faces blurred. Processed data (e.g. joint angles) is always de-identified.
Use the automatically chosen filter frequency.
Use a specific filter frequency in Hz.
Session created successfully during a session import.
Passed to the status callback during ModelHealthClient.import_session()
once the new session has been created.
A video is being uploaded during a session import.
Passed to the status callback during ModelHealthClient.import_session()
while videos are being transferred. trial names the current trial;
uploaded and total track progress within that trial.
A scalar (unilateral) metric measurement.
A bilateral metric measurement with independent left and right values.
A single computed biomechanical metric for one activity.
A category group that organizes related metrics on a dashboard.
All dashboard metrics for a single activity, organized into category groups.