# Python & JS Code Runner – Sandbox for AI Agents & Workflows (`gazidev/code-runner-sandbox`) Actor

Run Python or JavaScript (Node.js) code with JSON input in a fresh process: timeout with process-group kill, stdout/stderr capture, structured result, exit code, duration, pip/npm packages, batch jobs and dataset rows. Built for AI agents (MCP), n8n, Make and Zapier. Pay per execution.

- **URL**: https://apify.com/gazidev/code-runner-sandbox.md
- **Developed by:** [Cemal Atakli](https://apify.com/gazidev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 code executions

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Python & JavaScript Code Runner: a code sandbox for AI agents, n8n and Make

Run **Python 3.13** or **JavaScript (Node.js 22)** code with a JSON input and get a structured result back: the **return value**, **stdout**, **stderr**, **exit code** and **duration**. Every job runs in a fresh process with a hard timeout. You can install **pip / npm packages** at runtime, run **batches** of jobs (the same code over many inputs, or many different snippets), and turn a returned list into **dataset rows**.

It is made for **AI agents (Apify MCP)**, **n8n / Make / Zapier** workflows and API users who need to "just run this snippet" without servers or a Docker setup.

### Why this code runner

- **Reliable by design.** A failing or hanging user script never fails the run. Each job ends with a clear `status` (`ok`, `error`, `timeout`), the error type and message, and a trimmed traceback.
- **A hard timeout that really stops the code.** When a job times out, its whole process group is killed, including child processes it started.
- **Structured result, not only text.** Python sets `result = ...`. JavaScript uses `return ...`. If the code returns nothing, the last stdout line is parsed as JSON.
- **Batch jobs.** Up to 1,000 jobs per run, with optional parallel execution (`concurrency`).
- **Packages.** `pythonPackages: ["pandas"]` and `npmPackages: ["lodash"]` are installed once per run and shared by all jobs.
- **Files.** Anything the code writes to `./output/` is saved to the run's key-value store, and the row links to it.
- **Safe defaults.** Your `APIFY_TOKEN` and other `*TOKEN` / `*SECRET` / `*PASSWORD` variables are **removed** from the code's environment unless you opt in. stdout and stderr are capped at 256 KB per job by default, and the result at 5 MB.
- **Cheap.** You pay **$0.0008 per execution**. There is no monthly fee and no start-up fee beyond Apify's tiny Actor start event.

### Code conventions

| | Python | JavaScript (Node.js) |
|---|---|---|
| Input | variable `input` (dict / list / value) | variable `input` |
| Return data | set top-level `result = {...}` | `return {...}`. You can also use `module.exports = value`, `module.exports = async (input) => ...` or `exports.main = async (input) => ...` |
| Async | top-level `await` works | top-level `await` works (the code runs inside an async function) |
| Packages | `import pandas` after adding `"pandas"` to *pip packages* | `require('lodash')` after adding `"lodash"` to *npm packages*. Use `require`, not `import` |
| Fallback | last stdout line, if it is valid JSON | last stdout line, if it is valid JSON |

The code can also use the file `$INPUT_PATH`, the env variable `INPUT_JSON` (for inputs up to 64 KB) and `$OUTPUT_DIR` (= `./output`).

### Input example

```json
{
  "language": "python",
  "code": "import statistics\nresult = {'mean': statistics.mean(input['numbers'])}",
  "inputJson": {"numbers": [4, 8, 15, 16, 23, 42]},
  "timeoutSecs": 30
}
```

Batch: the same code over many inputs, plus one JavaScript job with its own code:

```json
{
  "language": "python",
  "code": "result = input['n'] ** 2",
  "jobs": [
    {"id": "a", "input": {"n": 3}},
    {"id": "b", "input": {"n": 4}},
    {"id": "js", "language": "javascript", "code": "return input.text.toUpperCase();", "input": {"text": "hi"}}
  ],
  "concurrency": 4
}
```

### Output example (one dataset row per job)

```json
{
  "jobId": "python-stats",
  "language": "python",
  "status": "ok",
  "ok": true,
  "exitCode": 0,
  "durationMs": 50,
  "result": {"count": 6, "sum": 108, "mean": 18, "sorted": [4, 8, 15, 16, 23, 42]},
  "resultSource": "result-variable",
  "stdout": "Got 6 numbers\n",
  "stderr": "",
  "error": null,
  "files": [],
  "charged": true
}
```

A failed job looks like this: `"status": "error", "exitCode": 1, "error": {"type": "ValueError", "message": "..."}`, and `stderr` holds the traceback of your code only. A timeout gives `"status": "timeout"`.
The `OUTPUT` record in the key-value store holds the run summary. For a single job it also holds that job's `result`, which is handy for API calls.

With **Dataset output = rows**, a returned list such as `[{"name": "A"}, {"name": "B"}]` becomes dataset rows, each with a `_jobId` field. This lets your code act as a small scraper or data generator in a pipeline.

### Use with AI agents (Apify MCP)

Add the Actor to your MCP client through the Apify MCP server (`https://mcp.apify.com?tools=gazidev/code-runner-sandbox`). The agent can then call it as a tool:

- send `language`, `code` and `inputJson`, then read `result`, `stdout`, `stderr` and `error` from the dataset row
- the agent can retry after an error, because each failure says what went wrong (`error.type`, `error.message`, traceback)
- set `timeoutSecs` low (5 to 30 s) for agent loops, and keep `failRunOnError` off so the agent always gets the row

Typical agent uses: exact math and statistics, date arithmetic, parsing and transforming JSON or CSV, checking a regex, testing a generated function against cases, and small data jobs with pandas.

### Use in n8n, Make and Zapier

Use the "Run Actor and get dataset items" action. To branch on failures, turn on **Fail the run if any job fails**. Otherwise, check `ok` in each row.

### Pricing

| Event | Price | When |
|---|---|---|
| Execution | **$0.0008** | per finished job, including the first 5 s of run time |
| Extra run time | $0.004 | per started 10 s beyond the first 5 s, per started GB of run memory (default memory is 512 MB, which counts as 1 GB) |
| Packages installed | $0.005 | once per run, only when pip/npm packages were installed successfully |

A finished job is charged even when your code raises an error, exits non-zero or times out, because the code did run. **Not charged:** invalid input, package install failures (no job runs), jobs that were never started, and platform errors.

Examples: 1,000 short jobs cost about **$0.80**. A single 2-second snippet costs $0.0008 plus the Actor start event.

**Max cost per run** is respected. Jobs start only while your budget covers them. A long job's timeout is shortened to fit the budget, and the rest of the jobs are listed as `not-run`.

| Actor | Price per execution (FREE tier) | Notes |
|---|---|---|
| **This Actor** | **$0.0008** + time beyond 5 s | Python and JS, batch, pip/npm, rows output |
| parsebird/code-runner-sandbox | $0.00099 | one execution per run |
| automation-lab/ai-code-runner-sandbox | $0.0010 + $0.005 start | |
| rixin/execute-javascript-code | $0.0009 per second | JavaScript only |
| apify/ai-code-sandbox | free (you pay the compute) | interactive sandbox, different use case |

(Store prices checked on 2026-10-24.)

### FAQ

**Is it secure?** Every job runs in a separate process inside *your own* Apify run container. Apify isolates that container from other users. Secrets are removed from the job environment by default. There is no extra seccomp or network jail inside the container, so the code can reach the internet. Treat untrusted code the way you would in any container.

**Can my code call the Apify API?** Yes. Turn on *Give the code my APIFY_TOKEN*. The Python `apify` / `apify-client` packages are already installed.

**Which versions are installed?** Python 3.13 and Node.js 22 with npm. Python's standard library is available with no installs.

**What happens if the job prints a lot?** stdout and stderr are cut at `maxOutputKb` (default 256 KB, max 4 MB), and `stdoutTruncated` is set. Return big data as a list (rows mode) or as files in `./output/`.

**What about memory?** Jobs share the run memory: 512 MB by default, and you can set it up to 8 GB in the run options. Raise it for pandas and big inputs, or when you use `concurrency` > 1.

**Are results returned in order?** With `concurrency` 1 the rows are in job order. With parallel jobs they are in the order the jobs finish. Use `jobId` to match them.

# Actor input Schema

## `language` (type: `string`):

Language of the top-level <code>code</code>. Jobs can override it with their own <code>language</code>.

## `code` (type: `string`):

Code to run. The job input is available as the variable <code>input</code> (also as file <code>$INPUT_PATH</code> and env <code>INPUT_JSON</code>).<br><b>Python:</b> set a top-level <code>result</code> variable (top-level <code>await</code> works).<br><b>JavaScript:</b> the code is the body of an async function - <code>return</code> a value, or set <code>module.exports</code>. Use <code>require()</code>, not <code>import</code>.<br>Fallback for both: if nothing is returned, the last line of stdout is parsed as JSON.

## `inputJson` (type: `object`):

JSON value passed to the code as <code>input</code>. Used by the top-level code and by jobs without their own <code>input</code>.

## `jobs` (type: `array`):

Optional batch: a list of jobs, each <code>{"id": "...", "language": "python|javascript", "code": "...", "input": {...}, "timeoutSecs": 30}</code>. Every field is optional: a job without <code>code</code> reuses the top-level code (same code, many inputs). When this list is not empty the top-level code is <b>not</b> run on its own. Max 1,000 jobs per run. One dataset row per job.

## `timeoutSecs` (type: `integer`):

Wall-clock limit for one job. On timeout the job's whole process group is killed and the row gets status <code>timeout</code>.

## `concurrency` (type: `integer`):

How many jobs run at the same time. Raise it for big batches of short jobs (and give the run more memory).

## `outputMode` (type: `string`):

<b>jobs</b>: one dataset row per job. <b>rows</b>: when the code returns a list, each element becomes its own dataset row (with <code>\_jobId</code>); max 10,000 rows per job. Job details are always in the <code>OUTPUT</code> record.

## `pythonPackages` (type: `array`):

Installed with pip once per run before the jobs start, e.g. <code>requests</code>, <code>pandas==2.2.3</code>. Max 30 packages in total, 1.5 GB, install timeout below. Adds 10-60 s to the run.

## `npmPackages` (type: `array`):

Installed with npm once per run, e.g. <code>lodash</code>, <code>dayjs@1</code>. Load them with <code>require('lodash')</code>.

## `packageInstallTimeoutSecs` (type: `integer`):

If pip/npm install fails or takes longer, no job runs, nothing is charged and the run fails with the installer log.

## `envVars` (type: `object`):

Extra environment variables for the code, e.g. <code>{"MODE": "test"}</code>. Not stored as secrets - do not put passwords here.

## `exposeApifyToken` (type: `boolean`):

Off by default: the code's environment gets no APIFY_TOKEN and no other \*TOKEN/\*SECRET/\*PASSWORD variables. Turn on only if your code must call the Apify API (e.g. to read a dataset or start another Actor).

## `saveOutputFiles` (type: `boolean`):

Files the code writes into <code>./output/</code> (path also in env <code>OUTPUT_DIR</code>) are saved to the run's key-value store and linked in the row (<code>files\[].url</code>). Max 20 files of 20 MB per job.

## `maxOutputKb` (type: `integer`):

Longer output is cut (flag <code>stdoutTruncated</code>). The returned result can be up to 5 MB.

## `includeCodeInOutput` (type: `boolean`):

Useful for audit logs of AI-generated code.

## `failRunOnError` (type: `boolean`):

Off: the run succeeds and each failed job is a row with <code>status</code> error/timeout (best for agents and loops). On: the run ends as FAILED when any job did not succeed (useful in n8n/Make/Zapier to branch on failure).

## Actor input object example

```json
{
  "language": "python",
  "code": "# `input` holds the job's JSON input. Set `result` to return data (any JSON-serializable value).\nimport statistics\n\nnums = input.get(\"numbers\", [])\nprint(f\"Got {len(nums)} numbers\")\nresult = {\n    \"count\": len(nums),\n    \"sum\": sum(nums),\n    \"mean\": statistics.mean(nums) if nums else None,\n    \"sorted\": sorted(nums),\n}\n",
  "inputJson": {
    "numbers": [
      1,
      2,
      3
    ]
  },
  "jobs": [
    {
      "id": "python-stats",
      "input": {
        "numbers": [
          4,
          8,
          15,
          16,
          23,
          42
        ]
      }
    },
    {
      "id": "js-word-count",
      "language": "javascript",
      "code": "// `input` holds the job's JSON input. `return` a value (top-level await works).\nconst words = (input.text || '').toLowerCase().match(/[a-z']+/g) || [];\nconst counts = {};\nfor (const w of words) counts[w] = (counts[w] || 0) + 1;\nconsole.log(`Counted ${words.length} words`);\nreturn Object.entries(counts)\n  .sort((a, b) => b[1] - a[1])\n  .slice(0, 5)\n  .map(([word, count]) => ({ word, count }));\n",
      "input": {
        "text": "The quick brown fox jumps over the lazy dog. The dog sleeps; the fox runs."
      }
    }
  ],
  "timeoutSecs": 30,
  "concurrency": 1,
  "outputMode": "jobs",
  "pythonPackages": [
    "requests",
    "python-dateutil"
  ],
  "npmPackages": [
    "lodash",
    "dayjs"
  ],
  "packageInstallTimeoutSecs": 180,
  "exposeApifyToken": false,
  "saveOutputFiles": true,
  "maxOutputKb": 256,
  "includeCodeInOutput": false,
  "failRunOnError": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `files` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "code": `# `input` holds the job's JSON input. Set `result` to return data (any JSON-serializable value).
import statistics

nums = input.get("numbers", [])
print(f"Got {len(nums)} numbers")
result = {
    "count": len(nums),
    "sum": sum(nums),
    "mean": statistics.mean(nums) if nums else None,
    "sorted": sorted(nums),
}`,
    "inputJson": {
        "numbers": [
            1,
            2,
            3
        ]
    },
    "jobs": [
        {
            "id": "python-stats",
            "input": {
                "numbers": [
                    4,
                    8,
                    15,
                    16,
                    23,
                    42
                ]
            }
        },
        {
            "id": "js-word-count",
            "language": "javascript",
            "code": "// `input` holds the job's JSON input. `return` a value (top-level await works).\nconst words = (input.text || '').toLowerCase().match(/[a-z']+/g) || [];\nconst counts = {};\nfor (const w of words) counts[w] = (counts[w] || 0) + 1;\nconsole.log(`Counted ${words.length} words`);\nreturn Object.entries(counts)\n  .sort((a, b) => b[1] - a[1])\n  .slice(0, 5)\n  .map(([word, count]) => ({ word, count }));\n",
            "input": {
                "text": "The quick brown fox jumps over the lazy dog. The dog sleeps; the fox runs."
            }
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gazidev/code-runner-sandbox").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "code": """# `input` holds the job's JSON input. Set `result` to return data (any JSON-serializable value).
import statistics

nums = input.get(\"numbers\", [])
print(f\"Got {len(nums)} numbers\")
result = {
    \"count\": len(nums),
    \"sum\": sum(nums),
    \"mean\": statistics.mean(nums) if nums else None,
    \"sorted\": sorted(nums),
}
""",
    "inputJson": { "numbers": [
            1,
            2,
            3,
        ] },
    "jobs": [
        {
            "id": "python-stats",
            "input": { "numbers": [
                    4,
                    8,
                    15,
                    16,
                    23,
                    42,
                ] },
        },
        {
            "id": "js-word-count",
            "language": "javascript",
            "code": """// `input` holds the job's JSON input. `return` a value (top-level await works).
const words = (input.text || '').toLowerCase().match(/[a-z']+/g) || [];
const counts = {};
for (const w of words) counts[w] = (counts[w] || 0) + 1;
console.log(`Counted ${words.length} words`);
return Object.entries(counts)
  .sort((a, b) => b[1] - a[1])
  .slice(0, 5)
  .map(([word, count]) => ({ word, count }));
""",
            "input": { "text": "The quick brown fox jumps over the lazy dog. The dog sleeps; the fox runs." },
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("gazidev/code-runner-sandbox").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "code": "# `input` holds the job'\''s JSON input. Set `result` to return data (any JSON-serializable value).\\nimport statistics\\n\\nnums = input.get(\\"numbers\\", [])\\nprint(f\\"Got {len(nums)} numbers\\")\\nresult = {\\n    \\"count\\": len(nums),\\n    \\"sum\\": sum(nums),\\n    \\"mean\\": statistics.mean(nums) if nums else None,\\n    \\"sorted\\": sorted(nums),\\n}\\n",
  "inputJson": {
    "numbers": [
      1,
      2,
      3
    ]
  },
  "jobs": [
    {
      "id": "python-stats",
      "input": {
        "numbers": [
          4,
          8,
          15,
          16,
          23,
          42
        ]
      }
    },
    {
      "id": "js-word-count",
      "language": "javascript",
      "code": "// `input` holds the job'\''s JSON input. `return` a value (top-level await works).\\nconst words = (input.text || '\'''\'').toLowerCase().match(/[a-z'\'']+/g) || [];\\nconst counts = {};\\nfor (const w of words) counts[w] = (counts[w] || 0) + 1;\\nconsole.log(`Counted ${words.length} words`);\\nreturn Object.entries(counts)\\n  .sort((a, b) => b[1] - a[1])\\n  .slice(0, 5)\\n  .map(([word, count]) => ({ word, count }));\\n",
      "input": {
        "text": "The quick brown fox jumps over the lazy dog. The dog sleeps; the fox runs."
      }
    }
  ]
}' |
apify call gazidev/code-runner-sandbox --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gazidev/code-runner-sandbox"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ka0DjtQYQ07hTirSe/builds/z6YzOc3HCeIhKzZgn/openapi.json
