How to read a sandbox's CPU and memory metrics
Call sbx.metrics({ range: "1h" }): latest is the newest reading and points the series, measured by the sandbox's host.
On Runtime the CPU metric is the same reading the bill is made from. The host measures each running sandbox's CPU time once a minute, and Runtime charges $0.025 per active vCPU-hour of that measured CPU, with a floor of 50 millicores (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).
Read them
TypeScriptimport { 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);Pythonfrom withruntime import Sandboxwith 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"])Terminalnpx withruntime sandbox metrics <id> --range 6hThe same readings are charts on the sandbox's page in your account, under
Sandboxes, and an agent reads
them with the runtime_sandbox_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 |
latest |
The newest reading, or null when there is none in the last 15 minutes |
range chooses the window, and 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
A sandbox's size is vcpu and memoryMiB, and you choose them. The readings
say whether you chose well:
- Memory. Memory is billed on what you reserve, $0.0075 per GiB-hour. If
the largest
memoryPeakBytesover a week of runs is 1.2 GiB, a 2 GiB sandbox does the job that a 4 GiB one did, for half the memory charge. The response also carriesmemoryLimitBytesto compare against. - CPU. CPU is billed on use, so extra vCPUs cost nothing while idle. A
cpuPeakPercentnear 100 during builds or tests means morevcpuwould finish sooner; the bill follows the work done.
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.connect("<sandbox id>");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`,);For what you were actually charged, per resource, runtime usage and
GET /v1/usage give the settled figures.
Send them to your own dashboards
An OpenTelemetry export pushes every sandbox's readings as OTLP gauges, about every 30 seconds, to Grafana, Honeycomb, Datadog, New Relic or any endpoint that takes OTLP over HTTP:
TypeScriptimport { 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 and runtime.sandbox.memory.limit, with one
resource per sandbox carrying its id, name, vCPUs and region. An account can
have three exports (OpenTelemetry export).
Mistakes and how Runtime handles them
- Reading
cpuPercentas per core. It is a share of all the sandbox's vCPUs. On a 2 vCPU sandbox, 50 means one core busy;cpuCoressays 1. - Expecting readings from a paused sandbox. A paused or stopped sandbox
has no new readings, and its earlier ones stay.
latestis null once nothing has been read for 15 minutes. - Looking for last month's detail. Each minute's reading is kept 24 hours, and hourly averages and peaks 30 days. Export them to keep more.
- Code written for E2B. Its
getMetrics()works throughwithruntime/e2band reads these same values;diskUsedis null.
Related
- Receive webhooks to hear when a sandbox stops.
- Pause and resume a sandbox when the readings show it waiting.
- The sandbox cost calculator.
Start
Terminalnpx withruntime sandbox run --trial --keep -- python3 -c 'print(6 * 7)'New accounts get 50 free sandbox hours, no card. Then run
runtime sandbox metrics <id> on the sandbox it kept.
Facts on this page were checked on 25 September 2026.