# How to download files from a sandbox Call `sbx.files.readText(path)` or `read(path)` to get one file. Call `sbx.files.download(dir, localDir)` to get 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. Keeping a sandbox paused costs $0.08 per decimal GB of saved state per month. Leaving a 2 vCPU, 4 GiB sandbox running idle costs $0.03125 an hour ([pricing](/docs/pricing#paused-storage), checked 26 October 2026). ## Read one file ```ts import { 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 string const bytes = await sbx.files.read("/workspace/out/report.csv"); // raw bytes console.log(text, bytes.length); console.log(await sbx.files.stat("/workspace/out/report.csv")); ``` ```python from withruntime import Sandbox with 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 returns the bytes unchanged. ## Download a folder ```ts import { 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")); ``` ```python import pathlib import tempfile from withruntime import Sandbox with 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. `cp` copies a file out when you pass `:/path` as the source: ```bash id=$(runtime sandboxes create) mkdir -p project && echo "print('hi')" > project/main.py runtime sandboxes cp ./project "${id}:/workspace/project" runtime sandboxes cat "${id}" /workspace/project/main.py runtime sandboxes cp "${id}:/workspace/project" ./project-copy runtime sandboxes stop "${id}" ``` ## Find what to download first `files.list` walks a directory to a `depth`. `files.glob` matches a pattern. An agent can use them to collect every chart or report it made, without knowing the names: ```ts import { 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 sandboxes cat` / `cp` | A terminal or a CI step | `:/path` names a sandbox path; a name works for the id | | `runtime_sandboxes_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 | | `GET …/files/archive?path=` | A folder, without an SDK | A streamed tar of any size; `gzip=true` compresses it | Reading a file costs nothing on its own. The sandbox is billed for measured CPU at $0.025 per active vCPU-hour, and for 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](/docs/javascript#volumes), which outlives sandboxes and is backed up off its server every day ([volume backups](/docs/storage#volume-backups)). - **Reading a large command output through `exec`.** A plain result holds at most 64 KiB of `stdout`. Redirect the command to a file, and read the file. - **A relative path.** Paths are absolute. `/workspace` is the sandbox user's home. - **Checking for a file by catching an error.** `files.exists(path)` returns `true` or `false`. `stat` returns the type, size, mode and times. ## Related - [Upload a folder](/how-to/upload-a-folder), the other direction. - [Pause and resume a sandbox](/how-to/pause-and-resume-a-sandbox) to keep results without paying for idle compute. - [Data analysis agent](/use-cases/data-analysis-agent): charts and tables made in a sandbox, fetched back. - [Files](/docs/python#files) in the Python reference. Facts on this page were checked on 26 October 2026.