> ## Documentation Index
> Fetch the complete documentation index at: https://docs.novita.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Claude Code Sandbox: Run Claude Code with Novita

> Learn how to set up a Claude Code sandbox with Novita AI. Run Claude Code headless in an isolated environment on a cloned repo, with Novita models.

This guide shows you how to set up a Claude Code sandbox using Novita Sandbox. You'll configure the environment, launch Claude Code, and run your coding agent in an isolated runtime.

## What you'll build

You will start a sandbox from the `claude-code` template — which ships with Anthropic's Claude Code CLI pre-installed — and run Claude Code in headless mode on a coding task. Claude Code reads and edits code, runs commands, and executes what it writes inside the sandbox, so none of its actions touch your machine. The model can be Anthropic's or any Novita-hosted model behind Novita's Anthropic-compatible API.

```mermaid theme={"system"}
flowchart TB
  App["Your app<br/>(novita-sandbox SDK)"] -- "create sandbox,<br/>claude -p" --> Sandbox
  subgraph Sandbox["Novita Sandbox · claude-code template"]
    Claude["Claude Code CLI"] --> Tools["Files · Shell · Code"]
  end
  Claude -- "Anthropic Messages API" --> Model["Anthropic API<br/>or Novita LLM API"]
```

## Prerequisites

* **Novita API key** — create one in [Key Management](https://novita.ai/settings/key-management) and export it as `NOVITA_API_KEY`.
* **A model key** — an Anthropic key exported as `ANTHROPIC_API_KEY` for the Quickstart, or nothing extra if you [use Novita models](#use-novita-models).
* **Novita Sandbox SDK** — Python 3.10+ or Node.js 20+:

```bash Python icon="python" theme={"system"}
pip install novita-sandbox
```

```bash JavaScript & TypeScript icon="js" theme={"system"}
npm install novita-sandbox
```

* **Claude Code version** — the `claude-code` template comes with the Claude Code CLI pre-installed; nothing to install. Run `claude --version` in a sandbox to see the bundled version.

## Quickstart

Create a sandbox from the `claude-code` template, run one headless task with `claude -p`, stream its output, and kill the sandbox.

```python Python icon="python" theme={"system"}
import os
from novita_sandbox import Novita

sandbox = Novita().sandbox.create(
    "claude-code", timeout=1800, envs={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]}
)
try:
    sandbox.commands.run(
        'claude --dangerously-skip-permissions -p "Write fizzbuzz.py, run it, and show the output"',
        cwd="/home/user",
        on_stdout=lambda d: print(d, end=""),
        on_stderr=lambda d: print(d, end=""),
        timeout=0,
    )
finally:
    sandbox.kill()
```

```js JavaScript & TypeScript icon="js" theme={"system"}
import { Novita } from 'novita-sandbox'

const sandbox = await new Novita().sandbox.create('claude-code', {
  timeoutMs: 1_800_000,
  envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
try {
  await sandbox.commands.run(
    'claude --dangerously-skip-permissions -p "Write fizzbuzz.py, run it, and show the output"',
    {
      cwd: '/home/user',
      onStdout: (d) => process.stdout.write(d),
      onStderr: (d) => process.stderr.write(d),
      timeoutMs: 0,
    }
  )
} finally {
  await sandbox.kill()
}
```

> [!NOTE]
>
> **`-p "<prompt>"`** (`--print`) runs Claude Code in headless mode: it processes the prompt, prints the result, and exits, which makes it scriptable. **`--dangerously-skip-permissions`** skips the approval prompts Claude Code normally shows before editing files or running commands — as the name warns, use it only in an isolated sandbox like this one.

## How it works

Claude Code gets a full Linux environment inside the sandbox. You drive it with the SDK: `files` and `git` to move code in and out, `commands` to run Claude Code and anything else. The snippets below assume a `sandbox` created as in the Quickstart.

### Execute generated code

Claude Code writes code and runs it in the sandbox's shell. Its output streams back through `on_stdout` / `on_stderr`, and the call returns the exit code.

```python Python icon="python" theme={"system"}
result = sandbox.commands.run(
    'claude --dangerously-skip-permissions -p "Create a Flask app in app.py with a /health route and test it"',
    cwd="/home/user",
    on_stdout=lambda d: print(d, end=""),
    timeout=0,
)
print("exit code:", result.exit_code)
```

```js JavaScript & TypeScript icon="js" theme={"system"}
const result = await sandbox.commands.run(
  'claude --dangerously-skip-permissions -p "Create a Flask app in app.py with a /health route and test it"',
  { cwd: '/home/user', onStdout: (d) => process.stdout.write(d), timeoutMs: 0 }
)
console.log('exit code:', result.exitCode)
```

### Read and write files on a cloned repository

The most common workflow: clone a repository with [`sandbox.git.clone`](https://docs.novita.ai/guides/sandbox-git-integration) (with credentials for private repos), let Claude Code change it, then read the result back — here, the diff.

```python Python icon="python" theme={"system"}
sandbox.git.clone(
    "https://github.com/your-org/your-repo.git",
    path="/home/user/repo",
    username="x-access-token",
    password=os.environ["GITHUB_TOKEN"],
    depth=1,
)
sandbox.commands.run(
    'claude --dangerously-skip-permissions -p "Add error handling to all API endpoints"',
    cwd="/home/user/repo",
    timeout=0,
)
print(sandbox.commands.run("git diff", cwd="/home/user/repo").stdout)
```

```js JavaScript & TypeScript icon="js" theme={"system"}
await sandbox.git.clone('https://github.com/your-org/your-repo.git', {
  path: '/home/user/repo',
  username: 'x-access-token',
  password: process.env.GITHUB_TOKEN,
  depth: 1,
})
await sandbox.commands.run('claude --dangerously-skip-permissions -p "Add error handling to all API endpoints"', {
  cwd: '/home/user/repo',
  timeoutMs: 0,
})
console.log((await sandbox.commands.run('git diff', { cwd: '/home/user/repo' })).stdout)
```

To move individual files instead, use `sandbox.files.write` and `sandbox.files.read`; see [Read and write files](https://docs.novita.ai/guides/sandbox-filesystem-read-write.md).

### Run commands yourself

Run any command next to the agent — install dependencies before it starts, or verify its work afterwards without trusting its own report.

```python Python icon="python" theme={"system"}
sandbox.commands.run("pip install -r requirements.txt", cwd="/home/user/repo", timeout=300)
sandbox.commands.run(
    'claude --dangerously-skip-permissions -p "Fix the failing tests"', cwd="/home/user/repo", timeout=0
)

check = sandbox.commands.run("python3 -m pytest -q", cwd="/home/user/repo")
print(check.stdout)
```

```js JavaScript & TypeScript icon="js" theme={"system"}
await sandbox.commands.run('pip install -r requirements.txt', { cwd: '/home/user/repo', timeoutMs: 300_000 })
await sandbox.commands.run('claude --dangerously-skip-permissions -p "Fix the failing tests"', {
  cwd: '/home/user/repo',
  timeoutMs: 0,
})

const check = await sandbox.commands.run('python3 -m pytest -q', { cwd: '/home/user/repo' })
console.log(check.stdout)
```

> [!TIP]
>
> A command that exits non-zero raises `CommandExitException` (Python) or `CommandExitError` (JavaScript). Catch it when a failing test run is an expected outcome.

### Resume a session

Claude Code can continue a previous session, so you can run a multi-step workflow across several calls. Run with `--output-format json` to get a `session_id`, then pass it to `--resume`.

```python Python icon="python" theme={"system"}
import json

initial = sandbox.commands.run(
    'claude --dangerously-skip-permissions --output-format json -p "Analyze the codebase and create a refactoring plan"',
    cwd="/home/user/repo",
    timeout=0,
)
session_id = json.loads(initial.stdout)["session_id"]

sandbox.commands.run(
    f'claude --dangerously-skip-permissions --resume {session_id} -p "Now implement step 1 of the plan"',
    cwd="/home/user/repo",
    on_stdout=lambda d: print(d, end=""),
    timeout=0,
)
```

```js JavaScript & TypeScript icon="js" theme={"system"}
const initial = await sandbox.commands.run(
  'claude --dangerously-skip-permissions --output-format json -p "Analyze the codebase and create a refactoring plan"',
  { cwd: '/home/user/repo', timeoutMs: 0 }
)
const sessionId = JSON.parse(initial.stdout).session_id

await sandbox.commands.run(
  `claude --dangerously-skip-permissions --resume ${sessionId} -p "Now implement step 1 of the plan"`,
  { cwd: '/home/user/repo', onStdout: (d) => process.stdout.write(d), timeoutMs: 0 }
)
```

## Use Novita models

Novita provides an [Anthropic-compatible API](https://docs.novita.ai/guides/llm-anthropic-compatibility.md), so Claude Code can run on Novita-hosted models. Replace the `envs` in the Quickstart with the values below — no other change is needed. Change `ANTHROPIC_MODEL` to switch to any [supported model](https://docs.novita.ai/guides/llm-anthropic-compatibility.md#supported-models). See [Claude Code with Novita](https://docs.novita.ai/guides/claude-code.md) for details.

```python Python icon="python" theme={"system"}
sandbox = Novita().sandbox.create("claude-code", timeout=1800, envs={
    "ANTHROPIC_BASE_URL": "https://api.novita.ai/anthropic",
    "ANTHROPIC_AUTH_TOKEN": os.environ["NOVITA_API_KEY"],
    "ANTHROPIC_MODEL": "moonshotai/kimi-k3.1-pro",
    "ANTHROPIC_SMALL_FAST_MODEL": "deepseek/deepseek-v4.1-flash",
})
```

```js JavaScript & TypeScript icon="js" theme={"system"}
const sandbox = await new Novita().sandbox.create('claude-code', {
  timeoutMs: 1_800_000,
  envs: {
    ANTHROPIC_BASE_URL: 'https://api.novita.ai/anthropic',
    ANTHROPIC_AUTH_TOKEN: process.env.NOVITA_API_KEY,
    ANTHROPIC_MODEL: 'moonshotai/kimi-k3.1-pro',
    ANTHROPIC_SMALL_FAST_MODEL: 'deepseek/deepseek-v4.1-flash',
  },
})
```

To pin the configuration inside the sandbox instead of passing environment variables, write the same keys to the `env` block of `/home/user/.claude/settings.json` before running `claude`. Model usage is billed separately from sandbox runtime, at the model's own per-token rate.

## Production tips

* **Timeouts** — `timeout` (seconds; `timeoutMs` in JavaScript) on `sandbox.create` is the sandbox's maximum lifetime; extend it while Claude Code works with [`set_timeout`](https://docs.novita.ai/guides/sandbox-timeout-handle). Pass `timeout=0` to `commands.run` so a long Claude Code turn isn't cut off at the SDK's default command timeout. Without long-running mode the timeout is capped by your tier (1, 3, or 4 hours); for longer runs, see [Long-running sandboxes](https://docs.novita.ai/guides/sandbox-long-running.md).
* **Pause and resume** — between steps of a multi-step job, [pause](https://docs.novita.ai/guides/sandbox-pause-resume.md) the sandbox instead of killing it. The repo, installed packages and Claude Code sessions survive, so `--resume` still works after you reconnect, and CPU and RAM billing stops while it is paused.
* **Concurrency** — each sandbox is isolated, so you can run one Claude Code task per sandbox in parallel — for example, one per repository or per issue. Concurrent sandboxes are capped by your account tier (5 on Free, 100 on Paid); see [Quota Limits](https://docs.novita.ai/guides/sandbox-quota-limit.md).
* **Cost** — sandboxes are billed per second while running. A 2 vCPU / 2 GiB sandbox costs about \$0.0000260/s, so a 10-minute task is roughly **\$0.016** in sandbox time, plus model tokens. See [Pricing](https://docs.novita.ai/guides/sandbox-pricing.md).
* **Keep secrets out of the sandbox** — pass header-based API keys, such as your model key, as [Secrets](https://docs.novita.ai/guides/sandbox-secret-sandbox-use.md) so code in the sandbox only sees a placeholder. For Git, use a short-lived token scoped to the one repository.

## Troubleshooting

> [accordion] 402: insufficient balance when creating a sandbox
>
> Your account balance can't cover a new sandbox. Top up on the [Billing](https://novita.ai/billing) page or claim the new-user credits described in [Pricing](https://docs.novita.ai/guides/sandbox-pricing.md), then retry.

> [accordion] Claude Code asks you to log in, or returns 401
>
> Claude Code found no usable credentials. With Anthropic, pass `ANTHROPIC_API_KEY` in `envs`. With Novita, pass `ANTHROPIC_AUTH_TOKEN` (your Novita key) together with `ANTHROPIC_BASE_URL`, and don't set `ANTHROPIC_API_KEY` at the same time.

> [accordion] The command hangs or produces no output
>
> Without `-p`, Claude Code opens its interactive UI and waits for input that never comes. Always pass `-p "<prompt>"`, and `--dangerously-skip-permissions` so tool approvals don't block the run.

## Next steps

> [card] [Sandbox SDK reference](https://docs.novita.ai/guides/sandbox-create.md)
>
> Every option for creating and managing sandboxes.

> [card] [Codex Sandbox](https://docs.novita.ai/guides/sandbox-codex.md)
>
> Run OpenAI's coding agent the same way.

> [card] [Sandbox cookbook](https://github.com/novitalabs/sandbox-cookbook)
>
> Runnable Novita Sandbox examples on GitHub.

> [card] [OpenCode and OpenClaw](https://docs.novita.ai/guides/sandbox-opencode.md)
>
> Run open-source agents in a sandbox.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.