# How to create a read-only API key for monitoring and CI Choose **Read only** when you create a key at withruntime.com/account/keys, or run `runtime keys create --read-only`. **On Runtime a read-only key sees the whole account, every product included, and cannot start, change or spend anything.** It covers products added later without being reissued, and it lasts until revoked. A dashboard or a CI check that holds one cannot start a sandbox or touch the balance, even if the key leaks (checked 25 September 2026, [security](/docs/security#read-only-keys-and-daily-limits)). ## Create one An owner, admin or developer of the account can create a key of either kind. - **In the browser:** at [API keys](https://withruntime.com/account/keys), choose **Read only**. - **From a terminal:** the CLI asks for the key, and a person approves it in the browser after checking the code, the key's name and its access. ```bash no-run runtime keys create --name grafana --read-only runtime keys create --name pr-checks --read-only | gh secret set RUNTIME_READ_KEY ``` The key is printed once, alone on standard output, and Runtime keeps only a hash of it. A billing member, an agent and an existing key cannot approve a key request ([keys for CI](/docs/cli#keys-for-ci)). ## What it can and cannot do | A read-only key can read | It cannot | | ------------------------------------------------------ | --------------------------------------------- | | Every sandbox, its state, spec and cost | Create, start, stop, pause or wake anything | | Images, volumes, snapshots, previews and network rules | Run a command or write a file | | Account notices and the balance | Change a setting or a rule | | Its own access and limit | Spend anything | | The audit log, when an owner or admin made the key | See inside sandboxes: files or command output | | | See job runs or secrets | A request to change something is refused with 403 `forbidden`, raised as `PermissionDeniedError` in both SDKs. ## Use it from code Point the client at the key with `RUNTIME_API_KEY`, as with any key. This script is the kind of thing a read-only key is for: it lists what is running and what each sandbox is set up as, and touches nothing. ```ts import { Runtime } from "withruntime"; const runtime = new Runtime(); // RUNTIME_API_KEY holds the read-only key const page = await runtime.sandboxes.list({ state: ["running"] }); for await (const sbx of page) console.log(sbx.id, sbx.info.name, sbx.state); ``` ```python from withruntime import Runtime runtime = Runtime() # RUNTIME_API_KEY holds the read-only key for sbx in runtime.sandboxes.list(state=["running"]): print(sbx.id, sbx.info["name"], sbx.state) ``` A job that must be sure it holds a read-only key can check before it starts: ```ts check import { PermissionDeniedError, Runtime } from "withruntime"; const runtime = new Runtime(); const { access } = await runtime.limits.get(); if (access !== "read") throw new Error(`expected a read-only key, got ${access}`); try { await runtime.sandboxes.create(); } catch (error) { if (error instanceof PermissionDeniedError) console.log("refused, as it should be"); else throw error; } ``` ```python check from withruntime import PermissionDeniedError, Runtime with Runtime() as runtime: access = runtime.limits.get()["access"] # "full", "read" or "selected" assert access == "read", access try: runtime.sandboxes.create() except PermissionDeniedError: print("refused, as it should be") ``` `runtime limits` prints the same from a terminal, and an agent on MCP calls `runtime_limits_get`. `access` is `read` for a read-only key, `full` for one that reaches every action, and `selected` for a key limited to the actions it names ([API account](/docs/api#account)). ## Where a read-only key fits - **Dashboards and alerting:** sandbox counts, states and costs, account notices such as a paused sandbox about to expire, and the balance. - **CI checks:** a pull-request job that verifies an image tag exists or that no sandbox with a given label is left running. - **Cost reports:** `runtime usage --json` and `GET /v1/usage` give every figure in integer microdollars. - **Audits:** a read-only key made by an owner or admin reads the audit log, with who did what and from where ([teams](/docs/teams#audit-log)). ## Mistakes to avoid - **Giving the monitoring job the deploy key.** A full key in a dashboard can start sandboxes and spend credit if it leaks. Give it its own read-only key. - **Expecting command output.** A read-only key sees a sandbox's state and cost but not what is inside it. A check that needs a file's contents needs a full key, ideally with a [daily spending limit](/how-to/set-a-daily-spending-limit). - **Trying to make keys with a key.** No key can create, list or revoke keys; that is done by a person, in the browser. - **Putting the key in a URL or a command line.** Keep it in a secret store and pass it as `RUNTIME_API_KEY`. Revoke a key from its row at API keys. Every key made, revoked or limited is in the audit log. For keys that can spend within a bound, see [cap a sandbox's cost](/how-to/cap-the-cost-of-a-sandbox); for keeping a provider's key out of a sandbox entirely, see [turn off sandbox internet](/how-to/turn-off-sandbox-internet). Facts on this page were checked on 25 September 2026.