How to download files from a sandbox
Call sbx.files.readText(path) or read(path) for one file, and sbx.files.download(dir, localDir) for a whole folder.
On Runtime you can take files out of a sandbox even after its work is paused. A paused sandbox keeps its whole disk, and a request for a file wakes it by itself, usually in about half a second. Keeping it paused costs $0.08 per decimal GB of saved state a month, against $0.03125 an hour to leave a 2 vCPU, 4 GiB sandbox running idle (pricing, checked 25 September 2026).
Read one file
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();await sbx.files.write("/workspace/out/report.csv", "name,score\nada,9\n");const text = await sbx.files.readText("/workspace/out/report.csv"); // a stringconst bytes = await sbx.files.read("/workspace/out/report.csv"); // raw bytesconsole.log(text, bytes.length);console.log(await sbx.files.stat("/workspace/out/report.csv"));Pythonfrom withruntime import Sandboxwith Sandbox.create() as sbx: sbx.files.write("/workspace/out/report.csv", "name,score\nada,9\n") text = sbx.files.read_text("/workspace/out/report.csv") # str data = sbx.files.read("/workspace/out/report.csv") # bytes print(text, len(data)) print(sbx.files.stat("/workspace/out/report.csv"))Use read for images, archives and anything else that is not text; it gives
you the bytes unchanged.
Download a folder
TypeScriptimport { mkdtemp } from "node:fs/promises";import { tmpdir } from "node:os";import { join } from "node:path";import { Sandbox } from "withruntime";await using sbx = await Sandbox.create();await sbx.files.write("/workspace/results/summary.txt", "all tests passed\n");await sbx.files.write("/workspace/results/logs/run.log", "ok\n");const local = await mkdtemp(join(tmpdir(), "results-"));await sbx.files.download("/workspace/results", join(local, "results"));Pythonimport pathlibimport tempfilefrom withruntime import Sandboxwith Sandbox.create() as sbx: sbx.files.write("/workspace/results/summary.txt", "all tests passed\n") local = pathlib.Path(tempfile.mkdtemp()) / "results" sbx.files.download("/workspace/results", str(local)) print(sorted(p.name for p in local.rglob("*")))The folder travels as one compressed archive, and each file keeps its
permissions. From a terminal, cat prints a file and cp with <id>:/path
as the source copies one out:
Terminalid=$(runtime sandbox create)mkdir -p project && echo "print('hi')" > project/main.pyruntime sandbox cp ./project "${id}:/workspace/project"runtime sandbox cat "${id}" /workspace/project/main.pyruntime sandbox cp "${id}:/workspace/project" ./project-copyruntime sandbox stop "${id}"Find what to download first
files.list walks a directory to a depth, and files.glob matches a
pattern, so an agent can collect every chart or report it made without knowing
the names:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();await sbx.files.write("/workspace/charts/a.png", "png bytes");for (const entry of await sbx.files.list("/workspace", { depth: 2 })) console.log(entry.type, entry.size, entry.path);console.log(await sbx.files.glob("**/*.png"));Ways out, compared
| Method | Best for | Notes |
|---|---|---|
files.readText(path) |
Logs, JSON, CSV | Decoded as text |
files.read(path) |
Images, archives, binaries | Raw bytes, unchanged |
files.download(dir, local) |
A folder of results | One compressed archive; permissions kept |
runtime sandbox cat / cp |
A terminal or a CI step | <id>:/path names a sandbox path; a name works for the id |
runtime_sandbox_files_read |
An agent over MCP | Whole files, or a range of lines |
GET …/files/content?path= |
Your own HTTP client | The file's raw bytes |
Reading a file costs nothing on its own. The sandbox is billed for measured CPU at $0.025 per active vCPU-hour and memory at $0.0075 per GiB-hour.
Mistakes to avoid
- Stopping before copying out. A stopped sandbox is not a backup. Download what you need first, or keep the files on a volume, which outlives sandboxes and is backed up off its server every day (volume backups).
- Reading a huge command output through
exec. A plain result holds at most 64 KiB ofstdout. Redirect the command to a file and read the file. - A relative path. Paths are absolute;
/workspaceis the sandbox user's home. - Checking for a file by catching an error.
files.exists(path)answerstrueorfalse, andstatgives type, size, mode and times.
Related
- Upload a folder, the other direction.
- Pause and resume a sandbox to keep results without paying for idle compute.
- Data analysis agent: charts and tables made in a sandbox, fetched back.
- Files in the Python reference.
Facts on this page were checked on 25 September 2026.