Runtime

How to let an AI agent analyze Excel spreadsheets safely

Upload the workbook to a sandbox with the internet off, edit it with pandas and openpyxl in a stateful interpreter, and download it.

On Runtime 2,000 workbook analyses cost about $1.92 a month on 2 vCPU, 4 GiB sandboxes, at the rates in force on 25 September 2026, because the sandbox is billed for the CPU the model's code uses. pandas 2.1.4 and NumPy come in the default image, a new sandbox answered its first Python command in 351 ms at the median on 24 September 2026 (speed), and with network: { internet: false } the figures in a customer's workbook cannot leave the machine.

The short answer

Build a small image with openpyxl once, then give each workbook a sandbox of its own:

TypeScriptimport { readFile, writeFile } from "node:fs/promises";import { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "sheets", recipe: { pip: ["openpyxl"] } }); // onceawait using sbx = await runtime.sandboxes.create({ image: "sheets", network: { internet: false } });await sbx.files.write("/workspace/in/budget.xlsx", await readFile("budget.xlsx"));// Every sheet at once, as a dict of data frames the model can inspect.await sbx.interpreter.run(  "import pandas as pd\nbook = pd.read_excel('in/budget.xlsx', sheet_name=None)",);const shapes = await sbx.interpreter.run("{name: df.shape for name, df in book.items()}");console.log(shapes.results[0]?.data["text/plain"]);// ... the model's cells run here, each one sees `book` ...await sbx.interpreter.run(  "import os\nos.makedirs('out', exist_ok=True)\n" +    "with pd.ExcelWriter('out/summary.xlsx') as w:\n" +    "    for name, df in book.items():\n" +    "        df.describe().to_excel(w, sheet_name=name[:31])",);await writeFile("summary.xlsx", await sbx.files.read("/workspace/out/summary.xlsx"));
Pythonfrom withruntime import Runtimeruntime = Runtime()runtime.images.build(name="sheets", recipe={"pip": ["openpyxl"]})  # oncewith runtime.sandboxes.create(image="sheets", network={"internet": False}) as sbx:    with open("budget.xlsx", "rb") as file:        sbx.files.write("/workspace/in/budget.xlsx", file.read())    sbx.interpreter.run("import pandas as pd\nbook = pd.read_excel('in/budget.xlsx', sheet_name=None)")    shapes = sbx.interpreter.run("{name: df.shape for name, df in book.items()}")    print(shapes["results"][0]["data"]["text/plain"])    sbx.interpreter.run(        "import os\nos.makedirs('out', exist_ok=True)\n"        "with pd.ExcelWriter('out/summary.xlsx') as w:\n"        "    for name, df in book.items():\n"        "        df.describe().to_excel(w, sheet_name=name[:31])"    )    with open("summary.xlsx", "wb") as file:        file.write(sbx.files.read("/workspace/out/summary.xlsx"))

pandas reads .xlsx files with openpyxl, which is why the image adds it. The interpreter keeps book in memory between cells, so the model can look at the columns, clean them and summarize them without reading the file again. A CSV needs nothing extra: pd.read_csv works in the default image.

Formulas, values and what the model sees

A spreadsheet is not only a table. The things that trip up an agent, and how to handle each in the sandbox:

In the workbook What to do
Several sheets sheet_name=None returns every sheet as a data frame, keyed by name
Header rows above the table header= and skiprows= in read_excel; let the model look at the first rows first
Formulas openpyxl's load_workbook returns the formula text by default
The computed value of a cell load_workbook(path, data_only=True) returns the value Excel stored when it last saved
Merged or styled cells Edit the workbook with openpyxl instead of pandas, so formatting is kept
A chart for the user Plot with matplotlib; the interpreter returns it as a PNG

Tell the model which of the two it is reading, so it never reports the text =SUM(B2:B40) as a figure, and keep the formulas when it writes the workbook back.

Why a sandbox and not your own server

Spreadsheets arrive from customers, and the code that reads them is written by a model. Both are untrusted. In the sandbox:

  • The code runs in a Firecracker microVM with its own kernel, not in a container sharing your server's kernel.
  • The data stays in: internet: false refuses every outbound connection, and root inside the sandbox cannot turn it back on.
  • Each cell has a time limit: pass timeoutMs to interpreter.run; a timeout returns as a result, not a hung worker.
  • Nothing is left behind: the sandbox stops at the end of the block, and with it every copy of the workbook.

Large workbooks and many files

files.write sends big files in parallel 1 MiB chunks, each checked by SHA-256, and resumes after a dropped connection; the only size limit is the disk, which diskMiB raises. A folder of files goes in one call with files.upload(localDir, "/workspace/in"). For workbooks kept in a bucket, mount it read-only as a directory; the sandbox never holds the bucket's key (mount your own bucket).

What a spreadsheet agent needs

Need How Runtime covers it
Read .xlsx and .csv pandas 2.1.4 in the default image; openpyxl in a one-line recipe image
State across the model's steps Interpreter variables persist between cells
Return an edited workbook files.read gives the bytes of any file the code wrote
Charts matplotlib figures come back as PNG results
Customer data kept private Internet off per sandbox, enforced on the host
A fresh machine per file 351 ms median to the first Python result, measured 24 September 2026
Code that loops forever timeoutMs per cell; the sandbox's lease bounds the rest

What it costs

Take 2,000 workbooks a month. Each keeps a 2 vCPU, 4 GiB sandbox running for 90 seconds while the model works through it, and the cells use 30 CPU-seconds:

TextCPU:     2,000 × 30 s / 3,600 × $0.025             = $0.42Memory:  2,000 × 90 s / 3,600 × 4 GiB × $0.0075    = $1.50Total:                                               $1.92

That is a tenth of a cent a workbook. The sheets image is stored free as one of the trial's first three images, and otherwise at $0.08 per GB a month. Runtime charges $0.025 per vCPU-hour of measured CPU and $0.0075 per GiB-hour of memory, and stores images at $0.08 per GB per 30-day month (pricing).

Start

Terminalnpx withruntime sandbox run --trial -- python3 -c 'import pandas; print(pandas.__version__)'

The first run opens a browser page to approve the connection. After that, runtime sandbox run-code <id> clean.py runs a script as an interpreter cell from a terminal (CLI).

Related: data analysis agent, chart generation, SQL analysis agent, turn off sandbox internet.

Sources

  • pandas.read_excel: openpyxl is the engine for .xlsx files, read 25 September 2026.
  • openpyxl tutorial: data_only returns the formula or the value Excel last stored, read 25 September 2026.

Facts on this page were checked on 25 September 2026.