# Python Code Runner - pip, pandas & Dataset In/Out (`automa-flow/python-code-runner`) Actor

Run your Python main() on rows from a Dataset, JSON or n8n in one API call. pip packages from a requirements field, pandas and openpyxl preinstalled, encrypted secrets as environment variables, rows back with per-item status.

- **URL**: https://apify.com/automa-flow/python-code-runner.md
- **Developed by:** [Vadim Bezrukov](https://apify.com/automa-flow) (community)
- **Categories:** Developer tools, Automation, Integrations
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 code runs

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 Code Runner - pip, pandas & Dataset In/Out

Run your own Python `main()` on rows in **one API call** and get rows back. Send the items of an n8n, Make or Zapier step as `rows`, or point `datasetId` at another Actor's Dataset; pandas, numpy and openpyxl are already installed, extra packages come from a `requirements` field, API keys go in encrypted `secrets` that your code reads as environment variables. Whatever `main` returns lands in the run's Dataset in your own shape, and a failure arrives as an explicit status row, never as an empty success.

Built for automation steps that need real Python: an n8n Cloud Code node that refuses `import pandas` or `import re`, a Pipedream Python step that has to move before Pipedream Workflows shut down on 2027-03-31, or an agent asked to "run this Python with pandas".

Latest notes are in the [changelog](https://apify.com/automa-flow/python-code-runner/changelog).

### What the first run returns

The Console prefill sends three order rows and this code:

```python
import pandas as pd


def main(rows, ctx):
    df = pd.DataFrame(rows)
    df["total"] = df["price"] * df["qty"]
    return df.sort_values("total", ascending=False)
```

The default Dataset gets three rows, sorted, each with the new `total` column, and `RUN_SUMMARY` says `SUCCESS`:

```json
[
  {"sku": "C-3", "price": 99.0, "qty": 1, "total": 99.0},
  {"sku": "B-2", "price": 3.2, "qty": 20, "total": 64.0},
  {"sku": "A-1", "price": 12.5, "qty": 4, "total": 50.0}
]
```

That run costs about **$0.0043**: one `code-run` event ($0.004), which includes the first 30 GB-seconds of compute, plus the $0.0003 start event.

### n8n in one node

Add an **HTTP Request** node after the node that holds your items:

- Method `POST`, URL `https://api.apify.com/v2/acts/automa-flow~python-code-runner/run-sync-get-dataset-items`
- Authentication: a Header Auth credential with `Authorization` = `Bearer <your Apify API token>`. Keep the token in the credential, never in the URL.
- Body (JSON), with the previous node's items as `rows`:

```json
{
  "code": "import pandas as pd\n\ndef main(rows, ctx):\n    df = pd.DataFrame(rows)\n    return df.groupby('customer', as_index=False)['amount'].sum()\n",
  "rows": {{ JSON.stringify($input.all().map(item => item.json)) }}
}
```

The response is the list of rows `main` returned, ready for the next node (Google Sheets, Airtable, a database, an LLM step). The official Apify node for n8n works the same way: pick this Actor, paste the same input and read the run's Dataset items. Set `failRunOnError` to `true` if the workflow should stop on a code error.

### Why use it

- **Rows in, rows out, one call.** Inline `rows`, a Dataset (`datasetId`) or an Actor-to-Actor integration go in; the rows your code returns come out in your own columns, with no envelope to unwrap.
- **pip packages from a field.** pandas and 15 other common packages are preinstalled, so most runs install nothing. Add more with `requirements`, one PyPI line each.
- **Secrets as environment variables.** Keys go in `secrets` (encrypted in the input), your code reads `os.environ["API_KEY"]`, and the values are removed from logs, status rows and `RUN_SUMMARY`. The code text itself is never logged, only its SHA-256.
- **A status for every item.** In `eachItem` mode one bad row never stops the others; `RUN_SUMMARY` lists the input indexes to resend.
- **Bounded.** Code timeout, output row cap, row size cap, log cap and `maxTotalChargeUsd` all hold, so an agent or a schedule cannot run away with a bill.
- **Made to be called again.** Every n8n or Make execution, schedule tick or scraper run is one call with the same code and new rows, so the Python step lives inside the workflow instead of on someone's laptop.

### Call modes

| `callMode` | Calls | Use it for |
| --- | --- | --- |
| `allItems` (default) | `main(rows, ctx)` once, with the list of all input rows | pandas reshaping, grouping, joins, one summary |
| `eachItem` | `main(row, ctx)` once per input row | per-row scoring, parsing or API calls where one failure must not stop the rest |

The names follow n8n's "Run Once for All Items" and "Run Once for Each Item". `main` may be `async`, may be a generator, and may take fewer parameters (`main()`, `main(rows)`). With no `rows` and no `datasetId`, `main([], ctx)` runs once as a script and reads its settings from `ctx.input`.

`ctx` holds `ctx.input` (the `input` object), `ctx.call_mode`, `ctx.item_index` (the row index in `eachItem` mode) and `ctx.save_file(name, data, content_type=None)`.

### Input

| Field | Default | Notes |
| --- | --- | --- |
| `code` | prefill | Python 3.12 source that defines `main`, up to 200,000 characters |
| `callMode` | `allItems` | `allItems` or `eachItem` |
| `rows` | prefill | Up to 10,000 JSON objects. Use `rows` or `datasetId`, not both |
| `datasetId` | none | Dataset to read; in an integration use `{{resource.defaultDatasetId}}` |
| `datasetOffset` / `maxRows` | 0 / 10,000 | Range of Dataset rows, up to 100,000 per run |
| `fields` | all | Up to 50 top-level fields to read from each Dataset row |
| `input` | `{}` | Parameters your code reads as `ctx.input` |
| `requirements` | empty | Extra PyPI packages, one per line, up to 30 lines |
| `secrets` | none | Up to 20 environment variables, encrypted |
| `timeoutSeconds` | 60 | 1-900 s for starting Python and running `main` |
| `installTimeoutSeconds` | 120 | 10-300 s for the install |
| `maxOutputRows` | 10,000 | 1-100,000; rows above it are dropped and counted |
| `maxRowBytes` | 1 MiB | 1 KiB to 4 MiB per returned row |
| `maxLogBytes` / `logUserOutput` | 256 KiB / on | How much `print` output reaches the run log |
| `statusRows` | `failures` | `failures`, `all` or `none` |
| `failRunOnError` | off | End the run FAILED when the status is not `SUCCESS` or `NO_ROWS` |
| `maxInputBytes` | 64 MiB | 1-256 MiB of input passed to `main` |

Run memory is 1,024 MB by default (1,024 to 4,096 MB), the run timeout 1,800 s. A synchronous API call (`run-sync-get-dataset-items`) waits at most 300 s, so start longer jobs asynchronously and read the Dataset when the run finishes.

### Return value rules

| `main` returns | Rows written |
| --- | --- |
| `None` | none (`NO_ROWS`) |
| a `dict` | one row |
| a list, tuple, generator or async generator of dicts | those rows |
| a pandas `DataFrame` | one row per record (`to_dict(orient="records")`) |
| a pandas `Series` of dicts | those dicts |

Values convert the same way every time: NaN, NaT and infinities become `null`; numpy and pandas numbers and booleans become JSON numbers and booleans; `datetime`, `date` and `Timestamp` become ISO 8601 text (`Z` for timezone-aware values, converted to UTC); `Decimal` becomes a string; sets become sorted lists; numpy arrays become lists. Anything else (bytes, custom objects, a list member that is not a dict) fails that unit with `OUTPUT_INVALID` and names the JSON path of the first bad value, for example `$[3].price`. A row larger than `maxRowBytes` fails the same way. The key `_status` is reserved for status rows, so a returned row that has it is refused.

### Packages

Preinstalled in the code's own environment (Python 3.12.13):

pandas 3.0.6, numpy 2.5.3, openpyxl 3.1.5, xlrd 2.0.2, python-dateutil 2.9.0.post0, tzdata 2026.5, requests 2.34.2, httpx 0.28.1, beautifulsoup4 4.15.0, lxml 6.1.3, pypdf 6.19.0, python-docx 1.2.0, Pillow 12.3.0, PyYAML 6.0.3, Jinja2 3.1.6, rapidfuzz 3.14.6.

`requirements` takes the `requirements.txt` lines you know: a name, optional extras, an optional version and an optional environment marker, such as `tabulate==0.9.0`, `polars>=1,<2` or `requests[socks]`. Blank lines and `#` comments are fine. A line that an installed version already satisfies is skipped and reported in `RUN_SUMMARY.packages.skipped`. Asking for another version of a preinstalled package (for example `pandas==2.2.3`) replaces it for that run only; the Actor's own runtime is separate and cannot break.

Refused before anything is installed, with the line number: options (`-r`, `-c`, `-e`, `--index-url`, `--extra-index-url`, `--find-links`, `--hash`), direct references (`name @ url`), URLs, `git+` and other VCS links, local paths and archive files, and wildcards in a name. Packages come from PyPI only, and only as wheels for Linux x86_64 and CPython 3.12: a source-only package ends `PACKAGE_INSTALL_FAILED` instead of compiling code at install time. Pick packages you trust; a misspelled name can be someone else's package.

### Secrets

`secrets` is an object of environment variables, for example `{"API_KEY": "...", "SHEET_ID": "..."}`. Keys use `A-Z`, `0-9` and `_`, start with a letter and cannot start with `APIFY_`, `ACTOR_`, `PYTHON`, `LD_`, `UV_` or `PIP_` or be `PATH`, `HOME`, `TZ`, `LANG` or `LC_ALL`. Values up to 8 KB. Before anything reaches the run log, a status row or `RUN_SUMMARY`, every secret value of 6 or more characters, and its URL-encoded and base64 forms, is replaced with `[REDACTED:<KEY>]`. Shorter values are left as they are. Rows and files your code returns are your data and are stored exactly as returned, so do not return a secret.

### Output rows and status rows

Your rows go to the default Dataset as returned. Status rows carry `_status` and sit in the same Dataset:

```json
{
  "_status": "CODE_ERROR",
  "_unit": "item",
  "_input_index": 2,
  "_error_type": "KeyError",
  "_error": "'price'",
  "_error_line": 4,
  "_stderr_tail": "Traceback (most recent call last): ...",
  "_rows_returned": 0,
  "_duration_ms": 3,
  "_finished_at": "2026-10-20T12:00:00Z",
  "_schema_version": 1
}
```

With `statusRows: failures` (the default) a status row is written for every failed unit, and one row when the run wrote no rows of its own, so a run never ends with an empty Dataset and no explanation. That includes FAILED runs: rejected input writes one `FAILED` row whose `_error_type` names the reason, such as `INVALID_INPUT`. `all` adds a row for every unit, `none` writes none. `_unit` is `call` for the single `allItems` call and `item` for one input row; `_input_index` is the row's position, and for Dataset input its absolute offset. The `results` view shows everything; the `status` view shows only the status columns. Apify views select columns and do not filter rows, so filter on `_status` in the next step when you need only one kind.

Statuses: `SUCCESS`, `NO_ROWS`, `CODE_ERROR`, `OUTPUT_INVALID`, `TIMEOUT`, `MEMORY_LIMIT`, `NOT_RUN` (an `eachItem` row that was never reached because the run stopped), plus the run-level `PACKAGE_INSTALL_FAILED`, `BUDGET_EXCEEDED`, `INTERRUPTED`, `INPUT_UNAVAILABLE` and `FAILED`.

### Files

`ctx.save_file("report.xlsx", data, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")` stores `bytes` or `str` in the run's Key-Value Store as `files-report.xlsx`. Up to 10 files and 50 MiB per run; names use letters, digits, `.`, `_` and `-`. Over a limit, `save_file` raises `ValueError` inside your code so you can see why. `RUN_SUMMARY.files` lists each key, size, SHA-256, content type and record URL.

### RUN_SUMMARY

Read it first. It holds the run `status`, `callMode`, the input source and counts, Dataset paging (`datasetItemsTotal`, `nextDatasetOffset`), counts per item status, rows written and dropped by the cap, `packages` (requested, skipped, installed with versions, download bytes), install and execution seconds, billed GB-seconds, charged events, `billingStatus`, `codeSha256` and `codeBytes` (never the code), the first error with type, line and redacted message, `failedInputIndexes` (up to 1,000) to resend, and `nextAction`.

For a `PARTIAL` run, send only the rows whose indexes are in `failedInputIndexes` after fixing the code. When `nextDatasetOffset` is set, run again with that `datasetOffset` for the rest.

### Pricing

Platform usage is included in the prices: you pay only these events.

| Event | Price | When |
| --- | --- | --- |
| Actor start | $0.0003 per GB of memory | every run |
| `code-run` | $0.004 | once per run in which your code started and finished, including a code error, a timeout or a memory or output limit; includes the first 30 GB-seconds |
| `extra-gb-minute` | $0.01 | each started GB-minute of install plus execution time beyond the included 30 GB-seconds |
| `packages-installed` | $0.004 | once per run that installed at least one package that was not preinstalled |
| `result-rows-100` | $0.0015 | each started 100 returned rows beyond the first 100 of a run |

Not charged: invalid or unreadable input, a failed install, a run that `maxTotalChargeUsd` stopped before the code started, an interrupted run, status rows, saved files and rows above `maxOutputRows`. A code error is charged like a success because the compute was used.

| Example | Bill |
| --- | --- |
| The prefill (three rows, pandas, 1 GB) | about $0.0043 |
| An n8n workflow calling it 30 times a day with 500 rows back and a 5 s pandas step | about $0.31 a day ($0.0103 a call) |
| A daily 3-minute job at 2 GB (360 GB-seconds) | about $0.065 |
| 20,000 returned rows in one run | adds 199 x $0.0015 = $0.30 |

`maxTotalChargeUsd` is respected before the code starts: the Actor computes the largest bill the run could reach, shrinks the code timeout and then the row cap to fit, and says so in `RUN_SUMMARY.notes`. If even one `code-run` does not fit, the run ends `BUDGET_EXCEEDED` without running anything.

### Failure semantics

A problem in your code is an outcome, not a failed run: the run ends SUCCEEDED with status rows and a `RUN_SUMMARY` that names the error. The run itself fails only when the Actor cannot do its job: the input is invalid (including a refused `requirements` line or a Dataset id that sits only inside an integration payload), the input Dataset cannot be read (`INPUT_UNAVAILABLE`, nothing charged, never treated as an empty input), storage cannot be written, a charge did not finish cleanly, or the image is broken. An empty Dataset is not a failure: `allItems` calls `main([], ctx)` and `eachItem` ends `NO_ROWS` without a charge.

`failRunOnError: true` makes the run end FAILED after the rows and `RUN_SUMMARY` are stored whenever the status is not `SUCCESS` or `NO_ROWS`, which is what an n8n HTTP Request node or a Make module needs to stop the scenario.

If the platform restarts a run while your code is running, the code is not run again (it may already have sent an email or written to an API): the run ends `INTERRUPTED`, rows already written stay, and nothing more is charged. Start a new run for the rest. User code is never retried and a failed install is never retried.

### Security model and its limits

Your code runs as a separate unprivileged user in its own process with a fresh environment: the run's Apify token and proxy password are not in that environment and the process cannot read the Actor's memory or environment. Limits on memory (run memory minus 384 MiB), processes (128), open files (512), file size (1 GiB) and CPU time hold, and at the timeout every process the code started is stopped, including ones that detached. The Actor's own runtime and the runner are read-only to your code. Packages go into the code's own environment, never into the Actor's.

This is not a virtual machine. The code and the installer have outbound network access, because calling APIs is a core use; nothing isolates that traffic. You are responsible for the code you run, the services it contacts and the packages you install. Do not run code you have not reviewed. Your rows are never logged and never sent anywhere except this run's Dataset; your `print` output is logged when `logUserOutput` is on, redacted and capped.

### Moving a Pipedream Python step

| Pipedream | Here |
| --- | --- |
| `def handler(pd: "pipedream")` | `def main(rows, ctx)` |
| `pd.steps["trigger"]["event"]` and earlier step exports | `rows` (the items you send) and `ctx.input` (fixed parameters) |
| `return value` / `pd.export` | the value `main` returns becomes the rows |
| environment variables | `secrets` |
| packages imported at the top | preinstalled, or one line each in `requirements` |

### API, Make, integrations and MCP

- **API:** `POST https://api.apify.com/v2/acts/automa-flow~python-code-runner/run-sync-get-dataset-items` returns the rows directly (up to 300 s); `POST .../runs` starts an asynchronous run. Send the token in the `Authorization: Bearer` header.
- **Make:** an HTTP "Make a request" module with the same URL, method and JSON body, and the token in a header.
- **After a scraper:** on the scraper's Integrations tab, connect this Actor (or a saved Task of it) and set `datasetId` to `{{resource.defaultDatasetId}}`, so your code runs over each new Dataset. A dataset id that sits only inside the integration's `payload` is not read; the run fails and says so.
- **Schedules:** save a Task with your code and `input` parameters (for example a date window) and put it on a schedule.

Agents can find the Actor through Apify MCP search; running it uses your Apify account, and `timeoutSeconds`, `maxOutputRows` and `maxTotalChargeUsd` bound each call. `RUN_SUMMARY` gives an agent the outcome without reading every row. For example, ask an agent: "Use automa-flow/python-code-runner to group these 200 order rows by customer with pandas, return the totals and stop at $0.05." It sends the rows and the code, then reads the status and the grouped rows back.

Direct MCP endpoint:

```
https://mcp.apify.com?tools=automa-flow/python-code-runner
```

If a run fails or looks wrong, open an Issue and include the run URL. Apify shares that run with us automatically, so there is no need to paste your code, data or keys.

### Technical details

- Python 3.12.13 in `apify/actor-python:3.12`; packages installed with `uv` from `https://pypi.org/simple`, wheels only, no user configuration files read.
- The code runs from a fresh `user_main.py`, so tracebacks point at your own line numbers.
- Inline rows are spooled to a file the code cannot read, then streamed to the code process; Dataset rows are read page by page, empty rows keep their place, and the read stops at `maxInputBytes` with a next offset.

# Changelog

This Actor's version history is a separate document: https://apify.com/automa-flow/python-code-runner/changelog.md

# Actor input Schema

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

Your Python 3.12 source. It must define main, sync or async: main(rows, ctx) in allItems mode, main(row, ctx) in eachItem mode. main may take fewer parameters. Return a dict, a list of dicts, a pandas DataFrame or None. Keep API keys out of the code: put them in Secrets.

## `callMode` (type: `string`):

allItems calls main(rows, ctx) once with all input rows (Run Once for All Items). eachItem calls main(row, ctx) once per row, and one failing row does not stop the others (Run Once for Each Item).

## `rows` (type: `array`):

Up to 10,000 JSON objects passed to main, for example the items of the previous n8n or Make step. Use rows or a Dataset, not both. The sample rows are dropped when a Dataset is picked.

## `datasetId` (type: `string`):

Apify Dataset whose rows are passed to main instead of rows. The run gets read access to this Dataset only. In an integration, set it to {{resource.defaultDatasetId}}.

## `datasetOffset` (type: `integer`):

First Dataset row to read (0-based). RUN_SUMMARY gives nextDatasetOffset when more rows remain.

## `maxRows` (type: `integer`):

How many Dataset rows to read in this run (1-100,000). Default 10,000.

## `fields` (type: `array`):

Optional top-level field names to read from each Dataset row (up to 50). Leave empty for whole rows; fewer fields use less memory.

## `input` (type: `object`):

Any JSON object your code reads as ctx.input, such as a threshold or a date. With no rows and no Dataset, main(\[], ctx) runs as a script.

## `requirements` (type: `string`):

Extra PyPI packages, one per line as in requirements.txt: a name with optional extras, version and marker, such as tabulate==0.9.0. Up to 30 lines. Wheels from PyPI only: options (-r, --index-url), URLs, git links and local paths are refused. pandas, numpy, openpyxl and more are already installed.

## `secrets` (type: `object`):

Up to 20 values your code reads with os.environ, for example {"API_KEY": "..."}. Stored encrypted and removed from logs, status rows and RUN_SUMMARY. Keys use A-Z, 0-9 and \_ and cannot start with APIFY\_, ACTOR\_, PYTHON, LD\_, UV\_ or PIP\_.

## `timeoutSeconds` (type: `integer`):

Wall time for starting Python and running main (all rows in eachItem mode), 1-900. A synchronous API call waits at most 300 s, so start longer jobs asynchronously.

## `installTimeoutSeconds` (type: `integer`):

Wall time for installing the requirements, 10-300.

## `maxOutputRows` (type: `integer`):

Rows written to the Dataset at most (1-100,000). Rows above it are dropped and counted, and the run ends OUTPUT_LIMIT.

## `maxRowBytes` (type: `integer`):

Largest returned row as JSON, 1,024 to 4,194,304 bytes. A larger row fails its unit with OUTPUT_INVALID.

## `maxLogBytes` (type: `integer`):

How much of your code's print output (stdout and stderr) is logged, 0 to 1,048,576 bytes. Secret values are redacted.

## `logUserOutput` (type: `boolean`):

Write your code's print output to the run log (redacted and capped).

## `statusRows` (type: `string`):

failures writes a \_status row only for a failed or empty unit, all writes one per unit, none writes none (RUN_SUMMARY still has every count).

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

When on, the run ends FAILED after storing rows and RUN_SUMMARY if the status is not SUCCESS or NO_ROWS, so an n8n or Make step stops on a code error.

## `maxInputBytes` (type: `integer`):

Largest input passed to main, as JSON lines, 1 MiB to 256 MiB. A Dataset read stops at this size and RUN_SUMMARY gives the next offset.

## `resource` (type: `object`):

Present on some webhook bodies. A dataset id here is not read: set datasetId to {{resource.defaultDatasetId}}.

## `payload` (type: `object`):

Added by Actor-to-Actor integrations. A nested dataset id is not read: set datasetId to {{resource.defaultDatasetId}}.

## Actor input object example

```json
{
  "code": "import pandas as pd\n\n\ndef main(rows, ctx):\n    df = pd.DataFrame(rows)\n    df[\"total\"] = df[\"price\"] * df[\"qty\"]\n    return df.sort_values(\"total\", ascending=False)\n",
  "callMode": "allItems",
  "rows": [
    {
      "sku": "A-1",
      "price": 12.5,
      "qty": 4
    },
    {
      "sku": "B-2",
      "price": 3.2,
      "qty": 20
    },
    {
      "sku": "C-3",
      "price": 99,
      "qty": 1
    }
  ],
  "datasetOffset": 0,
  "maxRows": 10000,
  "input": {},
  "timeoutSeconds": 60,
  "installTimeoutSeconds": 120,
  "maxOutputRows": 10000,
  "maxRowBytes": 1048576,
  "maxLogBytes": 262144,
  "logUserOutput": true,
  "statusRows": "failures",
  "failRunOnError": false,
  "maxInputBytes": 67108864
}
```

# Actor output Schema

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

No description

## `runSummary` (type: `string`):

No description

## `billingReceipt` (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": `import pandas as pd


def main(rows, ctx):
    df = pd.DataFrame(rows)
    df["total"] = df["price"] * df["qty"]
    return df.sort_values("total", ascending=False)`,
    "callMode": "allItems",
    "rows": [
        {
            "sku": "A-1",
            "price": 12.5,
            "qty": 4
        },
        {
            "sku": "B-2",
            "price": 3.2,
            "qty": 20
        },
        {
            "sku": "C-3",
            "price": 99,
            "qty": 1
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automa-flow/python-code-runner").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": """import pandas as pd


def main(rows, ctx):
    df = pd.DataFrame(rows)
    df[\"total\"] = df[\"price\"] * df[\"qty\"]
    return df.sort_values(\"total\", ascending=False)
""",
    "callMode": "allItems",
    "rows": [
        {
            "sku": "A-1",
            "price": 12.5,
            "qty": 4,
        },
        {
            "sku": "B-2",
            "price": 3.2,
            "qty": 20,
        },
        {
            "sku": "C-3",
            "price": 99,
            "qty": 1,
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("automa-flow/python-code-runner").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": "import pandas as pd\\n\\n\\ndef main(rows, ctx):\\n    df = pd.DataFrame(rows)\\n    df[\\"total\\"] = df[\\"price\\"] * df[\\"qty\\"]\\n    return df.sort_values(\\"total\\", ascending=False)\\n",
  "callMode": "allItems",
  "rows": [
    {
      "sku": "A-1",
      "price": 12.5,
      "qty": 4
    },
    {
      "sku": "B-2",
      "price": 3.2,
      "qty": 20
    },
    {
      "sku": "C-3",
      "price": 99,
      "qty": 1
    }
  ]
}' |
apify call automa-flow/python-code-runner --silent --output-dataset

```

## MCP server setup

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

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/R2iCRuRSWKZcy5MXQ/builds/yAwsXkRKFtRfSzADw/openapi.json
