AI Code Runner Sandbox
Pricing
Pay per event
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
Maintained by CommunityActor stats
0
Bookmarked
23
Total users
19
Monthly active users
11 days ago
Last modified
Categories
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:
- submit code and input;
- wait for the Actor run;
- read one dataset record;
- 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
| Language | Runtime | File used during execution | Notes |
|---|---|---|---|
| JavaScript | Node.js 22 | main.mjs | ES modules and built-in Node APIs |
| Python | Python 3 | main.py | Python 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
| Field | Type | Default | Purpose |
|---|---|---|---|
language | javascript or python | javascript | Runtime for the snippet |
code | string | required | Source code, up to 100 KB |
stdin | JSON value or string | omitted | Data sent to standard input |
parseJsonOutput | boolean | true | Parse complete stdout into result |
timeoutSeconds | integer | 5 | Wall timeout from 1 to 30 seconds |
maxOutputBytes | integer | 256000 | Combined stdout/stderr cap, maximum 1 MB |
saveFiles | string array | [] | Up to 10 generated relative paths |
maxSavedFileBytes | integer | 5000000 | Combined 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:
| Field | Meaning |
|---|---|
status | succeeded, failed, or timed_out |
language | Runtime selected by the input |
exitCode | Numeric subprocess exit code, or null after signal termination |
signal | Terminating signal when applicable |
stdout | Captured standard output |
stderr | Captured standard error and spawn diagnostics |
durationMs | Wall-clock execution duration |
timedOut | Whether the wall timer stopped execution |
outputTruncated | Whether the combined output cap was reached |
terminationReason | timeout, output_limit, or spawn_error when applicable |
result | Optional JSON value parsed from stdout |
resultParseError | Parsing error when stdout is not valid JSON |
files | Generated-file KVS references |
executedAt | ISO 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:
- HTTP Request node starts the Actor with JSON input.
- Wait/poll node waits for the run to finish.
- HTTP Request node reads the default dataset item.
- IF node checks
status === "succeeded". - The next node consumes
resultor 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 osfrom apify_client import ApifyClientclient = 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().itemsprint(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
parseJsonOutputis 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, andoutputTruncatedbefore 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.
Responsible and legal use
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.
Related automation tools
Combine this runner with other automation-lab Actors when a workflow needs collection before transformation:
- Tech Stack Detector for technology data that a snippet can score or group
- GitHub Scraper for repository data that a snippet can normalize
- WHOIS Lookup for domain records that a snippet can validate
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.


