API

WebSocket

REST gives you the final output. A WebSocket connection gives you output as it appears, and lets your user answer a program while it runs.

It runs both kinds of program:

  • A single file. Send the code itself as a script.
  • A project with several files. Upload the project once as a zip, then run it by its projectKey.

Use it for a terminal, a program that prompts the user, or anything where waiting until the end is not good enough.

Everything about languages, versions and program parameters is the same as REST. What changes is how you connect and how messages arrive.

How the connection is made

Your server holds the secret. The browser holds only a token, and talks to us directly. This is the same for a single file and for a project. A project only adds an upload at the start.

A single file

WebSocket call flowYour server exchanges the client secret for a token, gives the token to the browser, and the browser then connects to JDoodle directly to run programs and send input.Your pageYour serverJDoodle1. Client ID and secretPOST /v1/auth-token2. A token, valid 3 minutes3. The token onlynever the secret4. Connect with the token/v1/stomp5. Program and input, output back
Steps 1 to 3 happen on your server. Steps 4 and 5 run directly between the browser and us, while the program runs.

A project with several files

WebSocket call flow for a project with several filesYour server uploads the project as a zip and gets a project key, then exchanges the client secret for a token. It gives the token and the project key to the browser. The browser connects to JDoodle directly and runs the project by its key, sending input and getting output back.Your pageYour serverJDoodle1. Upload the project zip/v1/execute-api-multifile-upload-only2. A projectKey, kept 30 minutes3. Client ID and secretPOST /v1/auth-token4. A token, valid 3 minutes5. The token and projectKeynever the secret6. Connect, then execute by projectKey and mainFile/v1/stomp7. Input in, output back
Steps 1 to 5 happen on your server. Upload once, then run the key as often as you like for 30 minutes. Steps 6 and 7 run directly between the browser and us.

The details and examples are in "Projects with several files", further down this page.

Your server is only involved in getting the token, and in uploading a project. After that the conversation is between your user and us, which is what makes an interactive program feel immediate.

Get a token

The browser connects to us directly, so your client secret must not be involved. Your server exchanges the secret for a short-lived token first.

curl -X POST 'https://api.jdoodle.com/v1/auth-token' \
  -H 'Content-Type: application/json' \
  -d '{
        "clientId": "your_client_id",
        "clientSecret": "your_client_secret"
      }'
// Java 11 or newer. Runs on your server, never in a browser.
String body = "{"
    + "\"clientId\": \"your_client_id\","
    + "\"clientSecret\": \"your_client_secret\""
    + "}";

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.jdoodle.com/v1/auth-token"))
    .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. Runs on your server, never in a browser.
const response = await fetch('https://api.jdoodle.com/v1/auth-token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    clientId: 'your_client_id',
    clientSecret: 'your_client_secret'
  })
})

const token = await response.text()
// Send this token to the browser. Never the secret.
# Runs on your server, never in a browser.
import requests

response = requests.post(
    "https://api.jdoodle.com/v1/auth-token",
    json={
        "clientId": "your_client_id",
        "clientSecret": "your_client_secret",
    },
)

token = response.text

The response is the token itself, as plain text rather than JSON:

eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKRE9PRExFIiwic3ViIjoiV1MtQVBJLVRPS0VOIi...

The token lasts three minutes, and you can run as many programs as you like in that time. Getting one costs one credit, on top of the credit each run costs, so one token covering a whole session is cheaper than one per run. See credits and pricing.

Request a token when a user is about to run something, rather than fetching one and keeping it.

Connect

https://api.jdoodle.com/v1/stomp

The connection speaks STOMP over SockJS, so use a SockJS client with a STOMP wrapper rather than a raw WebSocket. Load JDoodle's SockJS build:

<script src="https://api.jdoodle.com/js/sockjs.js"></script>
<script src="https://api.jdoodle.com/js/webstomp.js"></script>

The examples below are JavaScript because this half runs in your user's browser. Your server's part is getting the token above, which you can do in any language.

What Where
Subscribe to /user/queue/execute-i
Send messages to /app/execute-ws-api-token
// JDoodle's sockjs and webstomp builds, loaded in the browser.
// heartbeat:false is required — webstomp's default heartbeat misbehaves over
// the SockJS transport and tears the connection down before output streams.
const client = webstomp.over(new SockJS('https://api.jdoodle.com/v1/stomp'), {
  heartbeat: false
})

client.connect({}, () => {
  client.subscribe('/user/queue/execute-i', (message) => {
    const status = Number(message.headers.statusCode)
    if (status === 201) console.log('started')
    else if (status === 204) console.log('finished')
    else console.log(message.body)
  })

  client.send(
    '/app/execute-ws-api-token',
    JSON.stringify({
      script: 'print("Hello, World!")',
      language: 'python3',
      // versionIndex must be a number, not a string
      versionIndex: 0
    }),
    { message_type: 'execute', token }
  )
})

Every message carries two headers:

  • token. The token your server obtained.
  • message_type. Either execute to start a program, or input to answer one that is running.

Start a program

Send a message_type of execute, with the same fields REST takes:

{
  "script": "name = input()\nprint(f\"Hello, {name}\")",
  "language": "python3",
  "versionIndex": 0
}

Answer a running program

Send a message_type of input with what your user typed. The body is the text itself rather than JSON, and a newline is \n.

client.send('/app/execute-ws-api-token', userTyped + '\n', {
  message_type: 'input',
  token
})

This is the part REST cannot do. You do not need to know the input before the program starts.

Projects with several files

The socket can run a project with several files, but it cannot carry the files themselves. Running one takes two steps:

  1. Your server uploads the project as a zip over REST and gets back a projectKey.
  2. The browser sends an execute message with that key in place of a script.

The zip rules are the same as for REST. See "The project must be a zip" in REST.

Upload the project

Upload with execute-api-multifile-upload-only. It stores the project without running it, so nothing runs until your user is ready. This request carries your client secret, so it belongs on your server, next to the token request.

The upload is a form upload, not JSON. The file must be sent with the content type application/zip. Some clients do not set this on their own, so the examples below set it.

curl -X POST 'https://api.jdoodle.com/v1/execute-api-multifile-upload-only' \
  -F 'clientId=your_client_id' \
  -F 'clientSecret=your_client_secret' \
  -F 'projectFile=@project.zip;type=application/zip'
// Java 11 or newer. Runs on your server, never in a browser.
// 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" }
}) {
  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"
    + "Content-Type: application/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-upload-only"))
    .header("Content-Type", "multipart/form-data; boundary=" + boundary)
    .POST(HttpRequest.BodyPublishers.ofByteArray(body.toByteArray()))
    .build();

HttpResponse<String> response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());

System.out.println(response.body());
// Node 18 or newer. Runs on your server, never in a browser.
import { readFile } from 'node:fs/promises'

const zip = new Blob([await readFile('project.zip')], { type: 'application/zip' })

const form = new FormData()
form.set('clientId', 'your_client_id')
form.set('clientSecret', 'your_client_secret')
form.set('projectFile', zip, 'project.zip')

const response = await fetch(
  'https://api.jdoodle.com/v1/execute-api-multifile-upload-only',
  { method: 'POST', body: form }
)

const { projectKey } = await response.json()
// Send this key to the browser, together with the token.
# Runs on your server, never in a browser.
import requests

with open("project.zip", "rb") as project:
    response = requests.post(
        "https://api.jdoodle.com/v1/execute-api-multifile-upload-only",
        data={
            "clientId": "your_client_id",
            "clientSecret": "your_client_secret",
        },
        files={"projectFile": ("project.zip", project, "application/zip")},
    )

project_key = response.json()["projectKey"]

The response is JSON. Only projectKey is filled in, because nothing ran:

{
  "projectKey": "your_client_id-75845496-87a1-428e-891c-dc5697014fdb",
  "output": null,
  "statusCode": null
}

Give the browser the projectKey and a token. Neither one is a secret, and the key only works for the client ID that uploaded it.

Run it over the socket

Send a message_type of execute as before. In place of script, send:

Field What it is
multiFile true. Without it, the key is ignored and nothing runs
projectKey The key from the upload
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 a number
client.send(
  '/app/execute-ws-api-token',
  JSON.stringify({
    multiFile: true,
    projectKey,
    mainFile: 'Main.java',
    language: 'java',
    versionIndex: 0
  }),
  { message_type: 'execute', token }
)

Everything after that is the same as a single file. Output arrives on /user/queue/execute-i, and you answer the program with input messages.

You can run the same key as many times as you like while it lasts. Send mainFile every time, because it is not stored with the project. Without it, the run fails with status 503.

How long a project lasts

A project is kept for 30 minutes after it is uploaded. Runs over the socket do not extend this. When the time is up, upload the project again to get a new key.

If the key has expired, was never uploaded, or belongs to another client ID, the run does not start. You get a message with status 404 that names the key.

What it costs

Uploading costs one credit. Each run of a project over the socket costs two credits, plus one more if internet access is on. The token costs one credit as usual. See credits and pricing.

What comes back

Output arrives as messages on the queue you subscribed to. The status on each message says what it is:

Status Meaning
201 Execution started
204 Execution time
206 Output files
400, 401 Authentication problem
404 Project key not found
429 Daily limit reached
410 Connection closed (run ended)
500 Server error

A working sample

A complete HTML page that connects, runs a program, and lets you answer it while it runs. Save it, open it in a browser, paste a token, and press Run.

Download jdoodle-websocket.html

It is one file with no build step. The two libraries load from api.jdoodle.com/js/, so the downloaded page runs on its own, and the whole flow is about eighty lines, so it is easier to read than to describe.

When REST is the better choice

REST is less to build and less to get wrong. Choose the socket only when your users need to interact with a running program, or when output arrives over time and they should see it as it happens.

What next

Whichever interface you use, the language and version values are the same.

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