API Reference (Leader /api)
Conventions
- Only the leader Pioreactor has the
/apiendpoints exposed. - Async endpoints return
202 Acceptedwith atask_idandresult_url_path. - Poll
GET /unit_api/task_results/{task_id}untilstatusissucceededorfailed. $broadcastmay be used in path parameters where documented to target all units/workers.- File download endpoints return binary bodies; use the response content-type to handle them.
- Path parameters are shown inline in the endpoint URL.
- Request/response examples are the canonical shapes; omit optional fields you do not need.
- Errors have the following schema:
{
"error": "Human-readable error message",
"cause": "Human-readable cause (defaults to error if not set)",
"remediation": "Suggested fix or next step",
"status": 400
}
Use /api/workers/... for worker-only targets (experiment-scoped jobs/logs) and /api/units/... when the leader is also a valid target; both accept $broadcast where supported.
Pioreactor Leader API
Generated from core/pioreactor/web/api.py.
This file is generated. Edit the API source or generator instead of editing this file by hand.
Endpoint count: 156
Endpoint Index
Get Automation Descriptors
Return the leader's automation UI descriptors for one automation family.
Endpoint
GET /api/automations/descriptors/{automation_type}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| automation_type | string | Yes | Automation type, for example dosing, temperature, or led. |
Response
Success
Status: 200 OK
Example body:
[
{
"display_name": "Only record temperature",
"automation_name": "only_record_temperature",
"description": "Apply no heating, only record the temperature.",
"source": null,
"fields": []
},
{
"display_name": "Thermostat",
"automation_name": "thermostat",
"description": "Vary the amount of applied heating to keep the culture near a target temperature, using a control-loop.",
"source": null,
"fields": [
{
"key": "target_temperature",
"default": 30,
"label": "Target temperature",
"disabled": false,
"required": true,
"unit": "\u2103",
"type": "numeric",
"options": null
}
]
}
]
Get Chart Descriptors
Return the leader's chart UI descriptors.
Endpoint
GET /api/charts/descriptors
Response
Success
Status: 200 OK
Example body:
[
{
"chart_key": "implied_growth_rate",
"data_source": "growth_rates",
"title": "Implied growth rate",
"source": "app",
"y_axis_label": "Growth rate, h\u207b\u00b9",
"fixed_decimals": 2,
"down_sample": true,
"mqtt_topic": "growth_rate_calculating/growth_rate",
"lookback": 100000,
"data_source_column": null,
"payload_key": "growth_rate",
"y_transformation": "(y) => y",
"y_axis_domain": [
-0.02,
0.1
],
"interpolation": "stepAfter"
},
{
"chart_key": "implied_daily_growth_rate",
"data_source": "growth_rates",
"title": "Implied daily growth rate",
"source": "app",
"y_axis_label": "Growth rate, d\u207b\u00b9",
"fixed_decimals": 2,
"down_sample": true,
"mqtt_topic": "growth_rate_calculating/growth_rate",
"lookback": 100000,
"data_source_column": null,
"payload_key": "growth_rate",
"y_transformation": "(y) => 24 * y",
"y_axis_domain": [
-0.1,
1.0
],
"interpolation": "stepAfter"
},
{
"chart_key": "fraction_of_volume_that_is_alternative_media",
"data_source": "alt_media_fractions",
"title": "Fraction of volume that is alternative media",
"source": "app",
"y_axis_label": "Fraction",
"fixed_decimals": 3,
"down_sample": false,
"mqtt_topic": "bioreactor/alt_media_fraction",
"lookback": 100000,
"data_source_column": "alt_media_fraction",
"payload_key": null,
"y_transformation": "(y) => y",
"y_axis_domain": [
0.0,
0.05
],
"interpolation": "stepAfter"
}
]
Get Shared Config
Get Shared Config endpoint.
Endpoint
GET /api/config/shared
Response
Success
Status: 200 OK
Response body is plain text.
Update Shared Config
Update Shared Config endpoint.
Endpoint
PATCH /api/config/shared
Request
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| code | string | Yes | code. |
{
"code": "[section]\nkey=value\n"
}
Response
Success
Status: 200 OK
Example body:
{
"status": "success"
}
Get Shared Config History
Get Shared Config History endpoint.
Endpoint
GET /api/config/shared/history
Response
Success
Status: 200 OK
Example body:
[
{
"filename": "config.ini",
"timestamp": "2026-08-04T21:28:03.520Z",
"data": "[PWM]\n# map the externals to the PWM\n# hardware PWM are available on channels 1 & 3.\n1=stirring\n2=waste\n3=media\n4=alt_media\n5=heating\n\n\n[leds]\nA=IR\nB=white_light\nC=\nD=\n\n\n[bioreactor]\n# efflux_tube_volume_ml is determined by the volume that just touches the outflow tube. I.e. if you\n# where to keep running the waste pump, what would the stable volume be.\n# see docs\nefflux_tube_volume_ml=14\ninitial_volume_ml=14\ninitial_alt_media_fraction=0.0\ninitial_cumulative_media_added_ml=0\ninitial_cumulative_a...<truncated>"
},
{
"filename": "config.ini",
"timestamp": "2026-08-04T21:27:21.754Z",
"data": "[PWM]\n# map the externals to the PWM\n# hardware PWM are available on channels 1 & 3.\n1=stirring\n2=waste\n3=media\n4=alt_media\n5=heating\n\n\n[leds]\nA=IR\nB=white_light\nC=\nD=\n\n\n[bioreactor]\n# efflux_tube_volume_ml is determined by the volume that just touches the outflow tube. I.e. if you\n# where to keep running the waste pump, what would the stable volume be.\n# see docs\nefflux_tube_volume_ml=14\ninitial_volume_ml=14\ninitial_alt_media_fraction=0.0\ninitial_cumulative_media_added_ml=0\ninitial_cumulative_a...<truncated>"
},
{
"filename": "config.ini",
"timestamp": "2026-06-30T16:13:30.963Z",
"data": "[PWM]\n# map the externals to the PWM\n# hardware PWM are available on channels 1 & 3.\n1=stirring\n2=waste\n3=media\n4=alt_media\n5=heating\n\n\n[leds]\nA=IR\nB=white_light\nC=\nD=\n\n\n[bioreactor]\n# efflux_tube_volume_ml is determined by the volume that just touches the outflow tube. I.e. if you\n# where to keep running the waste pump, what would the stable volume be.\n# see docs\nefflux_tube_volume_ml=14\ninitial_volume_ml=14\ninitial_alt_media_fraction=0.0\ninitial_cumulative_media_added_ml=0\ninitial_cumulative_a...<truncated>"
}
]
Get Config For Pioreactor Unit
Get merged configs, optionally limiting $broadcast with repeated unit query parameters.
Endpoint
GET /api/config/units/{pioreactor_unit}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| pioreactor_unit | string | Yes | Unit name or $broadcast where supported. |
Response
Success
Status: 200 OK
Example body:
{
"configs": {
"localhost": {
"PWM": {
"1": "stirring",
"2": "waste",
"3": "media",
"4": "bubblert",
"5": "heating"
},
"leds": {
"A": "IR",
"B": "white_light",
"C": "",
"D": ""
},
"bioreactor": {
"efflux_tube_volume_ml": "14",
"initial_volume_ml": "14",
"initial_alt_media_fraction": "0.0",
"initial_cumulative_media_added_ml": "0",
"initial_cumulative_alt_media_added_ml": "0",
"initial_cumulative_waste_removed_ml": "0"
},
"stirring.config": {
"initial_target_rpm": "500",
"initial_duty_cycle": "15",
"pwm_hz": "200",
"use_rpm": "True",
"duration_between_updates_seconds": "23",
"post_delay_duration": "0.25",
"pre_delay_duration": "0.25",
"enable_dodging_od": "true",
"target_rpm_during_od_reading": "0",
"target_rpm_outside_od_reading": "500"
},
"dosing_automation.turbidostat": {
"biomass_signal": "auto"
},
"stirring.pid": {
"Kp": "0.007",
"Ki": "0.0",
"Kd": "0.0"
},
"od_config.photodiode_channel": {
"1": "REF",
"2": "90"
},
"od_reading.config": {
"samples_per_second": "0.2",
"turn_off_leds_during_reading": "1",
"pd_reference_ema": "0.4",
"ir_led_intensity": "80",
"duration_between_led_off_and_od_reading": "0.1",
"smoothing_penalizer": "6.0",
"use_dark_offsets": "1"
},
"camera": {
"snapshot_interval_minutes": "5",
"camera_index": "0",
"ir_led_intensity": "90",
"enabled": "1",
"keep_camera_active": "0"
},
"storage": {
"database": "/Users/camerondavidson-pilon/code/pioreactor/.pioreactor/storage/pioreactor.sqlite",
"temporary_cache": "/Users/camerondavidson-pilon/code/pioreactor/.pioreactor/storage/local_intermittent_pioreactor_metadata.sqlite",
"persistent_cache": "/Users/camerondavidson-pilon/code/pioreactor/.pioreactor/storage/local_persistent_pioreactor_metadata.sqlite",
"number_of_backup_replicates_to_workers": "0"
},
"logging": {
"log_file": "./pioreactor.log",
"ui_log_file": "./pioreactor.log",
"ui_log_level": "DEBUG",
"console_log_level": "DEBUG"
},
"cluster.topology": {
"leader_hostname": "localhost",
"leader_address": "localhost"
},
"ui.overview.settings": {
"filtered_od_lookback_minutes": "240",
"raw_od_lookback_minutes": "240",
"log_display_count": "65",
"time_display_mode": "hours"
},
"ui": {
"port": "4999",
"proto": "http"
},
"ui.overview.charts": {
"implied_growth_rate": "1",
"implied_daily_growth_rate": "0",
"fraction_of_volume_that_is_alternative_media": "1",
"normalized_optical_density": "1",
"raw_optical_density": "1",
"temperature": "1",
"optical_density": "1"
},
"ui.overview.cards": {
"dosings": "1",
"event_logs": "1",
"profiles": "1"
},
"dosing_automation.pid_morbidostat": {
"Kp": "5",
"Ki": "0",
"Kd": "0"
},
"temperature_automation.thermostat": {
"Kp": ".01",
"Ki": ".01",
"Kd": ".01"
},
"mqtt": {
"username": "pioreactor",
"password": "raspberry",
"broker_address": "localhost",
"broker_ws_port": "9001",
"broker_port": "1883",
"ws_protocol": "ws",
"use_tls": "0"
},
"dosing_automation.config": {
"pause_between_subdoses_seconds": "0.5",
"waste_removal_multiplier": "2.0",
"max_volume_to_warn": "17.0",
"max_volume_to_stop": "18.0",
"max_subdose": "1.0",
"experimental_pump_malfunction_tolerance": "0.2",
"experimental_detect_pump_malfunction": "False"
}
}
},
"errors": {}
}
Get Specific Config For Pioreactor Unit
Get Specific Config For Pioreactor Unit endpoint.
Endpoint
GET /api/config/units/{pioreactor_unit}/specific
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| pioreactor_unit | string | Yes | Unit name or $broadcast where supported. |
Response
Success
Status: 200 OK
Response body is plain text.
Update Specific Config For Pioreactor Unit
Update Specific Config For Pioreactor Unit endpoint.
Endpoint
PATCH /api/config/units/{pioreactor_unit}/specific
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| pioreactor_unit | string | Yes | Unit name or $broadcast where supported. |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| code | string | Yes | code. |
{
"code": "[section]\nkey=value\n"
}
Response
Success
Status: 200 OK
Example body:
{
"status": "success"
}
Get Specific Config History For Pioreactor Unit
Get Specific Config History For Pioreactor Unit endpoint.
Endpoint
GET /api/config/units/{pioreactor_unit}/specific/history
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| pioreactor_unit | string | Yes | Unit name or $broadcast where supported. |
Response
Success
Status: 200 OK
Example body:
[
{
"filename": "unit_config.ini::localhost",
"timestamp": "2026-04-08T00:58:21.686Z",
"data": "[PWM]\n# map the externals to the PWM\n# hardware PWM are available on channels 1 & 3.\n1=stirring\n2=waste\n3=media\n4=bubblert\n5=heating"
},
{
"filename": "unit_config.ini::localhost",
"timestamp": "2026-04-08T00:18:45.007Z",
"data": "[PWM]\n# map the externals to the PWM\n# hardware PWM are available on channels 1 & 3.\n1=stirring\n2=waste\n3=media\n4=bubbler\n5=heating"
},
{
"filename": "config_localhost.ini",
"timestamp": "2025-12-03T02:50:38.730Z",
"data": "[bioreactor]\nmax_volume_ml=30\n"
}
]
Get Zipped Configs
Get Zipped Configs endpoint.
Endpoint
GET /api/config/zipped
Response
Success
Status: 200 OK
Response body is binary file data.
Get Exportable Datasets
Get Exportable Datasets endpoint.
Endpoint
GET /api/datasets/exportable
Response
Success
Status: 200 OK
Example body:
[
{
"dataset_name": "pioreactor_unit_activity_data",
"description": "This dataset includes most of your experiment data, including the time series of OD metrics, temperature, stirring rates, LED updates, and dosings.",
"display_name": "Pioreactor unit activity data (recommended)",
"has_experiment": true,
"has_unit": true,
"default_order_by": "timestamp",
"table": "pioreactor_unit_activity_data",
"query": null,
"source": "app",
"timestamp_columns": [
"timestamp"
],
"always_partition_by_unit": true,
"column_descriptions": {},
"column_units": {}
},
{
"dataset_name": "logs",
"description": "This dataset includes the append-only collection of logs from all Pioreactors. A subset of these logs are displayed in the Log Table in the Experiment Overview.",
"display_name": "Pioreactor logs",
"has_experiment": true,
"has_unit": true,
"default_order_by": "timestamp",
"table": "logs",
"query": null,
"source": "app",
"timestamp_columns": [
"timestamp"
],
"always_partition_by_unit": false,
"column_descriptions": {},
"column_units": {}
},
{
"dataset_name": "od_readings",
"description": "This dataset includes a time series of readings provided by the sensors (transformed via a calibration curve, if available), the inputs for growth calculations and normalized optical density.",
"display_name": "Optical density",
"has_experiment": true,
"has_unit": true,
"default_order_by": "timestamp",
"table": "od_readings",
"query": null,
"source": "app",
"timestamp_columns": [
"timestamp"
],
"always_partition_by_unit": false,
"column_descriptions": {},
"column_units": {}
}
]
Preview Exportable Dataset
Preview Exportable Dataset endpoint.
Endpoint
GET /api/datasets/exportable/{target_dataset}/preview
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| target_dataset | string | Yes | Exportable dataset name. |
Response
Success
Status: 200 OK
Example body:
[
{
"timestamp": "2026-01-01T00:00:00Z",
"pioreactor_unit": "pio01",
"experiment": "testing_experiment"
}
]
Export Exportable Datasets
Export selected datasets for one experiment.
Endpoint
POST /api/datasets/exportable/export
Request
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| datasets | array | Yes | datasets. |
| experiment | string | Yes | experiment. |
| partition_by_experiment | boolean | Yes | partition by experiment. |
| partition_by_unit | boolean | Yes | partition by unit. |
| end_time | object | No | end time. |
| start_time | object | No | start time. |
{
"datasets": [
"od_readings"
],
"experiment": "testing_experiment",
"partition_by_experiment": true,
"partition_by_unit": true,
"end_time": "2026-01-01T12:00:00Z",
"start_time": "2026-01-01T00:00:00Z"
}
Response
Success
Status: 202 Accepted
Example body:
{
"unit": "pio01",
"task_id": "abcd1234",
"result_url_path": "/unit_api/task_results/abcd1234",
"status": "accepted"
}
Export Exportable Datasets To Usb
Export selected datasets for one experiment to the leader's mounted USB.
Endpoint
POST /api/datasets/exportable/export-to-usb
Request
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| datasets | array | Yes | datasets. |
| experiment | string | Yes | experiment. |
| partition_by_experiment | boolean | Yes | partition by experiment. |
| partition_by_unit | boolean | Yes | partition by unit. |
| end_time | object | No | end time. |
| start_time | object | No | start time. |
{
"datasets": [
"od_readings"
],
"experiment": "testing_experiment",
"partition_by_experiment": true,
"partition_by_unit": true,
"end_time": "2026-01-01T12:00:00Z",
"start_time": "2026-01-01T00:00:00Z"
}
Response
Success
Status: 202 Accepted
Example body:
{
"unit": "pio01",
"task_id": "abcd1234",
"result_url_path": "/unit_api/task_results/abcd1234",
"status": "accepted"
}
Get Experiment Profiles
Get Experiment Profiles endpoint.
Endpoint
GET /api/experiment_profiles
Response
Success
Status: 200 OK
Example body:
[
{
"experimentProfile": {
"version": "1.0",
"experiment_profile_name": "updating_jobs",
"metadata": {
"author": "Cam Davidson-Pilon",
"description": "A profile to immediately start stirring, heating to 30C, and, after 2h, update temperature to 35C."
},
"plugins": [],
"common": {
"jobs": {
"stirring": {
"actions": "<truncated>",
"description": "<truncated>"
},
"temperature_automation": {
"actions": "<truncated>",
"description": "<truncated>"
}
}
},
"pioreactors": {},
"inputs": {}
},
"file": "update_temp.yaml",
"fullpath": "/Users/camerondavidson-pilon/code/pioreactor/.pioreactor/experiment_profiles/update_temp.yaml"
},
{
"experimentProfile": {
"version": "1.0",
"experiment_profile_name": "test_simple1",
"metadata": {
"author": "Jane Doe",
"description": null
},
"plugins": [],
"common": {
"jobs": {
"od_reading": {
"actions": "<truncated>",
"description": "<truncated>"
}
}
},
"pioreactors": {},
"inputs": {}
},
"file": "test_simple.yaml",
"fullpath": "/Users/camerondavidson-pilon/code/pioreactor/.pioreactor/experiment_profiles/test_simple.yaml"
},
{
"experimentProfile": {
"version": "1.0",
"experiment_profile_name": "temp_test",
"metadata": {
"author": null,
"description": "testing https://forum.pioreactor.com/t/writing-an-experimental-profile-based-on-a-temperature-automation-dependent-on-a-desired-od-reading/774"
},
"plugins": [],
"common": {
"jobs": {}
},
"pioreactors": {
"localhost": {
"jobs": {
"temperature_automation": "<truncated>"
},
"label": null
}
},
"inputs": {}
},
"file": "temp_test.yaml",
"fullpath": "/Users/camerondavidson-pilon/code/pioreactor/.pioreactor/experiment_profiles/temp_test.yaml"
}
]
Create Experiment Profile
Create an experiment profile YAML file.
Endpoint
POST /api/experiment_profiles
Request
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| body | string | Yes | body. |
| filename | string | Yes | filename. |
{
"body": "Profile YAML or text content.",
"filename": "profile.yaml"
}
Response
Success
Status: 200 OK
Example body:
{
"status": "success"
}
Delete Experiment Profile
Delete Experiment Profile endpoint.
Endpoint
DELETE /api/experiment_profiles/{filename}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| filename | string | Yes | Filename. |
Response
Success
Status: 200 OK
Example body:
{
"status": "success"
}
Get Experiment Profile
Get Experiment Profile endpoint.
Endpoint
GET /api/experiment_profiles/{filename}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| filename | string | Yes | Filename. |
Response
Success
Status: 200 OK
Response body is plain text.
Update Experiment Profile
Update Experiment Profile endpoint.
Endpoint
PATCH /api/experiment_profiles/{filename}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| filename | string | Yes | Filename. |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| body | string | Yes | body. |
{
"body": "Profile YAML or text content."
}
Response
Success
Status: 200 OK
Example body:
{
"status": "success"
}
Get Experiments
Get Experiments endpoint.
Endpoint
GET /api/experiments
Response
Success
Status: 200 OK
Example body:
[
{
"experiment": "demo",
"created_at": "2026-07-08T15:25:38.033Z",
"description": "aefaefef",
"delta_hours": 984.0,
"worker_count": 0,
"tags": [
"awdawd",
"rgrg"
]
},
{
"experiment": "test_bioreactor_topics_land_in_db2",
"created_at": "2026-07-08T15:19:13.502Z",
"description": null,
"delta_hours": 984.0,
"worker_count": 0,
"tags": []
},
{
"experiment": "test_bioreactor_topics_land_in_db",
"created_at": "2026-06-22T14:55:36.582000+00:00",
"description": null,
"delta_hours": 1369.0,
"worker_count": 0,
"tags": []
}
]
Create Experiment
Create a new experiment.
Endpoint
POST /api/experiments
Request
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | experiment. |
| description | string | No | description. |
| tags | array | No | tags. |
{
"experiment": "testing_experiment",
"description": "Experiment notes.",
"tags": [
"screening"
]
}
Response
Success
Status: 201 Created
Example body:
{
"experiment": "testing_experiment",
"created_at": "2026-01-01T00:00:00Z",
"description": "Experiment notes.",
"delta_hours": 0,
"worker_count": 1,
"tags": [
"screening"
]
}
Delete Experiment
Delete Experiment endpoint.
Endpoint
DELETE /api/experiments/{experiment}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 202 Accepted
Example body:
{
"unit": "pio01",
"task_id": "abcd1234",
"result_url_path": "/unit_api/task_results/abcd1234",
"status": "accepted"
}
Get Experiment
Get Experiment endpoint.
Endpoint
GET /api/experiments/{experiment}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Example body:
{
"experiment": "demo",
"created_at": "2026-07-08T15:25:38.033Z",
"description": "aefaefef",
"delta_hours": 984.0,
"worker_count": 0,
"tags": [
"awdawd",
"rgrg"
]
}
Update Experiment
Update Experiment endpoint.
Endpoint
PATCH /api/experiments/{experiment}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| description | string | No | description. |
| tags | array | No | tags. |
{
"description": "Experiment notes.",
"tags": [
"screening"
]
}
Response
Success
Status: 200 OK
Example body:
{
"experiment": "testing_experiment",
"created_at": "2026-01-01T00:00:00Z",
"description": "Experiment notes.",
"delta_hours": 0,
"worker_count": 1,
"tags": [
"screening"
]
}
Get Experiment Chart Preferences
Return the experiment's saved chart selections. Overview and individual Pioreactor chart views have independent ordered lists. A null value means the UI uses the configured defaults; an empty list means no charts are selected.
Endpoint
GET /api/experiments/{experiment}/chart_preferences
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Status: 200 OK
{
"overview_chart_keys": ["optical_density", "temperature"],
"pioreactor_chart_keys": null
}
An unknown experiment returns 404 Not Found.
Update Experiment Chart Preferences
Save chart selection and display order for an experiment. These preferences are stored on the leader and shared across browsers.
Endpoint
PATCH /api/experiments/{experiment}/chart_preferences
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
JSON Body
Provide at least one of overview_chart_keys or pioreactor_chart_keys. Each value is an ordered array of distinct chart keys or null to restore configuration defaults. Omitted fields are unchanged. An empty array hides all charts for that view. Obtain valid keys from GET /api/charts/descriptors.
{
"overview_chart_keys": ["optical_density", "temperature"],
"pioreactor_chart_keys": null
}
Response
Status: 200 OK. Returns both preference fields in the same shape as the GET endpoint.
Duplicate or unavailable chart keys, or a body with neither supported field, return 400 Bad Request. An unknown experiment returns 404 Not Found.
Get Camera Statuses For Experiment
Get Camera Statuses For Experiment endpoint.
Endpoint
GET /api/experiments/{experiment}/cameras
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Example body:
{
"cameras": {
"localhost": {
"ok": true,
"unit": "localhost",
"value": {
"unit": "localhost",
"detection_status": "detected",
"runtime_available": true,
"capture_command": null,
"mock": true,
"latest_still": {
"experiment": "demo",
"captured_at": "2026-08-18T15:54:17.433333Z",
"image_id": "20260818T155417.433333Z-a5d9d6ba",
"capture_reason": "scheduled"
},
"auto_capture_enabled": true,
"snapshot_interval_minutes": 5
}
}
}
}
Get Recent Experiment Profile Runs
Get Recent Experiment Profile Runs endpoint.
Endpoint
GET /api/experiments/{experiment}/experiment_profiles/recent
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Example body:
[]
Get Running Profiles
Get Running Profiles endpoint.
Endpoint
GET /api/experiments/{experiment}/experiment_profiles/running
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Example body:
[]
Get List Of Historical Workers For Experiment
Get List Of Historical Workers For Experiment endpoint.
Endpoint
GET /api/experiments/{experiment}/historical_worker_assignments
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Example body:
[
{
"pioreactor_unit": "localhost",
"experiment": "demo",
"is_currently_assigned_to_experiment": 0
}
]
Get Exp Logs
Shows event logs from all units, uses pagination.
Endpoint
GET /api/experiments/{experiment}/logs
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| min_level | string | No | min level. |
Response
Success
Status: 200 OK
Example body:
[
{
"timestamp": "2026-01-01T00:00:00Z",
"level": "INFO",
"message": "Log message.",
"task": "stirring",
"source": "app",
"pioreactor_unit": "pio01",
"experiment": "testing_experiment"
}
]
Get Media Rates
Shows amount of added media per unit. Note that it only consider values from a dosing automation (i.e. not manual dosing, which includes continously dose)
Endpoint
GET /api/experiments/{experiment}/media_rates
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Example body:
{
"all": {
"altMediaRate": 0.0,
"mediaRate": 0.0
}
}
Get Recent Logs
Shows recent event logs from all units
Endpoint
GET /api/experiments/{experiment}/recent_logs
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| min_level | string | No | min level. |
Response
Success
Status: 200 OK
Example body:
[
{
"timestamp": "2026-01-01T00:00:00Z",
"level": "INFO",
"message": "Log message.",
"task": "stirring",
"source": "app",
"pioreactor_unit": "pio01",
"experiment": "testing_experiment"
}
]
Get Fallback Time Series
Get Fallback Time Series endpoint.
Endpoint
GET /api/experiments/{experiment}/time_series/{data_source}/{column}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
| data_source | string | Yes | Time-series data source name. |
| column | string | Yes | Dataset column name. |
Response
Success
Status: 200 OK
Body shape: series is a list of series labels. data is a parallel list of point arrays, so data[i] contains the points for series[i]. Each point has x as an ISO-8601 UTC timestamp string and y as a number.
Example body:
{
"series": [
"pio01",
"pio02"
],
"data": [
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.01234
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.0125
}
],
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.00987
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.01001
}
]
]
}
Get Growth Rates
Gets growth rates for all units
Endpoint
GET /api/experiments/{experiment}/time_series/growth_rates
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Body shape: series is a list of series labels. data is a parallel list of point arrays, so data[i] contains the points for series[i]. Each point has x as an ISO-8601 UTC timestamp string and y as a number.
Example body:
{
"series": [
"pio01",
"pio02"
],
"data": [
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.01234
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.0125
}
],
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.00987
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.01001
}
]
]
}
Get Od Readings
Gets raw od for all units
Endpoint
GET /api/experiments/{experiment}/time_series/od_readings
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Body shape: series is a list of series labels. data is a parallel list of point arrays, so data[i] contains the points for series[i]. Each point has x as an ISO-8601 UTC timestamp string and y as a number.
Example body:
{
"series": [
"pio01",
"pio02"
],
"data": [
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.01234
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.0125
}
],
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.00987
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.01001
}
]
]
}
Get Od Readings Filtered
Gets normalized od for all units
Endpoint
GET /api/experiments/{experiment}/time_series/od_readings_filtered
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Body shape: series is a list of series labels. data is a parallel list of point arrays, so data[i] contains the points for series[i]. Each point has x as an ISO-8601 UTC timestamp string and y as a number.
Example body:
{
"series": [
"pio01",
"pio02"
],
"data": [
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.01234
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.0125
}
],
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.00987
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.01001
}
]
]
}
Get Od Readings Fused
Get Od Readings Fused endpoint.
Endpoint
GET /api/experiments/{experiment}/time_series/od_readings_fused
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Body shape: series is a list of series labels. data is a parallel list of point arrays, so data[i] contains the points for series[i]. Each point has x as an ISO-8601 UTC timestamp string and y as a number.
Example body:
{
"series": [
"pio01",
"pio02"
],
"data": [
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.01234
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.0125
}
],
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.00987
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.01001
}
]
]
}
Get Od Raw Readings
Gets raw od for all units
Endpoint
GET /api/experiments/{experiment}/time_series/raw_od_readings
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Body shape: series is a list of series labels. data is a parallel list of point arrays, so data[i] contains the points for series[i]. Each point has x as an ISO-8601 UTC timestamp string and y as a number.
Example body:
{
"series": [
"pio01",
"pio02"
],
"data": [
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.01234
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.0125
}
],
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.00987
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.01001
}
]
]
}
Get Temperature Readings
Gets temperature readings for all units
Endpoint
GET /api/experiments/{experiment}/time_series/temperature_readings
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Body shape: series is a list of series labels. data is a parallel list of point arrays, so data[i] contains the points for series[i]. Each point has x as an ISO-8601 UTC timestamp string and y as a number.
Example body:
{
"series": [
"pio01",
"pio02"
],
"data": [
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.01234
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.0125
}
],
[
{
"x": "2026-01-01T00:00:00.000Z",
"y": 0.00987
},
{
"x": "2026-01-01T00:01:00.000Z",
"y": 0.01001
}
]
]
}
Get Unit Labels
Get Unit Labels endpoint.
Endpoint
GET /api/experiments/{experiment}/unit_labels
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Example body:
{}
Upsert Unit Labels
Update or insert a new unit label for the current experiment.
Endpoint
PATCH /api/experiments/{experiment}/unit_labels
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| label | string | Yes | label. |
| unit | string | Yes | unit. |
{
"label": "Control",
"unit": "example_unit"
}
Response
Success
Status: 201 Created
Example body:
{
"status": "success"
}
Upsert Unit Labels
Update or insert a new unit label for the current experiment.
Endpoint
PUT /api/experiments/{experiment}/unit_labels
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| label | string | Yes | label. |
| unit | string | Yes | unit. |
{
"label": "Control",
"unit": "example_unit"
}
Response
Success
Status: 201 Created
Example body:
{
"status": "success"
}
Remove Workers From Experiment
Remove Workers From Experiment endpoint.
Endpoint
DELETE /api/experiments/{experiment}/workers
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 202 Accepted
Example body:
{
"unit": "pio01",
"task_id": "abcd1234",
"result_url_path": "/unit_api/task_results/abcd1234",
"status": "accepted"
}
Get List Of Workers For Experiment
Get List Of Workers For Experiment endpoint.
Endpoint
GET /api/experiments/{experiment}/workers
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Response
Success
Status: 200 OK
Example body:
[]
Add Worker To Experiment
Assign a worker, treating a retry of the current assignment as a no-op.
Endpoint
PUT /api/experiments/{experiment}/workers
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| pioreactor_unit | string | Yes | pioreactor unit. |
{
"pioreactor_unit": "pio02"
}
Response
Success
Status: 200 OK
Example body:
{
"status": "success"
}
Remove Worker From Experiment
Remove Worker From Experiment endpoint.
Endpoint
DELETE /api/experiments/{experiment}/workers/{pioreactor_unit}
Request
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| experiment | string | Yes | Experiment identifier. |
| pioreactor_unit | string | Yes | Unit name or $broadcast where supported. |
Response
Success
Status: 200 OK
Example body:
{
"status": "success"
}
Get Active Experiments
Get list of experiments with at least one active worker assigned
Endpoint
GET /api/experiments/active
Response
Success
Status: 200 OK
Example body:
[
{
"experiment": "ALE - Acetate",
"created_at": "2024-09-04T17:04:46.423882Z",
"description": "MZ PhD Evolution research experiment. Pioreactors 9-16.",
"delta_hours": 17111.0,
"worker_count": 1,
"tags": []
}
]
Get Experiments Worker Assignments
Get Experiments Worker Assignments endpoint.
Endpoint
GET /api/experiments/assignment_count
Response
Success
Status: 200 OK
Example body:
[
{
"experiment": "ALE - Acetate",
"worker_count": 1
}
]
Get Latest Experiment
Get Latest Experiment endpoint.
Endpoint
GET /api/experiments/latest
Response
Success
Status: 200 OK
Example body:
{
"experiment": "demo",
"created_at": "2026-07-08T15:25:38.033Z",
"description": "aefaefef",
"delta_hours": 984.0,
"worker_count": 0,
"tags": [
"awdawd",
"rgrg"
]
}
Get Job Descriptors
Return the leader's background-job UI descriptors.
Endpoint
GET /api/jobs/descriptors
Response
Success
Status: 200 OK
Example body:
[
{
"display_name": "Stirring",
"job_name": "stirring",
"display": true,
"published_settings": [
{
"key": "target_rpm",
"type": "numeric",
"display": true,
"description": "Modify the target RPM of stirring. This will effect the optical density reading. Too low and the stirring may completely stop. Too high and the resulting vortex may interfere with the optics.",
"default": null,
"unit": "RPM",
"label": "Target stir RPM",
"editable": true,
"min": null,
"max": null
}
],
"source": "app",
"description": "Start the stirring on the Pioreactor. Stirring is needed for mixing and proper OD measurements.",
"subtext": null,
"is_testing": false
},
{
"display_name": "Optical density",
"job_name": "od_reading",
"display": true,
"published_settings": [],
"source": "app",
"description": "Collect optical density measurements of the culture over time.",
"subtext": null,
"is_testing": false
},
{
"display_name": "Growth rate",
"job_name": "growth_rate_calculating",
"display": true,
"published_settings": [],
"source": "app",
"description": "Transform optical density measurements into culture growth rate measurements. Start this after innoculation. Begins by sampling for a few minutes to gather a baseline.",
"subtext": null,
"is_testing": false
}
]
Get Local Access Point
Get Local Access Point endpoint.
Endpoint
GET /api/local_access_point
Response
Success
Status: 200 OK
Example body:
{
"active": false
}
Get Logs
Shows event logs from all units, uses pagination.
Endpoint
GET /api/logs
Request
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| min_level | string | No | min level. |
Response
Success
Status: 200 OK
Example body:
[
{
"timestamp": "2026-01-01T00:00:00Z",
"level": "INFO",
"message": "Log message.",
"task": "stirring",
"source": "app",
"pioreactor_unit": "pio01",
"experiment": "testing_experiment"
}
]
Get Models
Return the list of supported Pioreactor models (name, version, display_name).
Endpoint
GET /api/models
Response
Success
Status: 200 OK
Example body:
{
"models": [
{
"model_name": "pioreactor_40ml",
"model_version": "1.5",
"display_name": "Pioreactor 40ml, v1.5",
"reactor_capacity_ml": 40.0,
"reactor_diameter_mm": 27.0,
"reactor_max_fill_volume_ml": 36.0,
"max_temp_to_reduce_heating": 78.0,
"max_temp_to_disable_heating": 80.0,
"max_temp_to_shutdown": 85.0,
"is_legacy": false,
"is_contrib": false
},
{
"model_name": "pioreactor_40ml",
"model_version": "1.0",
"display_name": "Pioreactor 40ml, v1.0",
"reactor_capacity_ml": 40.0,
"reactor_diameter_mm": 27.0,
"reactor_max_fill_volume_ml": 36.0,
"max_temp_to_reduce_heating": 78.0,
"max_temp_to_disable_heating": 80.0,
"max_temp_to_shutdown": 85.0,
"is_legacy": true,
"is_contrib": false
},
{
"model_name": "pioreactor_20ml",
"model_version": "1.1",
"display_name": "Pioreactor 20ml, v1.1",
"reactor_capacity_ml": 20.0,
"reactor_diameter_mm": 27.0,
"reactor_max_fill_volume_ml": 18.0,
"max_temp_to_reduce_heating": 78.0,
"max_temp_to_disable_heating": 80.0,
"max_temp_to_shutdown": 85.0,
"is_legacy": true,
"is_contrib": false
}
]
}
Get Settings Descriptors
Return the leader's settings UI descriptors.
Endpoint
GET /api/settings/descriptors