On Runtime, a conversation's interpreter costs $0.03125 per hour while it waits for the next message. Runtime bills the CPU the code uses, not the CPUs the sandbox holds. So a chat that runs a cell every few minutes pays for the seconds of work and the memory.
The short answer
Use one sandbox per conversation, and find it again by name on every turn. The interpreter keeps variables between cells. The sandbox pauses itself when the chat goes quiet.
TypeScriptimport { Sandbox } from "withruntime";export async function runCell(conversationId: string, code: string) { const sbx = await Sandbox.getOrCreate(`chat-${conversationId}`, { vcpu: 1, memoryMiB: 2048, idlePauseSeconds: 600, // pause after ten quiet minutes network: { internet: false }, }); const cell = await sbx.interpreter.run(code, { timeoutMs: 60_000 }); return { status: cell.status, // "ok", "error", "timeout" ... stdout: cell.stdout, error: cell.error?.value, results: cell.results.map((result) => result.data), // text/plain, image/png, tables };}Pythonfrom withruntime import Sandboxdef run_cell(conversation_id: str, code: str) -> dict: sbx = Sandbox.get_or_create( f"chat-{conversation_id}", vcpu=1, memory_mib=2048, idle_pause_seconds=600, # pause after ten quiet minutes network={"internet": False}, ) cell = sbx.interpreter.run(code, timeout_ms=60_000) return { "status": cell["status"], "stdout": cell["stdout"], "error": (cell["error"] or {}).get("value"), "results": [result["data"] for result in cell["results"]], }Expose runCell to the model as a tool, for example run_python(code). Pass
its return value back as the tool result. The first call in a conversation
creates the sandbox. Every later call gets the same sandbox, woken if it paused.
What comes back
Each cell returns its status, its printed output, an error with its traceback if it raised, and a list of results. Each result is a bundle keyed by MIME type:
| You run | You get back |
|---|---|
A pandas data frame such as df |
A table (application/vnd.runtime.table+json) |
| A matplotlib chart | A PNG image (base64 in image/png) |
print(...) |
stdout |
| An exception | status: "error" and error with name, value, traceback |
| A cell that runs too long | status: "timeout" |
display of a file (.csv, .html) |
The file as a result |
Render the PNG in your chat UI as an image, the table as a table, and send the text to the model so it can read its own output.
Let users upload files
A user's CSV goes into the sandbox as a file. The next cell reads it:
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.getOrCreate("chat-42", { idlePauseSeconds: 600 });await sbx.files.write("/workspace/sales.csv", "month,revenue\nJan,120\nFeb,135\n");const cell = await sbx.interpreter.run( "import pandas as pd\ndf = pd.read_csv('sales.csv')\ndf.describe()",);console.log(cell.results[0]?.data["text/plain"]);Pythonfrom withruntime import Sandboxsbx = Sandbox.get_or_create("chat-42", idle_pause_seconds=600)sbx.files.write("/workspace/sales.csv", "month,revenue\nJan,120\nFeb,135\n")cell = sbx.interpreter.run("import pandas as pd\ndf = pd.read_csv('sales.csv')\ndf.describe()")print(cell["results"][0]["data"]["text/plain"])/workspace is the working directory, so relative paths resolve there. pandas,
NumPy and matplotlib are in the default image (the sandbox
environment). Large files upload in checked chunks.
There is no size limit beyond the disk.
What a chatbot interpreter needs
| Need | How Runtime covers it |
|---|---|
| State between turns | Variables persist between cells; a paused sandbox keeps its memory and processes |
| Several languages | Python, JavaScript, TypeScript, R, Java, Bash and Go |
| Charts and tables | matplotlib and R plots as PNG; pandas and R data frames as tables |
| One user cannot reach another | A Firecracker microVM with its own kernel for every conversation |
| Code that sends data out | network: { internet: false }, enforced on the host, root included |
| An infinite loop | timeoutMs ends the cell and reports timeout |
| Quiet conversations | idlePauseSeconds pauses; the next call wakes it |
| A runaway bill | maxTotalCostMicros per sandbox and a daily spending limit per key |
| Many chats at the same time | 100 sandboxes at the same time on a paid account, four without credit |
R, Java and Go install the first time a sandbox uses them. That takes about 35
to 90 seconds, once. To avoid that wait, bake them into a
custom image with apt, so every conversation starts with them.
When the model needs a package
Keep the internet off by default. To let a cell install from PyPI, allow only the registry. Then close it again:
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.getOrCreate("chat-42", { network: { internet: false } });await sbx.network.set({ internet: true, allow: ["pypi.org", "*.pythonhosted.org"] });await sbx.exec("pip install --quiet tabulate", { check: true, timeoutMs: 120_000 });await sbx.network.set({ internet: false });A rule applies immediately, including to connections that are already open (turn off sandbox internet).
What it costs
A conversation's sandbox of 1 vCPU and 2 GiB is billed while it runs. Measured CPU costs $0.025 per vCPU-hour, with a floor of a twentieth of a vCPU. Memory costs $0.0075 per GiB-hour (pricing). Assume 10,000 conversations a month. Each keeps its sandbox running for 5 minutes and uses 20 CPU-seconds of work:
- CPU
- 10,000 × 20 s / 3,600 × $0.025
- $1.39
- Memory
- 10,000 × 300 s / 3,600 × 2 GiB × $0.0075
- $12.50
- Total
- $13.89
That is about $0.0014 a conversation. The usage, 20 CPU-seconds over 300 seconds, is above the floor. So the floor adds nothing. Stop the sandbox when the conversation closes. A sandbox that you keep paused instead is billed as paused storage at $0.08 per GB per 30-day month. Every account gets 100 free hours of a 1 GB machine in its first 30 days, with no card.
Start
Terminalprintf '%s\n' 'print(sum([125, 250, 375]))' > invoice.pynpx withruntime sandboxes run -- python3 - < invoice.pyThe first run prints a link. Open it in your browser to approve the run. You do not need an API key to copy. Then use the JavaScript or Python SDK as shown above.
Related: what a code interpreter is, run untrusted LLM code safely, a data analysis agent, pause and resume a sandbox.
Facts on this page were checked on 26 October 2026.