Skip to content
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, then download the result.

On Runtime, 2,000 workbook analyses cost about $1.92 a month on 2 vCPU, 4 GiB sandboxes. This is at the rates in force on 26 October 2026. The sandbox is billed only for the CPU the model's code uses. pandas 2.1.4 and NumPy come in the default image. With network: { internet: false }, the data 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. The model can therefore 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 more than a table. These are the cases that cause agents problems, and how to handle each one 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. Then it never reports the text =SUM(B2:B40) as a figure. Keep the formulas when it writes the workbook back.

Why a sandbox and not your own server

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

  • The code runs in a Firecracker microVM with its own kernel. It does not run in a container that shares your server's kernel.
  • The data stays in. internet: false refuses every outbound connection. 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 as a hung worker.
  • Nothing is left behind. The sandbox is deleted at the end of the block, and with it every copy of the workbook.

Large workbooks and many files

files.write sends large files in parallel 1 MiB chunks. Each chunk is checked by SHA-256, and an upload 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 the bucket 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 A new microVM per file, from a machine started ahead of time for the common shapes
Code that loops forever timeoutMs per cell; timeoutSeconds bounds the sandbox

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. The cells use 30 CPU-seconds:

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

That is about a tenth of a cent per workbook. The sheets image is stored free as one of the first three images an account without credit keeps free. Otherwise it costs $0.08 per GB a month. Runtime charges $0.025 per vCPU-hour of measured CPU and $0.0075 per GiB-hour of memory. It stores images at $0.08 per GB per 30-day month (pricing).

Start

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

The first run opens a browser page to approve the connection. After that, runtime sandboxes 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 26 October 2026.
  • openpyxl tutorial: data_only returns the formula or the value Excel last stored, read 26 October 2026.

Facts on this page were checked on 26 October 2026.

Related.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card