AI Code Runner Sandbox avatar

AI Code Runner Sandbox

Pricing

Pay per event

Go to Apify Store
AI Code Runner Sandbox

AI Code Runner Sandbox

Execute short JavaScript or Python snippets in a fresh bounded container process. Get structured status, stdout, stderr, parsed JSON, timing, and generated-file references for AI agents and automations.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Stas Persiianenko

Stas Persiianenko

Maintained by Community

Actor stats

0

Bookmarked

23

Total users

19

Monthly active users

11 days ago

Last modified

Share

Run short JavaScript or Python code in an Apify Actor and receive one predictable execution record.

AI Code Runner Sandbox is made for AI agents, automation workflows, and data teams that need to execute generated transformations without maintaining a runner service. Every run starts in a fresh temporary directory, accepts optional JSON through standard input, captures stdout and stderr, and finishes with structured status data.

  • 🧩 Choose Node.js 22 or Python 3
  • 📥 Pass JSON to the snippet through stdin
  • 📤 Parse JSON stdout into a ready-to-use result
  • ⏱️ Enforce a 1–30 second wall-clock timeout
  • 🛡️ Cap output, subprocess resources, and saved-file bytes
  • 📁 Copy selected generated files to the run key-value store

This is a bounded stateless process runner, not a persistent VM or interactive web shell. Version 1 does not install packages or retain a workspace between runs.

What does AI Code Runner Sandbox do?

The Actor writes your source code to a new temporary directory and starts the selected runtime as a dedicated unprivileged user. It sends your input to stdin and captures the subprocess output.

When execution ends, the Actor writes exactly one row to the default dataset. A syntax error, exception, non-zero exit, timeout, or output overflow is represented as a terminal execution record rather than hidden behind an empty dataset.

A successful record can include parsed JSON and references to generated files. This makes the output easy to route into the next step of an agent, n8n scenario, Make workflow, webhook, or data pipeline.

Who is it for?

AI agent builders

Use generated code as a small deterministic tool: calculate metrics, reshape JSON, validate an answer, or derive a decision before the next agent step.

n8n and Make users

Run a transformation that is awkward to express with visual mapping nodes. Consume the structured dataset row instead of parsing container logs.

Data and analytics teams

Apply a short Python calculation or JavaScript normalization to a JSON payload and save a compact CSV, JSON, or text artifact.

Developers and QA engineers

Reproduce a small algorithm, test a generated snippet, or validate output behavior with explicit exit codes and stderr.

Why use this code execution Actor?

Running generated snippets inside a long-lived integration server creates operational work. You need runtime installation, process supervision, timeout handling, output capture, storage cleanup, and a stable API envelope.

This Actor packages those concerns into one call:

  1. submit code and input;
  2. wait for the Actor run;
  3. read one dataset record;
  4. optionally retrieve generated files from the run key-value store.

The stateless design is especially useful when jobs are independent and repeatable. There is no session to reset and no old workspace to contaminate a later result.

Supported runtimes

LanguageRuntimeFile used during executionNotes
JavaScriptNode.js 22main.mjsES modules and built-in Node APIs
PythonPython 3main.pyPython standard library

Version 1 intentionally provides the standard runtimes only. It does not run npm install, pip install, shell commands supplied as input, coding-agent CLIs, or persistent services.

Your snippet can create files inside its temporary working directory. List the relative paths in saveFiles if you want those files copied to the run key-value store before cleanup.

Quick start: run JavaScript

Use this input in the Apify Console:

{
"language": "javascript",
"code": "const chunks=[]; for await (const c of process.stdin) chunks.push(c); const input=JSON.parse(Buffer.concat(chunks).toString()); console.log(JSON.stringify({sum: input.values.reduce((a,b)=>a+b,0), count: input.values.length}));",
"stdin": { "values": [3, 5, 8] },
"parseJsonOutput": true,
"timeoutSeconds": 5
}

The parsed result is:

{
"sum": 16,
"count": 3
}

Quick start: run Python

{
"language": "python",
"code": "import json, sys\ndata = json.load(sys.stdin)\nvalues = data['values']\nprint(json.dumps({'sum': sum(values), 'count': len(values)}))",
"stdin": { "values": [13, 21, 34] },
"parseJsonOutput": true,
"timeoutSeconds": 5
}

Python receives the serialized JSON on stdin. Write JSON and a trailing newline to stdout when parseJsonOutput is enabled.

Input reference

FieldTypeDefaultPurpose
languagejavascript or pythonjavascriptRuntime for the snippet
codestringrequiredSource code, up to 100 KB
stdinJSON value or stringomittedData sent to standard input
parseJsonOutputbooleantrueParse complete stdout into result
timeoutSecondsinteger5Wall timeout from 1 to 30 seconds
maxOutputBytesinteger256000Combined stdout/stderr cap, maximum 1 MB
saveFilesstring array[]Up to 10 generated relative paths
maxSavedFileBytesinteger5000000Combined saved-file cap, maximum 10 MB

Keep first runs small. A five-second timeout and no generated files are enough for most transformations.

Output data

Each run produces one execution record with these fields:

FieldMeaning
statussucceeded, failed, or timed_out
languageRuntime selected by the input
exitCodeNumeric subprocess exit code, or null after signal termination
signalTerminating signal when applicable
stdoutCaptured standard output
stderrCaptured standard error and spawn diagnostics
durationMsWall-clock execution duration
timedOutWhether the wall timer stopped execution
outputTruncatedWhether the combined output cap was reached
terminationReasontimeout, output_limit, or spawn_error when applicable
resultOptional JSON value parsed from stdout
resultParseErrorParsing error when stdout is not valid JSON
filesGenerated-file KVS references
executedAtISO 8601 terminal-record timestamp

Example execution record

{
"status": "succeeded",
"language": "javascript",
"exitCode": 0,
"signal": null,
"stdout": "{\"sum\":16,\"count\":3}\n",
"stderr": "",
"durationMs": 48,
"timedOut": false,
"outputTruncated": false,
"result": { "sum": 16, "count": 3 },
"files": [],
"executedAt": "2026-07-24T12:00:00.000Z"
}

User-code failures still produce a record. Check status and exitCode before trusting result.

Save generated files

A snippet may generate a CSV, JSON, HTML, or binary file in its working directory. Ask the Actor to preserve specific paths:

{
"language": "python",
"code": "from pathlib import Path\nPath('summary.txt').write_text('validated: 42\\n')\nprint('done')",
"saveFiles": ["summary.txt"],
"maxSavedFileBytes": 100000
}

The files array contains the original relative path, key-value-store key, byte size, content type, and store ID. Missing files are skipped with a warning. Directories, paths outside the working directory, and files beyond the aggregate cap are not copied.

Timeouts and output limits

timeoutSeconds is a wall-clock limit. When it expires, the Actor signals the whole detached subprocess group and follows with a forced kill if needed. This also stops child processes created by the snippet.

maxOutputBytes applies to stdout and stderr combined. Once reached, the Actor stops the process group, keeps the bytes already captured, sets outputTruncated: true, and reports terminationReason: "output_limit".

Python also receives an address-space limit. JavaScript receives a V8 heap limit. The Actor container memory allocation is the final boundary for the complete run.

Security model and limitations

The child process runs under dedicated UID/GID 10001, with no_new_privs, a minimal environment, limited CPU time, file descriptors, and process count. The child does not receive APIFY_TOKEN or other parent environment variables.

The temporary directory is deleted after the terminal record and requested files are stored. State never carries into another run.

However, this Actor is not a hardware VM and does not claim protection equivalent to a specialized microVM sandbox. Submitted code can use the runtime's networking APIs. Do not place passwords, API keys, private records, or untrusted third-party secrets in code or stdin. Use it for bounded transformations where you control or have reviewed the snippet.

Error handling

There are two error classes:

  • User-code outcomes — syntax errors, exceptions, non-zero exits, timeouts, and output overflows produce a dataset record.
  • Actor contract failures — missing language/code, invalid limits, oversized source, or unavailable runtime fail the Actor run.

This distinction prevents an invalid request from looking like successful code execution while preserving diagnostics for normal snippet failures.

If JSON parsing fails, the execution can still be succeeded. Read stdout and resultParseError to decide whether the data contract was met.

How much does it cost to run JavaScript or Python code?

The Actor uses pay-per-event pricing:

  • Sandbox start: $0.005 once per run
  • Code execution: BRONZE price of $0.00087345 per terminal record
  • higher subscription tiers receive automatic volume discounts

You pay for one execution event whether user code succeeds, exits non-zero, or times out, because the bounded runtime and diagnostic record are still produced. Apify Console shows the exact charge before and after each run. The execution tier was calibrated from measured cloud runtime cost and checked against equivalent PPE code-runner workflows.

Integration with n8n and Make

A practical n8n workflow is:

  1. HTTP Request node starts the Actor with JSON input.
  2. Wait/poll node waits for the run to finish.
  3. HTTP Request node reads the default dataset item.
  4. IF node checks status === "succeeded".
  5. The next node consumes result or retrieves a generated file.

In Make, use an HTTP module to call the Actor API and map result fields into later modules. Keep code static in the scenario when possible and pass changing values through stdin.

Apify API with cURL

curl "https://api.apify.com/v2/acts/automation-lab~ai-code-runner-sandbox/runs?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"language":"javascript","code":"console.log(JSON.stringify({ok:true}))","parseJsonOutput":true}'

Store tokens in secret variables. Never embed an Apify token inside the snippet itself.

Apify API with JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/ai-code-runner-sandbox').call({
language: 'javascript',
code: 'const input = JSON.parse(await new Response(process.stdin).text());',
stdin: { values: [1, 2, 3] },
parseJsonOutput: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0]);

For production code, use a complete stdin reader like the Quick start example rather than the abbreviated line above.

Apify API with Python

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/ai-code-runner-sandbox').call(run_input={
'language': 'python',
'code': "import json; print(json.dumps({'ok': True}))",
'parseJsonOutput': True,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items[0])

Use the returned defaultKeyValueStoreId when retrieving generated-file keys.

Use through Apify MCP

Connect an MCP-compatible agent to:

https://mcp.apify.com/?tools=automation-lab/ai-code-runner-sandbox

Claude Code setup

$claude mcp add --transport http apify-ai-code-runner "https://mcp.apify.com/?tools=automation-lab/ai-code-runner-sandbox"

Claude Desktop, Cursor, and VS Code setup

Add this HTTP server to the MCP JSON settings in Claude Desktop, Cursor, or VS Code:

{
"mcpServers": {
"apify-ai-code-runner": {
"url": "https://mcp.apify.com/?tools=automation-lab/ai-code-runner-sandbox"
}
}
}

Restart the client after saving the configuration. Claude Desktop users can then select the Actor tool; Cursor and VS Code users can enable it for the active agent session.

Example prompts for Claude Desktop or Claude Code:

  • “Run this JavaScript reducer with the supplied JSON and return the parsed result.”
  • “Execute this Python validation for five seconds and show stderr if it fails.”
  • “Generate summary.csv, preserve it, and return the key-value-store reference.”

The MCP client still starts an ordinary Actor run. The same timeout, output, and file limits apply.

Tips for reliable snippets

  • ✅ Print only JSON to stdout when parseJsonOutput is enabled.
  • ✅ Send progress or diagnostics to stderr.
  • ✅ Keep source deterministic and pass changing data through stdin.
  • ✅ Request only generated files that downstream steps need.
  • ✅ Check status, timedOut, and outputTruncated before using results.
  • ✅ Use integer and finite-loop boundaries in generated code.
  • ❌ Do not start servers or background daemons.
  • ❌ Do not depend on files from a previous run.
  • ❌ Do not print secrets.

Common workflows

Validate generated JSON

Run a Python schema check and return a compact list of violations.

Transform webhook payloads

Normalize inconsistent keys, dates, and nested arrays before writing to a CRM.

Calculate metrics

Compute weighted scores, aggregates, or statistical summaries that a prompt should not estimate.

Produce a small export

Create a CSV or text report and preserve it through saveFiles.

Test an AI-generated function

Call the function with fixed cases, emit assertions to stderr, and use the exit code as a gate.

Troubleshooting

Why is result missing?

Enable parseJsonOutput and ensure stdout contains one complete valid JSON value. Extra debug lines make the combined text invalid; write those lines to stderr instead.

Why was my generated file not saved?

The path must be relative to the working directory, identify a regular file, exist when the snippet exits, and fit within maxSavedFileBytes. Check the Actor log for a skip warning.

Why did a successful process have a parse error?

Process success and JSON validity are separate. Exit code 0 means the runtime completed; resultParseError means its stdout did not satisfy the requested JSON contract.

Can I install a package?

No. Version 1 intentionally supports built-in Node.js and Python standard-library modules only. Bundle small logic directly in the snippet.

Only execute code you are authorized to run. Do not use the Actor to attack systems, evade access controls, mine cryptocurrency, distribute malware, or process data in violation of privacy and contractual obligations.

You are responsible for the behavior of networking calls made by submitted code and for the data passed through stdin. Review generated code before production use and avoid secrets.

Combine this runner with other automation-lab Actors when a workflow needs collection before transformation:

Always verify that a related Actor fits your data source and compliance requirements.

FAQ

Is every run stateless?

Yes. The Actor creates and removes a fresh working directory during each run. Only dataset output and explicitly saved KVS records remain in Apify storage.

Does a code exception fail the Actor run?

No. A normal user-code exception produces status: "failed", the exit code, and stderr. Invalid Actor input or an internal runner failure fails the Actor itself.

Can code access my Apify token?

The child environment is explicitly scrubbed and does not contain APIFY_TOKEN. Do not paste tokens into code or stdin.

Is outbound networking disabled?

No. Runtime networking APIs remain available. Do not execute unknown hostile code or provide secrets.

Can I run several snippets in one Actor run?

Version 1 executes one snippet per run. Start separate runs for independent jobs so each has its own limits and execution record.

What happens after 30 seconds?

Thirty seconds is the maximum input timeout. The Actor terminates the subprocess group and returns a timed_out record.

Where are generated files stored?

Requested files are copied to the run's default key-value store. Their keys and store ID appear in the files array.