# How to read a sandbox's CPU and memory metrics Call `sbx.metrics({ range: "1h" })`. `latest` is the newest reading, and `points` is the series. The sandbox's host takes both measurements. **On Runtime the CPU metric is the same reading the bill is made from.** The host measures each running sandbox's CPU time at least every half minute, and Runtime charges $0.025 per active vCPU-hour of that measured CPU, with a floor of 50 millicores ([pricing](/docs/pricing)). So the chart that shows a sandbox idling also shows why it costs little: a 2 vCPU, 4 GiB sandbox that waits costs $0.03125 an hour, and $0.08 with both CPUs busy. Metrics, events and export cost nothing extra ([metrics and webhooks](/docs/observability)). ## Read them ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create(); await sbx.exec("python3 -c 'sum(i * i for i in range(10**8))'", { timeoutMs: 120_000 }); const m = await sbx.metrics({ range: "15m" }); console.log(m.latest?.cpuPercent, m.latest?.memoryBytes); for (const p of m.points) console.log(p.at, p.cpuCores, p.cpuPeakPercent, p.memoryPeakBytes); ``` ```python check from withruntime import Sandbox with Sandbox.create() as sbx: sbx.exec("python3 -c 'sum(i * i for i in range(10**8))'", timeout_ms=120_000) m = sbx.metrics(range="15m") latest = m["latest"] if latest: print(latest["cpuPercent"], latest["memoryBytes"]) for point in m["points"]: print(point["at"], point["cpuCores"], point["memoryPeakBytes"]) ``` ```bash check npx withruntime sandboxes metrics --range 6h ``` The same readings appear as charts on the Metrics tab of a sandbox's page in your account, under [Sandboxes](https://withruntime.com/account/sandboxes). The sandbox list also shows each sandbox's current CPU and memory. An agent reads them with the `runtime_sandboxes_metrics` MCP tool. ## What each field means | Field | Meaning | | ----------------- | ---------------------------------------------------------------------- | | `cpuPercent` | CPU in use as a percent of all the sandbox's vCPUs, 0 to 100 | | `cpuCores` | The same, as a number of cores | | `cpuPeakPercent` | The busiest interval between two readings inside the bucket | | `memoryBytes` | Memory the sandbox's machine holds, as its host measures it | | `memoryPeakBytes` | The most it held inside the bucket | | `diskUsedBytes` | Bytes used on the root file system, as `df /` in the sandbox reports | | `latest` | The newest reading while the sandbox runs; null when it is not running | Each point also has the disk's reads and writes (`diskReadBytesPerSecond`, `diskWriteBytesPerSecond`, `diskReadOpsPerSecond`, `diskWriteOpsPerSecond`) and the network's traffic (`networkReceivedBytesPerSecond`, `networkSentBytesPerSecond`). Each is null where it was not measured ([all the fields](/docs/observability#metrics)). `range` sets the window. Each point sums one bucket: | `range` | Bucket | Kept for | | ----------- | ---------- | -------- | | `15m` | 10 seconds | 24 hours | | `1h` | 20 seconds | 24 hours | | `6h` | 2 minutes | 24 hours | | `24h` | 8 minutes | 24 hours | | `7d`, `30d` | 1 hour | 30 days | ## Size a sandbox from its readings You choose a sandbox's size with `vcpu` and `memoryMiB`. The readings show whether you chose well: - **Memory.** Memory is billed on the amount you reserve, at $0.0075 per GiB-hour. If the largest `memoryPeakBytes` over a week of runs is 1.2 GiB, a 2 GiB sandbox can run the same work as a 4 GiB one, at half the memory charge. The response also includes `memoryLimitBytes` for comparison. - **CPU.** CPU is billed on use, so extra vCPUs cost nothing while idle. A `cpuPeakPercent` near 100 during builds or tests means that more `vcpu` would finish the work sooner. The bill follows the work done. ```ts check import { Sandbox } from "withruntime"; const sbx = await Sandbox.connect(""); const week = await sbx.metrics({ range: "7d" }); const peak = Math.max(0, ...week.points.map((p) => p.memoryPeakBytes)); console.log( `peak ${(peak / 2 ** 30).toFixed(2)} GiB of ${(week.memoryLimitBytes / 2 ** 30).toFixed(0)} GiB`, ); ``` To see what you were charged, per resource, `runtime usage` and `GET /v1/usage` both return the settled figures. ## Send them to your own dashboards An OpenTelemetry export sends every sandbox's readings as OTLP gauges, about every 30 seconds, to Grafana, Honeycomb, Datadog, New Relic, or any endpoint that accepts OTLP over HTTP: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.otel.create({ endpoint: "https://api.honeycomb.io", headers: { "x-honeycomb-team": process.env.HONEYCOMB_KEY ?? "" }, signals: ["metrics"], }); ``` The gauges are `runtime.sandbox.cpu.utilization`, `runtime.sandbox.cpu.usage`, `runtime.sandbox.memory.usage`, `runtime.sandbox.memory.limit`, `runtime.sandbox.disk.usage` and `runtime.sandbox.disk.limit`. The disk's and the network's traffic are the sums `runtime.sandbox.disk.io`, `runtime.sandbox.disk.operations` and `runtime.sandbox.network.io`. Each sandbox is one resource, with its id, name, vCPUs and region. Add `"traces"` to `signals` for a span of every API call. An account can have three exports ([OpenTelemetry export](/docs/observability#opentelemetry-export)). ## Mistakes and how Runtime handles them - **Reading `cpuPercent` as per core.** It is a share of all the sandbox's vCPUs. On a 2 vCPU sandbox, 50 means one core busy; `cpuCores` says 1. - **Expecting readings from a paused sandbox.** A paused or stopped sandbox has no new readings, and its earlier ones stay. `latest` is null while it is not running. - **Looking for last month's detail.** Each reading is kept 24 hours, and hourly averages and peaks 30 days. Export them to keep more. - **Code written for E2B.** Its `getMetrics()` works through `withruntime/e2b` and reads these same values, `diskUsed` included. ## Related - [Receive webhooks](/how-to/receive-webhooks) to hear when a sandbox stops. - [Pause and resume a sandbox](/how-to/pause-and-resume-a-sandbox) when the readings show it waiting. - [The sandbox cost calculator](/calculators/sandbox-cost-calculator). ## Start ```bash no-run printf '%s\n' 'print(sum([125, 250, 375]))' > invoice.py npx withruntime sandboxes run --keep -- python3 - < invoice.py ``` A new account gets 100 free hours of a 1 GB machine in its first 30 days, with no card. Then run `runtime sandboxes metrics ` on the kept sandbox. Facts on this page were checked on 26 October 2026.