# Code Runtime (DO NOT REMOVE OR AI TEAM IS AFTER YOU) (`apify/code-runtime`) Actor

- **URL**: https://securitybyobscurity.apify.com/apify/code-runtime.md
- **Developed by:** [Apify Technologies](https://securitybyobscurity.apify.com/apify) (community)
- **Categories:** AI
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Code Runtime (experimental)

> ⚠️ **Experimental infrastructure Actor.** It powers **Code Mode** on
> [mcp.apify.com](https://mcp.apify.com) and is normally invoked by the Apify
> MCP Server, not run by hand. Its behaviour and API may change without notice.

### What it does

Executes one JS script that an AI agent submits through the Apify MCP
Server, then returns whatever the script printed.

Lets an agent do many Apify operations in **one call** — search the Store,
run an Actor, read its dataset, filter and aggregate — instead of sending
every intermediate result back through the model. This Actor is the sandbox
that runs that script.

**Worth it only for bulk work** (measured, A/B eval vs. calling Actor tools
directly):

| Workload | Verdict |
|---|---|
| Filter/sort/aggregate 50+ dataset records | Modest win — ~20-35% less time, ~20% fewer tokens |
| Fan out over 10+ sub-resources with a sizeable payload each (visit many pages, chain Actors) | Decisive win — ~60% less time, ~75% fewer tokens |
| Under 10 items, no fan-out | **Don't use this Actor** — ~20K-token sandbox overhead isn't paid back |

### Calling this Actor

Self-contained — no special MCP-server opt-in required. Any MCP client
already has `search-actors`, `fetch-actor-details`, and `call-actor` as
default tools:

```
call-actor({ actor: "apify/code-runtime", input: { code: "..." } })
```

Default `timeoutSecs: 900`, `memoryMbytes: 1024` (`.actor/actor.json`) —
override per call for scripts chaining several long Actor runs (MCP
`call-actor`'s `callOptions.timeout`/`callOptions.memory`, or the API's
`timeout`/`memory`).

### How it works

- **One script per run.** Reads `code`, runs it once, writes the result, exits.
- Runs inside a sandboxed [`workerd`](https://github.com/cloudflare/workerd)
  V8 isolate — see [Permissions & safety](#permissions--safety) for what's allowed.
- A global **`apify`** object exposes a small, typed subset of the Apify API
  — run Actors, read/write datasets and key-value stores — using the
  current run's token.
- `console.log`/`console.info` → **stdout**; `console.error`/`console.warn`
  → **stderr**, captured separately.
- Call `apify.actor.get({ actorId })` before running an Actor you haven't
  checked — don't guess its input schema.
- Log a nested run's `run.id`/`defaultDatasetId`/`defaultKeyValueStoreId`
  before processing its output — nothing persists between this Actor's own
  runs, but the Actors it started keep theirs.
- Already have a dataset/store ID from an earlier turn? Reuse it — don't
  re-run an identical call, it wastes cost.
- Print a small JSON summary, never a full dataset — only `console.log`/
  `console.info` output comes back; a top-level `return` is **not** captured.
- `callAndGetItems` reads the dataset once, right after its (max 60s) wait —
  if the child run is still `RUNNING` at that point, `items` may be empty or
  partial. Check the returned `run.status` before treating it as final.

### Input

```json
{
  "code": "const { items } = await apify.actor.callAndGetItems({ actorId: 'apify/rag-web-browser', input: { query: 'apify' }, limit: 3 });\nconsole.log(items.map((i) => i.metadata?.title).join('\\n'));"
}
```

| Field | Type | Description |
|---|---|---|
| `code` | string | The JavaScript script to run (JS only, not transpiled). It receives the `apify` binding and `console`. |
| `maxActorRuns` | number | *Optional.* Caps how many Actor runs the script may start in total; exceeding it throws inside the script. |
| `maxTotalChargeUsd` | number | *Optional.* Execution-level spending budget across all runs the script starts (distinct from a single run's own `maxTotalChargeUsd`); exhausting it throws inside the script. |
| `defaultTimeoutSecs` | number | *Optional.* Default `timeoutSecs` for child runs that don't set their own. |

### Output

A single **dataset item**:

```json
{ "stdout": "Apify: Full-stack web scraping ...\n...", "stderr": "", "exitCode": 0, "statusMessage": "Script completed" }
```

| Outcome | `exitCode` | `statusMessage` |
|---|---|---|
| Script returned | `0` | `Script completed` |
| Script threw | `1` | `Script threw: ...` |
| Failed to compile (syntax error) | `1` | `Failed to compile: ...` |
| Run-level timeout / OOM kill | — | item may not exist for this run at all |

Check `exitCode`/`statusMessage`, not `stderr` content, to detect a failed
script — `stderr` also carries `console.error`/`console.warn` output, so its
presence alone isn't failure. A timeout/OOM kill is signaled by the Actor
run's own status (`SUCCEEDED` vs `FAILED`/`ABORTED`/`TIMED-OUT`), not by this
item's absence.

### Permissions & safety

- Sandbox has **no filesystem**; outbound `fetch` (redirects re-validated
  per hop) is limited to the Apify API (`*.apify.com`).
- **No imports** — runs without workerd's `nodejs_compat`, so no Node
  built-ins (`node:net`, `node:fs`, …) or npm packages. This also removes
  `node:net` (a raw-socket path that would bypass the `fetch` allowlist) and
  keeps the run token out of `process.env` (undefined here).
- Each run is an isolated, single-use container — nothing persists between runs.
- This closes **direct fetch-based exfil** — it does not close every path to
  move data out (e.g. `actor.start({ input })` on an Actor with its own
  internet access, or writing to a dataset/key-value store).

### Recipes

#### Chain Actors (one run's output feeds the next)

```js
const { items: results } = await apify.actor.callAndGetItems({
    actorId: 'apify/google-search-scraper', input: { queries: 'apify' }, limit: 10,
});
const startUrls = results.flatMap((r) => r.organicResults ?? []).map((r) => ({ url: r.url }));
const { items: pages } = await apify.actor.callAndGetItems({
    actorId: 'apify/website-content-crawler', input: { startUrls },
});
console.log(JSON.stringify(pages.slice(0, 3).map((p) => p.url)));
```

#### Bounded parallel fan-out

This Actor's clearest win: run several Actors (or the same Actor over
several inputs) concurrently, then reduce before returning. Chunk it (5–10
at a time) — an unbounded `Promise.all` can hit your account's
concurrent-run or memory limits.

```js
const inputs = [{ query: 'a' }, { query: 'b' }, { query: 'c' } /* ... */];
const CHUNK = 5;
const results = [];
for (let i = 0; i < inputs.length; i += CHUNK) {
    const batch = inputs.slice(i, i + CHUNK);
    const batchResults = await Promise.all(
        batch.map((input) => apify.actor.callAndGetItems({ actorId: 'apify/rag-web-browser', input, limit: 5 })),
    );
    results.push(...batchResults.flatMap((r) => r.items));
}
console.log(JSON.stringify(results.slice(0, 5))); // small summary, not the full dump
```

#### Read an entire dataset without managing offsets

`dataset.listItems`/`store` work two ways — `await` for one page, `for
await` to auto-paginate every item (see [the apify binding](#the-apify-binding)):

```js
// One page — e.g. a quick peek
const { items, count } = await apify.dataset.listItems({ datasetId, limit: 10 });

// Every item, however many pages that takes
let matches = 0;
for await (const item of apify.dataset.listItems({ datasetId })) {
    if (item.rating >= 4.5) matches++;
}
console.log(`${matches} matching items`);
```

#### Runs longer than 60s: start, then poll

`actor.call`'s wait is capped at 60s per request (a REST API limit, not this
Actor's). For a longer-running Actor, start it and poll:

```js
let run = await apify.actor.start({ actorId, input });
const TERMINAL = ['SUCCEEDED', 'FAILED', 'ABORTED', 'TIMED-OUT'];
while (!TERMINAL.includes(run.status)) {
    run = await apify.run.waitForFinish({ runId: run.id, waitForFinishSecs: 60 });
}
```

### The `apify` binding

Every method takes one options object and returns parsed JSON — except
`store` and `dataset.listItems`, which return a value that's both a
`Promise` (one page) and an `AsyncIterable` (every match/item,
auto-paginated). Full API docs:
[API.md](https://github.com/apify/actor-code-runtime/blob/master/docs/API.md).
(`?` = optional, `= x` = default)

```js
// Store — GET /v2/store, a top-level Apify API resource (not an Actor method)
apify.store({ search, limit?, offset?, category? })  // → { items, count, offset, limit }; dual Promise/AsyncIterable, see above

// Actors
apify.actor.get({ actorId })                                  // → actor
apify.actor.start({ actorId, input?, memoryMbytes?, timeoutSecs?, maxTotalChargeUsd?, maxItems? })  // → run
apify.actor.call({ actorId, ...startOpts, waitForFinishSecs = 60 })           // → run (may be non-terminal READY/RUNNING past the 60s cap — not an error, see Recipes)
apify.actor.callAndGetItems({ actorId, input?, fields?, limit?, ...runOpts })  // → { run, items } (items may be partial if run is still RUNNING — check run.status)

// Runs
apify.run.get({ runId })                                    // → run
apify.run.waitForFinish({ runId, waitForFinishSecs = 60 })  // → run (same non-terminal caveat)
apify.run.abort({ runId })                                  // → run
apify.run.getLog({ runId, limit? })                         // → string

// Datasets
apify.dataset.create({ name? })                             // → dataset
apify.dataset.pushItems({ datasetId, items })               // → void
apify.dataset.listItems({ datasetId, fields?, omit?, limit?, offset?, clean?, desc? })  // → { items, count, offset, limit, desc }; dual Promise/AsyncIterable, see above
apify.dataset.inferFields({ datasetId, sample = 5 })        // → { itemCount, fields[] }

// Key-value stores
apify.keyValueStore.create({ name? })                        // → store
apify.keyValueStore.set({ storeId, key, value, contentType? })  // → void
apify.keyValueStore.get({ storeId, key })                    // → value | null
apify.keyValueStore.list({ storeId, limit?, exclusiveStartKey? })  // → { items }
```

### Learn more

- Apify MCP Server: <https://mcp.apify.com>

# Actor input Schema

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

JavaScript executed in the sandbox with `apify` and `console` globals; only console output is captured and pushed to the dataset as { stdout, stderr, exitCode, statusMessage } — a top-level `return` value is NOT captured. Before writing code that calls a specific Actor, check its real input field names first — via fetch-actor-details (outside this script, before you write it) or apify.actor.get({ actorId }) (inside it, for an Actor picked at runtime). Do not guess field names from memory; a wrong one throws a fast 400, but costs a wasted round trip. Print a small JSON summary of the result — never dump full datasets. Write top-level `await` statements directly in the script; do NOT wrap your logic in an async function you call without awaiting (e.g. `async function main(){...}; main()`) — the script returns as soon as the top-level body finishes, silently discarding anything still pending, with no error. Every apify.\* method takes ONE options object keyed by id, e.g. apify.actor.call({ actorId, input }), apify.dataset.listItems({ datasetId, limit }) — this is NOT the public apify-client SDK's curried apify.actor(id).call(input) shape. If a prior attempt already logged a nested run's defaultDatasetId/defaultKeyValueStoreId (visible in your own earlier turns), reuse it — do NOT re-run the same Actor call with identical input, that wastes compute on a call that already succeeded. apify.actor.call/run.waitForFinish may return non-terminal (READY/RUNNING) once the 60s wait cap elapses — that is NOT a failure, poll again instead of throwing.

## `maxActorRuns` (type: `integer`):

Caps how many Actor runs this script may start in total across actor.start/actor.call/actor.callAndGetItems. Starting one more once the limit is reached throws inside the script. Omit for no limit.

## `maxTotalChargeUsd` (type: `number`):

Execution-wide spending budget across every Actor run this script starts — distinct from a single call's own maxTotalChargeUsd, which only caps that one run. Each run's own cap is clamped so the combined total never exceeds this budget; starting a run once it's exhausted throws inside the script. Omit for no limit.

## `defaultTimeoutSecs` (type: `integer`):

Applied as timeoutSecs to actor.start/actor.call/actor.callAndGetItems calls that don't specify their own. Omit to use the Apify API's own default.

## Actor input object example

```json
{}
```

# Actor output Schema

## `output` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("apify/code-runtime").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("apify/code-runtime").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 '{}' |
apify call apify/code-runtime --silent --output-dataset

```

## MCP server setup

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

```

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-securitybyobscurity.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api-securitybyobscurity.apify.com/v2/actors/eclsWeWmhK1ETo7js/builds/kTXpEAf92a7QnmVfv/openapi.json
