API
REST
One POST runs a program and returns its output. This page covers everything the REST interface can do.
Authenticating
Every call carries a Client ID and a Client secret, sent in the body.
Your secret identifies your account and everything run with it is billed to you. Anything in a web page can be read, so keep the secret on your server and never in browser code, a mobile app, or a public repository.
Run a program
POST https://api.jdoodle.com/v1/executecurl -X POST 'https://api.jdoodle.com/v1/execute' \
-H 'Content-Type: application/json' \
-d '{
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"script": "print(\"Hello, World!\")",
"language": "python3",
"versionIndex": "0"
}'// Java 11 or newer. No dependencies.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class RunProgram {
public static void main(String[] args) throws Exception {
String body = "{"
+ "\"clientId\": \"your_client_id\","
+ "\"clientSecret\": \"your_client_secret\","
+ "\"script\": \"print(\\\"Hello, World!\\\")\","
+ "\"language\": \"python3\","
+ "\"versionIndex\": \"0\""
+ "}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.jdoodle.com/v1/execute"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}// Node 18 or newer. No dependencies.
const response = await fetch('https://api.jdoodle.com/v1/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
script: 'print("Hello, World!")',
language: 'python3',
versionIndex: '0'
})
})
const result = await response.json()
console.log(result.output)# pip install requests
import requests
response = requests.post(
"https://api.jdoodle.com/v1/execute",
json={
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"script": 'print("Hello, World!")',
"language": "python3",
"versionIndex": "0",
},
)
print(response.json()["output"])Whatever language you use, the response is the same:
{
"output": "Hello, World!",
"statusCode": 200,
"memory": "123456",
"cpuTime": "0.02"
}Each of these runs on your server. None belongs in a browser, because each carries your client secret.
What you can send
| Parameter | Type | Required | What it is |
|---|---|---|---|
clientId |
String | Yes | Your client ID |
clientSecret |
String | Yes | Your client secret |
script |
String | Yes | The program to run |
language |
String | Yes | Which language to run it in |
versionIndex |
String | Yes | Position in the version list, not a version number |
stdin |
String | No | Standard input for the program |
args |
String | No | Command line arguments |
libs |
Array | No | External libraries to install |
compileArgs |
String | No | Extra arguments for the compiler |
runArgs |
String | No | Extra arguments when running |
compileOnly |
Boolean | No | Compile without running |
versionIndex is a position, not a version
This one catches people. versionIndex is where a version sits in that
language's list, counting from zero. It is not the version number.
So "versionIndex": "0" does not mean "version 0" and does not mean "the
latest". It means the first version listed for that language, which is
usually the oldest one we still support.
For Python 3, index 0 is an old 3.x release, not the newest. If you want a
recent version, look up its position on the
languages and versions page and send that number.
Pin it deliberately. A pinned index keeps your results the same when new versions are added to the list.
What comes back
| Field | What it is |
|---|---|
output |
What the program printed, or the compiler's message |
statusCode |
The status of the request, not of the program |
memory |
Memory the run used |
cpuTime |
CPU time the run used |
compilationStatus |
With compileOnly only. 0 compiled, 1 did not |
error |
On a failed request, in place of output |
Giving a program input
Send stdin for anything the program reads. A program that expects input and
gets none will wait, then time out.
curl -X POST 'https://api.jdoodle.com/v1/execute' \
-H 'Content-Type: application/json' \
-d '{
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"script": "name = input()\nprint(f\"Hello, {name}\")",
"stdin": "Ada",
"language": "python3",
"versionIndex": "0"
}'String body = "{"
+ "\"clientId\": \"your_client_id\","
+ "\"clientSecret\": \"your_client_secret\","
+ "\"script\": \"name = input()\\nprint(name)\","
+ "\"stdin\": \"Ada\","
+ "\"language\": \"python3\","
+ "\"versionIndex\": \"0\""
+ "}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.jdoodle.com/v1/execute"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();const response = await fetch('https://api.jdoodle.com/v1/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
script: 'name = input()\nprint(f"Hello, {name}")',
stdin: 'Ada',
language: 'python3',
versionIndex: '0'
})
})response = requests.post(
"https://api.jdoodle.com/v1/execute",
json={
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"script": 'name = input()\nprint(f"Hello, {name}")',
"stdin": "Ada",
"language": "python3",
"versionIndex": "0",
},
)If your users write programs that prompt while running, REST cannot serve them. Use a WebSocket connection instead.
Using libraries
libs takes the package names your language's own package manager uses.
curl -X POST 'https://api.jdoodle.com/v1/execute' \
-H 'Content-Type: application/json' \
-d '{
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"script": "import pandas as pd\nprint(pd.__version__)",
"libs": ["pandas"],
"language": "python3",
"versionIndex": "0"
}'String body = "{"
+ "\"clientId\": \"your_client_id\","
+ "\"clientSecret\": \"your_client_secret\","
+ "\"script\": \"import pandas as pd\\nprint(pd.__version__)\","
+ "\"libs\": [\"pandas\"],"
+ "\"language\": \"python3\","
+ "\"versionIndex\": \"0\""
+ "}";const response = await fetch('https://api.jdoodle.com/v1/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
script: 'import pandas as pd\nprint(pd.__version__)',
libs: ['pandas'],
language: 'python3',
versionIndex: '0'
})
})response = requests.post(
"https://api.jdoodle.com/v1/execute",
json={
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"script": "import pandas as pd\nprint(pd.__version__)",
"libs": ["pandas"],
"language": "python3",
"versionIndex": "0",
},
)Checking without running
compileOnly compiles the program and tells you whether it built, without
running it. The response carries compilationStatus: 0 if it compiled, 1
if it did not.
Useful for giving a student feedback on syntax before they run anything.
Internet access
By default a program cannot reach the internet. Programs that need to call an API or fetch a page require internet access to be enabled.
Two things to know. It is not available on the Free plan, so you need a paid plan to use it at all. And each run that uses it costs one extra credit, on top of the credit the run itself costs.
Projects with several files
When a project has several files that import each other, send the files instead
of a script.
| Endpoint | What it does |
|---|---|
https://api.jdoodle.com/v1/execute-api-multifile |
Upload a project and run it |
https://api.jdoodle.com/v1/execute-api-multifile-upload-only |
Upload a project without running it |
https://api.jdoodle.com/v1/execute-api-multifile-existing |
Run a project you uploaded earlier |
The two upload endpoints take the project as an uploaded file, so those requests are form uploads rather than JSON.
The project must be a zip
projectFile is a zip archive of your project. Nothing else is accepted:
the request is rejected unless its content type is application/zip.
Three things to know before you build one:
- Zip the files, not a wrapper folder.
mainFileis found by its path inside the archive, so an extra top-level folder changes every path. - There is a size limit. A zip over it is rejected, and the error says what the limit is.
- Archives that expand enormously are refused. A zip bomb is rejected before anything is unpacked.
| Parameter | What it is |
|---|---|
projectFile |
The project, as a zip archive |
mainFile |
The file that should run, by its path inside the zip |
language |
Which language to run it in |
versionIndex |
Position in the version list, as above |
mainFile names your entry point. The other files are reached from it through
your own imports.
Uploading and running a project
The two upload endpoints take a form upload, not JSON.
curl -X POST 'https://api.jdoodle.com/v1/execute-api-multifile' \
-F 'clientId=your_client_id' \
-F 'clientSecret=your_client_secret' \
-F 'projectFile=@project.zip' \
-F 'mainFile=Main.java' \
-F 'language=java' \
-F 'versionIndex=0'// Multipart needs a boundary, so the body is built by hand.
String boundary = "----jdoodle" + System.currentTimeMillis();
var parts = new StringBuilder();
for (String[] field : new String[][] {
{ "clientId", "your_client_id" },
{ "clientSecret", "your_client_secret" },
{ "mainFile", "Main.java" },
{ "language", "java" },
{ "versionIndex", "0" }
}) {
parts.append("--").append(boundary).append("\r\n")
.append("Content-Disposition: form-data; name=\"").append(field[0]).append("\"\r\n\r\n")
.append(field[1]).append("\r\n");
}
byte[] project = Files.readAllBytes(Path.of("project.zip"));
var body = new ByteArrayOutputStream();
body.write(parts.toString().getBytes());
body.write(("--" + boundary + "\r\nContent-Disposition: form-data; "
+ "name=\"projectFile\"; filename=\"project.zip\"\r\n\r\n").getBytes());
body.write(project);
body.write(("\r\n--" + boundary + "--\r\n").getBytes());
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.jdoodle.com/v1/execute-api-multifile"))
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(HttpRequest.BodyPublishers.ofByteArray(body.toByteArray()))
.build();import { readFile } from 'node:fs/promises'
const form = new FormData()
form.set('clientId', 'your_client_id')
form.set('clientSecret', 'your_client_secret')
form.set('projectFile', new Blob([await readFile('project.zip')]), 'project.zip')
form.set('mainFile', 'Main.java')
form.set('language', 'java')
form.set('versionIndex', '0')
const response = await fetch('https://api.jdoodle.com/v1/execute-api-multifile', {
method: 'POST',
body: form
})
const result = await response.json()
console.log(result.output, result.projectKey)with open("project.zip", "rb") as project:
response = requests.post(
"https://api.jdoodle.com/v1/execute-api-multifile",
data={
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"mainFile": "Main.java",
"language": "java",
"versionIndex": "0",
},
files={"projectFile": project},
)
print(response.json()["output"])The response includes a projectKey. Send that key to
execute-api-multifile-existing to run the same project again without
uploading it, as JSON rather than a form:
curl -X POST 'https://api.jdoodle.com/v1/execute-api-multifile-existing' \
-H 'Content-Type: application/json' \
-d '{
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"projectKey": "the_key_from_the_upload",
"language": "java",
"versionIndex": "0"
}'String body = "{"
+ "\"clientId\": \"your_client_id\","
+ "\"clientSecret\": \"your_client_secret\","
+ "\"projectKey\": \"the_key_from_the_upload\","
+ "\"language\": \"java\","
+ "\"versionIndex\": \"0\""
+ "}";const response = await fetch('https://api.jdoodle.com/v1/execute-api-multifile-existing', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
projectKey: 'the_key_from_the_upload',
language: 'java',
versionIndex: '0'
})
})response = requests.post(
"https://api.jdoodle.com/v1/execute-api-multifile-existing",
json={
"clientId": "your_client_id",
"clientSecret": "your_client_secret",
"projectKey": "the_key_from_the_upload",
"language": "java",
"versionIndex": "0",
},
)Uploading once and running many times
Uploading returns a projectKey. Keep it, and later runs send the key
instead of the files.
That saves both time and credits. Uploading costs one extra credit, so
uploading once and running by key is cheaper than sending the project on every
run. See credits and pricing for the full list. Running a project you already uploaded is a JSON request that takes
projectKey in place of projectFile and mainFile.
What next
If your users need to answer a program while it runs, the WebSocket interface builds on everything here.