# 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](/docs/pricing#paused-storage), checked 25 September 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 gives you 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 and `cp` with `:/path` as the source copies one out: ```bash id=$(runtime sandbox create) mkdir -p project && echo "print('hi')" > project/main.py runtime sandbox cp ./project "${id}:/workspace/project" runtime sandbox cat "${id}" /workspace/project/main.py runtime sandbox cp "${id}:/workspace/project" ./project-copy runtime 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: ```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 sandbox cat` / `cp` | A terminal or a CI step | `:/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](/docs/javascript#volumes), which outlives sandboxes and is backed up off its server every day ([volume backups](/docs/storage#volume-backups)). - **Reading a huge 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)` answers `true` or `false`, and `stat` gives 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 25 September 2026.