API documentation
Run untrusted Python, JavaScript, Java and C++ code from your product with one REST API. Send source code and stdin; get stdout, stderr, compiler output and exit codes from an isolated sandbox with no network access.
Overview
Executions are asynchronous: POST /v1/executions returns 202 immediately with an executionId; poll GET /v1/executions/{executionId} until the status is terminal.
Base URL: https://api.tatvastack.com
Quickstart
- Sign in to the dashboard and create an API key under API keys. It is shown once; store it on your server.
- Submit code, then poll for the result:
export CODEFORGE_API_KEY=cf_live_... # from the dashboard
curl -s -X POST https://api.tatvastack.com/v1/executions \
-H "Authorization: Bearer $CODEFORGE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"language":"python","source":"print(input()[::-1])","stdin":"hello"}'
# {"executionId":"exe_…","status":"QUEUED","createdAt":"…","statusUrl":"/v1/executions/exe_…"}
curl -s https://api.tatvastack.com/v1/executions/exe_… \
-H "Authorization: Bearer $CODEFORGE_API_KEY"
# {"executionId":"exe_…","status":"SUCCEEDED","stdout":"olleh\n","exitCode":0, …}import os, time, uuid, requests
API = "https://api.tatvastack.com"
HEADERS = {"Authorization": f"Bearer {os.environ['CODEFORGE_API_KEY']}"}
def run(language, source, stdin=""):
r = requests.post(f"{API}/v1/executions", json={"language": language, "source": source, "stdin": stdin},
headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())}, timeout=10)
r.raise_for_status()
execution_id = r.json()["executionId"]
while True:
result = requests.get(f"{API}/v1/executions/{execution_id}", headers=HEADERS, timeout=10).json()
if result["status"] not in ("QUEUED", "RUNNING"):
return result
time.sleep(0.75)
result = run("python", "print(sum(range(10)))")
print(result["status"], result["stdout"]) # SUCCEEDED 45// Node.js 18+ (built-in fetch)
const API = 'https://api.tatvastack.com';
const headers = { Authorization: `Bearer ${process.env.CODEFORGE_API_KEY}`, 'Content-Type': 'application/json' };
async function run(language, source, stdin = '') {
const res = await fetch(`${API}/v1/executions`, {
method: 'POST',
headers: { ...headers, 'Idempotency-Key': crypto.randomUUID() },
body: JSON.stringify({ language, source, stdin }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { executionId } = await res.json();
for (;;) {
const result = await (await fetch(`${API}/v1/executions/${executionId}`, { headers })).json();
if (!['QUEUED', 'RUNNING'].includes(result.status)) return result;
await new Promise((r) => setTimeout(r, 750));
}
}
const result = await run('javascript', 'console.log(6 * 7)');
console.log(result.status, result.stdout); // SUCCEEDED 42Authentication
Send your key as a bearer token: Authorization: Bearer cf_live_…. Keys belong to your account; revoking one in the dashboard rejects it on the next request. Never put an API key in browser or mobile code: call CodeForge from your backend.
Languages & runtimes
language | Runtime | Example |
|---|---|---|
python | CPython 3.12 | |
javascript | Node.js 24 | |
java | OpenJDK 21 | |
cpp | g++ 11 · C++17 | |
Standard library only: no package installation and no network access. Java: put main in any class; the file is named after your public class automatically. Java and C++ are compiled first; compiler errors return COMPILE_ERROR with compileOutput.
Execution lifecycle
QUEUED/RUNNING are in progress; the rest are terminal.
SUCCEEDED: exited with code 0.COMPILE_ERROR: Java/C++ failed to compile; seecompileOutput.RUNTIME_ERROR: non-zero exit, exception or crash; seestderrandexitCode.TIMED_OUT: exceededlimits.timeMs.PLATFORM_ERROR: our failure. Not charged; retry.
Poll every 500–1000 ms; polling does not use quota. Measured median time from request to result for a short program on a warm sandbox, from a client in India to us-east-1: Python 966 ms, JavaScript 900 ms, Java 1.3 s, C++ 846 ms (Java and C++ include compilation). The first run after an idle period starts a fresh sandbox and can take a few seconds longer.
Retries & idempotency
Send an Idempotency-Key header (a UUID per logical request) with every POST, and retry with the same key: you get the original execution back (header Idempotent-Replayed: true), with no second run and no extra charge. Reusing a key with a different body returns 409 IDEMPOTENCY_CONFLICT. Keys are remembered for 24 hours.
Limits & quotas
| Source code / stdin | 64 KiB each |
Run time (limits.timeMs) | default 5,000 ms, max 10,000 ms; compilation has its own budget |
| Output | 64 KiB per stream (stdout, stderr, compileOutput); truncated: true when cut |
| Memory | fixed per language: about 512 MiB for Python and C++ programs, 256 MiB heap for JavaScript and Java |
| Free plan | 500 runs per calendar month (UTC), 1 execution in flight at a time |
Every accepted POST counts as one run, except executions that end in PLATFORM_ERROR, which are refunded. Rejected requests (4xx) are never charged. Check usage with GET /v1/usage or in the dashboard. Responses include X-Quota-Limit and X-Quota-Remaining.
Errors
Every error has the same shape. Include requestId (also in the X-Request-Id header) when contacting support at bhaskar@tatvastack.com.
{
"error": {
"code": "CONCURRENCY_LIMIT",
"message": "Your free plan allows 1 execution at a time. …",
"requestId": "req_…",
"retryable": true
}
}
| Code | HTTP | Meaning |
|---|---|---|
VALIDATION_ERROR | 400 | The body is invalid (missing field, unknown language, too long). Fix the request. |
INVALID_IDEMPOTENCY_KEY | 400 | Idempotency-Key must be 1–255 characters of A–Z a–z 0–9 . _ : - |
BAD_REQUEST | 400 | The request could not be processed (for example malformed JSON). |
UNAUTHORIZED | 401 | Missing, invalid or revoked API key. |
NOT_FOUND | 404 | No such execution in your account, or no such route. |
IDEMPOTENCY_CONFLICT | 409 | This Idempotency-Key was already used with a different request. |
PAYLOAD_TOO_LARGE | 413 | Request body too large. |
QUOTA_EXCEEDED | 429 | Monthly allowance used up. Not retryable until resetsAt. |
CONCURRENCY_LIMIT | 429 | Too many executions in flight for your plan. Retry after one finishes. |
INTERNAL_ERROR | 500 | Unexpected error on our side. Safe to retry with the same Idempotency-Key. |
Data retention
| Source code and stdin | Deleted as soon as the execution finishes (or fails). Never written to logs. |
| Results (stdout, stderr, compiler output, status) | Kept for 24 hours after submission, then deleted. GET returns 404 afterwards. |
| Usage counts | Kept for billing: runs per day by language and status, without code or output. |
| Queued jobs | Encrypted at rest; held for at most a day, or 4 days if processing failed repeatedly. |
Sandbox & security
Each language runs in its own isolated micro-VM-based sandbox with no network route out (no internet, no DNS), no cloud credentials, process and memory limits, a wall-clock kill, and capped output. Jobs are encrypted for the sandbox and results are signed, and leftover files and processes are removed between runs. During the beta, CodeForge is invite-only while an independent security review is completed; we make no certification claims.
API reference
POST/v1/executions
Submit code for execution.
Queues one execution. Counts as one run against your monthly allowance and occupies one concurrency slot until it finishes.
Send an Idempotency-Key to make retries safe: the same key with the same request returns the original execution without a new run or charge.
Parameters
Idempotency-Key(header): Unique key for this logical request (recommended: a UUID). Valid for 24 hours. Reusing it with a different request body returns409.
Request body
| Field | Type | Details |
|---|---|---|
languagerequired | string | python, javascript, java, cpppython (CPython 3.12), javascript (Node.js 24), java (OpenJDK 21), cpp (g++ 11, C++17, -O2). |
sourcerequired | string | max 65,536 chars Program source (UTF-8, up to 64 KiB). Java: the class with main may have any name; a public class must match its name automatically. |
stdin | string | max 65,536 chars · default ""Standard input (up to 64 KiB). |
limits | object | timeMs: integer 100–10000, default 5000. Wall-clock limit for running the program. Compilation (Java/C++) has a separate budget and is not counted. |
Responses
202 | Accepted and queued (or an idempotent replay of an earlier request). |
400 | Invalid body (VALIDATION_ERROR) or Idempotency-Key (INVALID_IDEMPOTENCY_KEY). |
401 | Missing, invalid or revoked API key. |
409 | The Idempotency-Key was already used with a different request. |
413 | Request body too large. |
429 | CONCURRENCY_LIMIT (retryable: wait for a running job to finish) or QUOTA_EXCEEDED (not retryable until resetsAt). |
500 | Unexpected server error. Safe to retry with the same Idempotency-Key. |
GET/v1/executions/{executionId}
Get an execution's status and result.
Poll until status is terminal (anything other than QUEUED or RUNNING). Polling does not use quota. A sensible interval is 500–1000 ms. Results are kept for 24 hours after submission and then deleted; after that this returns 404. Source code and stdin are deleted as soon as the execution finishes.
Parameters
executionId(path, required):
Responses
200 | The execution. |
401 | Missing, invalid or revoked API key. |
404 | No such execution in your account. |
500 | Unexpected server error. Safe to retry with the same Idempotency-Key. |
GET/v1/usage
Get quota and usage.
This month's allowance and usage, current concurrency, and a daily breakdown for the last 30 days (UTC).
Responses
200 | Usage summary. |
401 | Missing, invalid or revoked API key. |
500 | Unexpected server error. Safe to retry with the same Idempotency-Key. |
Machine-readable spec: OpenAPI 3.1 (JSON). Import it into Postman, Insomnia or a client generator.