Kernel API
Programmatic access to your studies, datasets, and analysis pipelines.
Overview
The Kernel API is a REST API for listing the datasets in a study and running analysis pipelines against them. All requests are made over HTTPS to a single host:
Access to this API requires a subscription plan that includes Kernel Cloud API Access. If your organization is not on such a plan, API requests will be rejected. Contact Kernel to upgrade your plan.
Requests and responses are JSON. Every route requires an API key (see Authentication). Throughout this document, a dataset corresponds to a single recording session, and timestamps are floating-point seconds since the Unix epoch (UTC).
Authentication
An Organization Owner can generate an API key on the Organization Settings page in the Kernel portal. Treat this key like a password — anyone with it has access to your organization's studies and datasets.
The key looks like api_key_production.<uuid> — copy it exactly as
shown on the settings page. Pass it in the Authorization header on
every request:
Authorization: api_key_production.5e9c1e0a-1b23-4c56-8def-0123456789ab
In the examples below, set your key once and reuse it:
import requests
api_key = "YOUR_API_KEY"
base_url = "https://api.kernel.com"
headers = {"Authorization": api_key}
A missing or invalid API key returns 401 Unauthorized. A key only
grants access to studies owned by the organization it was issued for.
Requests & errors
A few rules apply across every route:
- All IDs (
study_id,dataset_id,pipeline_batch_id) must be valid UUIDs. A malformed ID returns400 Bad Request. pipeline_namemust be one of the available pipelines. An unknown name returns404 Not Found.- Request bodies accept only the documented fields. Any unexpected field returns
400 Bad Request. pipeline_paramsis an object of boolean flags. For batch runs, unknown flag names or non-boolean values are rejected with400 Bad Request.- A dataset that does not belong to the given study returns
400 Bad Request; a missing study or dataset returns404 Not Found.
List studies
Return all studies in an organization.
Path parameters
| Name | Type | Description |
|---|---|---|
| org_id | string (uuid) required | Your organization's UUID. The API key must belong to this organization. |
Response
| Field | Type | Description |
|---|---|---|
| studies | array | All studies in the organization. Each item contains the fields below. |
| studies[].id | string (uuid) | Study UUID. Use this as study_id in the other routes. |
| studies[].name | string | Human-readable study name. |
| studies[].is_complete | boolean | Whether the study has been marked complete. |
{
"studies": [
{
"id": "96a09389-1079-49f5-9df0-fdbbf357d07d",
"name": "Resting-state fNIRS cohort",
"is_complete": false
},
{
"id": "b4c1f0e2-8a77-4e39-9c15-2d6a1f4e7b8c",
"name": "Pilot study 2025",
"is_complete": true
}
]
}
Example
import requests
api_key = "YOUR_API_KEY"
org_id = "d2f4a1b8-6c3e-4a90-8b17-5e9c0a2f3d61"
response = requests.get(
f"https://api.kernel.com/api/v1/organization/{org_id}/studies",
headers={"Authorization": api_key},
)
response.raise_for_status()
studies = response.json()["studies"]
open_studies = [study for study in studies if not study["is_complete"]]
print(open_studies)
List datasets
Return all dataset IDs and metadata for a study, newest first.
Path parameters
| Name | Type | Description |
|---|---|---|
| study_id | string (uuid) required | The study's UUID. |
Response
| Field | Type | Description |
|---|---|---|
| datasets | array | List of datasets, newest first. Each item contains the fields below. |
| datasets[].id | string (uuid) | Dataset UUID. Use this as dataset_id in the pipeline routes. |
| datasets[].meta | object | Free-form metadata. Possible keys include name, description, experiment, number, invalid. |
| datasets[].participant | object | Participant info: id (uuid), participant_id (human-readable, e.g. S014), created_at (float), active (bool), pending (bool), status (string). |
| datasets[].created_date | float | Creation time, seconds since epoch. |
| datasets[].started_at | float | Recording start, seconds since epoch. |
| datasets[].stopped_at | float | Recording stop, seconds since epoch. |
{
"datasets": [
{
"id": "85f2c077-2d11-4fb5-acaf-5149db0922c6",
"meta": {
"name": "Session 2",
"description": "Resting-state, eyes closed",
"experiment": "resting_state",
"number": "2"
},
"participant": {
"id": "4ae9d4da-b6cb-4fb0-8636-8c6b44d0e808",
"participant_id": "S014",
"created_at": 1749556800.0,
"active": true,
"pending": false,
"status": "enrolled"
},
"created_date": 1750939200.0,
"started_at": 1750939230.0,
"stopped_at": 1750939830.0
}
]
}
Example
import requests
api_key = "YOUR_API_KEY"
study_id = "96a09389-1079-49f5-9df0-fdbbf357d07d"
response = requests.get(
f"https://api.kernel.com/api/v1/study/{study_id}/datasets",
headers={"Authorization": api_key},
)
response.raise_for_status()
datasets = response.json()["datasets"]
dataset_ids = [dataset["id"] for dataset in datasets]
print(dataset_ids)
Run a pipeline
Start an analysis pipeline for a single dataset.
Path parameters
| Name | Type | Description |
|---|---|---|
| study_id | string (uuid) required | The study's UUID. |
| dataset_id | string (uuid) required | The dataset's UUID. |
| pipeline_name | string required | One of the available pipelines listed below. |
Available pipelines
- analysis_eeg
- analysis_nirs_epoched
- analysis_nirs_glm
- analysis_task
- qc_eeg
- qc_nirs_basic
- qc_nirs
- qc_syncbox
- reconstruction
- pipeline_snirf_gated
- snirf_hb_moments
- snirf_moments
Request body (optional)
| Field | Type | Description |
|---|---|---|
| pipeline_params | object optional | Boolean flags that adjust the pipeline. Supported flags depend on the pipeline; the analysis pipelines accept the flags shown below. |
{
"pipeline_params": {
"skip_channel_pruning": true,
"skip_artifact_correction": true,
"skip_global_short_channel_regression": true
}
}
Response
| Field | Type | Description |
|---|---|---|
| job_id | string (uuid) | UUID of the pipeline run. Use it to poll status. |
{
"job_id": "32ce0eae-8d39-4ff4-ab59-249e034ebb8d"
}
Example
import requests
api_key = "YOUR_API_KEY"
study_id = "96a09389-1079-49f5-9df0-fdbbf357d07d"
dataset_id = "85f2c077-2d11-4fb5-acaf-5149db0922c6"
pipeline_name = "analysis_nirs_glm"
response = requests.post(
f"https://api.kernel.com/api/v1/study/{study_id}/dataset/{dataset_id}/pipeline/{pipeline_name}",
headers={"Authorization": api_key},
json={ # optional
"pipeline_params": {
"skip_channel_pruning": True,
"skip_artifact_correction": True,
"skip_global_short_channel_regression": True,
}
},
)
response.raise_for_status()
job_id = response.json()["job_id"]
print(job_id)
Get pipeline status
Get the status of the most recent pipeline run for a dataset, plus signed download URLs for the results once the run has succeeded.
Path parameters
| Name | Type | Description |
|---|---|---|
| study_id | string (uuid) required | The study's UUID. |
| dataset_id | string (uuid) required | The dataset's UUID. |
| pipeline_name | string required | The pipeline that was run. |
Response
| Field | Type | Description |
|---|---|---|
| job_id | string (uuid) | UUID of the most recent run. |
| status | string | Job status. One of the values below. |
| pipeline_params | object | Flags the run was started with (present only if any were set). |
| signed_urls | object | Present only when status is SUCCEEDED. Contains batch_job_id, execution_id, mime_type, urls (asset path → signed download URL), and sizes (asset path → bytes). |
SUBMITTEDPENDINGRUNNABLESTARTINGRUNNINGSUCCEEDEDFAILED
{
"job_id": "32ce0eae-8d39-4ff4-ab59-249e034ebb8d",
"status": "SUCCEEDED",
"pipeline_params": { "skip_channel_pruning": true },
"signed_urls": {
"batch_job_id": "7a1f5478-9cd1-4b35-916c-20372a86eb03",
"execution_id": "5083918b-5ced-49da-b208-7edffa0f9566",
"mime_type": "application/vnd.kernel.download",
"urls": {
"moments/HbO_moments.snirf": "https://kernel-assets.s3.amazonaws.com/...&X-Amz-Signature=...",
"qc/qc_report.html": "https://kernel-assets.s3.amazonaws.com/...&X-Amz-Signature=..."
},
"sizes": {
"moments/HbO_moments.snirf": 8734512.0,
"qc/qc_report.html": 45213.0
}
}
}
Example
import requests
api_key = "YOUR_API_KEY"
study_id = "96a09389-1079-49f5-9df0-fdbbf357d07d"
dataset_id = "85f2c077-2d11-4fb5-acaf-5149db0922c6"
pipeline_name = "analysis_nirs_glm"
response = requests.get(
f"https://api.kernel.com/api/v1/study/{study_id}/dataset/{dataset_id}/pipeline/{pipeline_name}/status",
headers={"Authorization": api_key},
)
response.raise_for_status()
status = response.json()
print(status["status"])
if status["status"] == "SUCCEEDED":
for asset_path, url in status["signed_urls"]["urls"].items():
print(asset_path, url)
Run a batch pipeline
Start the same pipeline for many datasets in a study with a single request. Returns a batch ID you can poll for aggregate progress.
Path parameters
| Name | Type | Description |
|---|---|---|
| study_id | string (uuid) required | The study's UUID. |
| pipeline_name | string required | One of the available pipelines. |
Request body
| Field | Type | Description |
|---|---|---|
| dataset_ids | array (uuid) required | Dataset UUIDs to process. Must be non-empty; maximum 500 per request. |
| pipeline_params | object optional | Boolean flags applied to every dataset. Unknown flag names or non-boolean values are rejected. |
{
"dataset_ids": [
"85f2c077-2d11-4fb5-acaf-5149db0922c6",
"168d306d-2334-4340-a4d7-c6171c6cc21c",
"7ab6093b-9639-4de4-8950-0569351aa0c8"
],
"pipeline_params": {
"skip_channel_pruning": true,
"skip_artifact_correction": true,
"skip_global_short_channel_regression": true
}
}
Response
| Field | Type | Description |
|---|---|---|
| pipeline_batch_id | string (uuid) | UUID for tracking the batch run. |
{
"pipeline_batch_id": "2425e5de-b37e-4f9e-8305-62433e862a9c"
}
Example
import requests
api_key = "YOUR_API_KEY"
study_id = "96a09389-1079-49f5-9df0-fdbbf357d07d"
pipeline_name = "analysis_nirs_glm"
response = requests.post(
f"https://api.kernel.com/api/v1/study/{study_id}/pipeline/{pipeline_name}/batch",
headers={"Authorization": api_key},
json={
"dataset_ids": [
"85f2c077-2d11-4fb5-acaf-5149db0922c6",
"168d306d-2334-4340-a4d7-c6171c6cc21c",
"7ab6093b-9639-4de4-8950-0569351aa0c8",
],
"pipeline_params": { # optional
"skip_channel_pruning": True,
"skip_artifact_correction": True,
"skip_global_short_channel_regression": True,
},
},
)
response.raise_for_status()
pipeline_batch_id = response.json()["pipeline_batch_id"]
print(pipeline_batch_id)
Get batch pipeline status
Get aggregate progress for a batch run. Once the batch is complete, the response includes a signed URL to a JSON results file with per-dataset statuses and download links.
Path parameters
| Name | Type | Description |
|---|---|---|
| study_id | string (uuid) required | The study's UUID. |
| pipeline_batch_id | string (uuid) required | The batch ID returned from the run-batch route. |
Response
| Field | Type | Description |
|---|---|---|
| pipeline_batch_id | string (uuid) | The batch UUID. |
| complete | boolean | Whether all jobs have finished. |
| stats | object | Counts: total, not_analyzable, analyzable, succeeded, failed, pending. |
| pipeline_params | object | Flags the batch was started with (present only if any were set). |
| results_url | string | Present only when complete is true. Signed URL to the batch results JSON (expires after 1 week). |
{
"pipeline_batch_id": "2425e5de-b37e-4f9e-8305-62433e862a9c",
"complete": false,
"stats": {
"total": 3,
"not_analyzable": 0,
"analyzable": 3,
"succeeded": 1,
"failed": 0,
"pending": 2
}
}
{
"pipeline_batch_id": "2425e5de-b37e-4f9e-8305-62433e862a9c",
"complete": true,
"stats": {
"total": 3,
"not_analyzable": 0,
"analyzable": 3,
"succeeded": 2,
"failed": 1,
"pending": 0
},
"results_url": "https://kernel-assets.s3.amazonaws.com/...&X-Amz-Signature=..."
}
Example
import time
import requests
api_key = "YOUR_API_KEY"
study_id = "96a09389-1079-49f5-9df0-fdbbf357d07d"
pipeline_batch_id = "2425e5de-b37e-4f9e-8305-62433e862a9c"
while True:
response = requests.get(
f"https://api.kernel.com/api/v1/study/{study_id}/pipeline/batch/{pipeline_batch_id}/status",
headers={"Authorization": api_key},
)
response.raise_for_status()
status = response.json()
print(status["stats"])
if status["complete"]:
print("Results:", status.get("results_url"))
break
time.sleep(30)