JATOS API
Using the JATOS API requires some advanced knowledge of HTTP and how to call APIs from a programming language or terminal. If you just want to run a study with JATOS, this is probably not what you need. Anything you can do programmatically with the API can also be done manually via JATOS' GUI.
Introduction
Since version 3.8.1, JATOS offers an HTTP API to make integrating JATOS into other tools easier. A common use case is calling JATOS directly from Python, R, Matlab, or any other programming language in an automated and programmatic fashion.
With the API, you can:
- Import/export studies
- Update your study by uploading, downloading, or deleting individual study asset files
- Export results
- Export study/component properties
- Get study codes (to build study links for distribution to participants)
Try Out the API
You can try out the API with your local JATOS. Here's how:
- Generate a token in your local JATOS. (The JATOS API uses personal access tokens with bearer authentication.)
- Copy your token.
- Go to petstore.swagger.io. You'll see all API endpoints and their descriptions.
- At the top of the Swagger page, click the green 'Authorize' button. Paste your JATOS token into Authorize → Bearer Auth. Don't forget to click Authorize.
- Choose the server
http://localhost:9000(probably already set). - Try it out! (Click on each link to try the corresponding endpoint with pre-loaded defaults.)
OpenAPI Specification
The JATOS API uses OpenAPI 3 for specification. You can use petstore.swagger.io for an easy-to-navigate page.
The API is a work in progress. To request additional endpoints, please open a GitHub issue.
Authentication
The JATOS API uses bearer authentication.
From swagger.io:
Bearer authentication (also called token authentication) is an HTTP authentication scheme that involves security tokens called bearer tokens. The name "Bearer authentication" can be understood as "give access to the bearer of this token." The bearer token is a cryptic string, usually generated by the server in response to a login request. The client must send this token in the Authorization header when making requests to protected resources.
Every HTTP request to the API needs this header (replace <token> with your token):
Authorization: Bearer <token>
Example requests to the endpoint /jatos/api/v1/admin/token (returns info about the used token):
- curl
- Python
- R
- JavaScript
- MATLAB
- PowerShell
curl -i -H "Authorization: Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f" https://example.com/jatos/api/v1/admin/token
import requests
headers = {
'Authorization': 'Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f',
}
response = requests.post('https://example.com/jatos/api/v1/admin/token', headers=headers)
require(httr)
headers = c(
`Authorization` = "Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f"
)
res <- httr::GET(url = "https://example.com/jatos/api/v1/admin/token", httr::add_headers(.headers=headers))
fetch('https://example.com/jatos/api/v1/admin/token', {
headers: {
'Authorization': 'Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f'
}
});
%% HTTP Interface
import matlab.net.*
import matlab.net.http.*
header = HeaderField('Authorization', 'Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f');
uri = URI('https://example.com/jatos/api/v1/admin/token');
response = RequestMessage('get', header).send(uri.EncodedURI);
$headers=@{}
$headers.Add("Authorization", "Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f")
$response = Invoke-WebRequest -Uri 'https://www.example.com/jatos/api/v1/admin/token' -Method GET -Headers $headers
Personal Access Tokens
The JATOS API uses personal access tokens (PATs or API tokens).
From Wikipedia:
A personal access token (or PAT) is a string of characters that can be used to authenticate a user when accessing a computer system instead of the usual password. Though associated with a single account, multiple PATs may be added, and can be manipulated independently of the password associated with that account, including creation and revocation of PATs without altering the password.
Unlike other systems (e.g., GitHub), JATOS tokens have no roles or scopes. A token has the same access as the user it is associated with. Therefore, a token can only be used to access studies or result data if the associated user is a member of that study. Only admin tokens (tokens associated with an admin user) can access the administration endpoints.
How to Generate a Token
- Go to the user menu (click your name in the top-right header).
- Click the button My API tokens.
- In the pop-up window, click New Token. Choose a descriptive name (doesn't have to be unique) and select the expiration period. Click Generate.
- Your token will be shown. Copy it to a safe place—it will never be shown again.
- In the token overview window, you can temporarily deactivate or delete a token.

Examples
Fetch Results
The endpoint to import a study, /jatos/api/v1/results, uses a POST request to get results (combined result data, files, and metadata) in a ZIP file (JATOS Study Archive).
Here are examples in different tools/languages for this`:
- curl
- Python
- R
- JavaScript
- MATLAB
- PowerShell
curl -X 'POST' 'https://example.com/jatos/api/v1/results?componentId=1' \
-H 'accept: application/zip' \
-H 'Authorization: Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f' \
-O -J
import requests
url = "https://example.com/jatos/api/v1/results"
params = {
"componentId": 1
}
headers = {
"accept": "application/zip",
"Authorization": "Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f"
}
# Stream response so we can download large files
response = requests.post(url, headers=headers, params=params, stream=True)
# Check for errors
response.raise_for_status()
# Determine filename from Content-Disposition header
cd = response.headers.get("Content-Disposition", "")
filename = cd.split("filename=")[1].strip('"')
# Write file
with open(filename, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
library(httr)
url <- "https://example.com/jatos/api/v1/results"
# Query parameters
params <- list(
componentId = 1
)
# Headers
headers <- add_headers(
"accept" = "application/zip",
"Authorization" = "Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f"
)
# Send POST request
res <- POST(url, headers, query = params)
# Check for HTTP errors
stop_for_status(res)
# Extract filename from Content-Disposition header
cd <- headers(res)$`content-disposition`
filename <- sub(".*filename=\"?([^\"]+)\"?.*", "\\1", cd)
# Write the file
writeBin(content(res, "raw"), filename)
import https from "https";
import fs from "fs";
const url = "https://example.com/jatos/api/v1/results?componentId=1";
const req = https.request(url, {
method: "POST",
headers: {
"accept": "application/zip",
"Authorization": "Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f"
}
}, res => {
const cd = res.headers["content-disposition"];
const match = cd.match(/filename="?([^"]+)"?/);
let filename = match[1];
const file = fs.createWriteStream(filename);
res.pipe(file);
file.on("finish", () => file.close());
});
req.on("error", console.error);
req.end();
url = 'https://example.com/jatos/api/v1/results?componentId=1';
options = weboptions( ...
'RequestMethod', 'post', ...
'HeaderFields', { ...
'accept' 'application/zip'; ...
'Authorization' 'Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f' ...
}, ...
'ContentType', 'binary' ... % Important for ZIP files
);
% Perform POST request
data = webread(url, options);
% Save to file
filename = 'results.zip';
fid = fopen(filename, 'w');
fwrite(fid, data);
fclose(fid);
$Url = "https://example.com/jatos/api/v1/results?componentId=1"
$Headers = @{
"accept" = "application/zip"
"Authorization" = "Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f"
}
# Perform the POST request and save the file to a temporary name
$response = Invoke-WebRequest -Uri $Url -Method POST -Headers $Headers -OutFile "temp.bin" -PassThru
# Extract filename from Content-Disposition header
$cd = $response.Headers["Content-Disposition"]
$cd -match 'filename="?([^\";]+)' | Out-Null
$filename = $matches[1]
# Rename the downloaded file
Rename-Item -Path "temp.bin" -NewName $filename -Force
Import a Study
The endpoint to import a study, /jatos/api/v1/study, uses a POST request with the header Content-Type: multipart/form-data to upload a study archive file (JZIP) in binary format.
Here are examples in different tools/languages for uploading a JZIP file named test.jzip:
- curl
- Python
- R
- JavaScript
- MATLAB
- PowerShell
curl -X 'POST' 'https://example.com/jatos/api/v1/study' \
-H 'accept: application/json' \
-H 'Authorization: Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f' \
-H 'Content-Type: multipart/form-data' \
-F 'study=@test.jzip'
import requests
headers = {
'accept': 'application/json',
'Authorization': 'Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f',
# requests will set the correct boundary automatically
}
files = {
'study': open('test.jzip', 'rb'),
}
response = requests.post('https://example.com/jatos/api/v1/study', headers=headers, files=files)
require(httr)
headers = c(
`Authorization` = "Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f"
)
res <- httr::POST(
url = "https://example.com/jatos/api/v1/study",
add_headers(.headers = headers),
body = list(study = upload_file("test.jzip"))
)
const form = new FormData();
form.append('study', File(['<data goes here>'], 'test.jzip'));
fetch('https://example.com/jatos/api/v1/study', {
method: 'POST',
headers: {
'accept': 'application/json',
'Authorization': 'Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f'
// Do not set Content-Type; the browser will set it with the correct boundary
},
body: form
});
%% HTTP Interface
import matlab.net.*
import matlab.net.http.*
import matlab.net.http.io.*
header = [
field.AcceptField(MediaType('application/json'))
HeaderField('Authorization', 'Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f')
];
uri = URI('https://example.com/jatos/api/v1/study');
body = MultipartFormProvider('study', FileProvider('test.jzip'));
response = RequestMessage('post', header, body).send(uri.EncodedURI);
$headers=@{}
$headers.Add("accept", "application/json")
$headers.Add("Authorization", "Bearer jap_OeYwru727YeLzxcHSvIFlTQ52Ud03wo7cd41f")
$form = @{
study = Get-Item "test.jzip"
}
$response = Invoke-WebRequest -Uri 'https://example.com/jatos/api/v1/study' -Method POST -Headers $headers -Form $form
Deactivate the JATOS API
By default, the API is activated and ready to use. If you want to turn it off, edit conf/jatos.conf (or conf/production.conf in versions < 3.8.3) in the JATOS installation folder. Search for jatos.api.allowed and set it to false:
jatos.api.allowed = false