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/execute
curl -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. mainFile is 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.

This website uses cookies to ensure you get the best experience on our website.