# How to Run Your Daytona SDK Code on Sandboxer

> Keep the official Daytona SDK and set two variables, DAYTONA_API_URL to https://sandboxer.com/daytona/api and DAYTONA_API_KEY to a Sandboxer API key. We ran create, commands, files, sessions, previews, stop, start and delete this way with the Python and TypeScript SDKs on prod. Calls the docs list as not covered raise not_supported.

Source: https://sandboxer.com/guides/run-daytona-sdk-code-on-sandboxer. Updated 2026-10-29.

**Short answer:** Keep the official Daytona SDK and set two variables, DAYTONA_API_URL to https://sandboxer.com/daytona/api and DAYTONA_API_KEY to a Sandboxer API key. We ran create, commands, files, sessions, previews, stop, start and delete this way with the Python and TypeScript SDKs on prod. Calls the docs list as not covered raise not_supported.

This page is a walkthrough you can copy. Every script below ran on prod on 2026-10-29, with `daytona` 0.226.1 for Python (3.12) and `@daytonaio/sdk` 0.226.1 for TypeScript (Node 24). The list of covered and refused calls lives in the [Daytona-compatible API reference](/docs/daytona). For a step-by-step move of a whole project, including images, volumes and secrets, see the [Daytona migration guide](/migrate/daytona).

## Step 1: Install the SDK you already use

Nothing here is Sandboxer-specific. Install Daytona's own packages.

```bash
# Python
pip install daytona

# TypeScript / JavaScript
npm i @daytonaio/sdk
```

## Step 2: Point it at Sandboxer

Create a Sandboxer API key in the dashboard or with `sbx api-key create` ([API keys](/docs/api-keys)). Its `ci` preset covers create, run, files, previews and deleting what the key created. Then set two variables in the shell, CI job or container where your code runs:

```bash
export DAYTONA_API_URL=https://sandboxer.com/daytona/api
export DAYTONA_API_KEY=sbx_live_…   # the Sandboxer key from above
```

`Daytona()` and `new Daytona()` read both variables, so your code needs no constructor arguments. The SDK finds the toolbox URL by itself.

## Step 3: Run a full lifecycle in Python

This script creates a sandbox in the EU region, runs a command, round-trips a file, uses a session, opens a preview link, stops and starts the sandbox, and deletes it. `target="eu"` picks the region; `us` works too, as do `us-east` and `eu-central` ([regions](/docs/regions)). Leave `target` out to get the region nearest you.

```python
import time, urllib.request, urllib.error
from daytona import Daytona, CreateSandboxFromSnapshotParams, SessionExecuteRequest

daytona = Daytona()  # reads DAYTONA_API_URL and DAYTONA_API_KEY

# 1. Create in a chosen region
sandbox = daytona.create(CreateSandboxFromSnapshotParams(
    name="aeo-daytona-sdk-1", target="eu", labels={"purpose": "aeo-daytona-sdk"}))
print("created", sandbox.id, sandbox.state, sandbox.target, f"{sandbox.cpu} vCPU / {sandbox.memory} GB")

try:
    # 2. Run a command
    r = sandbox.process.exec("uname -sr && pwd")
    print("exec:", r.exit_code, r.result.strip().replace("\n", " | "))

    # 3. Upload and download a file
    sandbox.fs.upload_file(b"name,score\nada,91\nlin,87\n", "/home/user/scores.csv")
    print("download:", sandbox.fs.download_file("/home/user/scores.csv").decode().splitlines())

    # 4. A session keeps cwd and exported variables
    sandbox.process.create_session("work")
    sandbox.process.execute_session_command("work", SessionExecuteRequest(command="cd /home/user && export RUN=42"))
    s = sandbox.process.execute_session_command("work", SessionExecuteRequest(command='echo "run $RUN in $(pwd)"'))
    print("session:", s.exit_code, s.output.strip())

    # 5. A preview link for a port
    sandbox.process.exec("nohup python3 -m http.server 8000 --directory /home/user >/dev/null 2>&1 &")
    link = sandbox.get_preview_link(8000)
    print("preview:", link.url, "token length", len(link.token))
    def status(headers):
        req = urllib.request.Request(link.url + "/scores.csv", headers=headers)
        try:
            return urllib.request.urlopen(req).status
        except urllib.error.HTTPError as e:
            return e.code
    for _ in range(10):
        code = status({"x-daytona-preview-token": link.token})
        if code == 200:
            break
        time.sleep(1)
    print("preview with token:", code, "without:", status({}))

    # 6. Stop and start
    t = time.time(); sandbox.stop(); print("stopped", sandbox.state, f"{time.time()-t:.1f}s")
    t = time.time(); sandbox.start(); print("started", sandbox.state, f"{time.time()-t:.1f}s")
    print("after start:", sandbox.process.exec("cat /home/user/scores.csv | wc -l; pgrep -f http.server >/dev/null && echo server still running").result.strip().replace("\n", " | "))
finally:
    sandbox.delete()
    print("deleted", sandbox.id)
```

Output, with the token never printed:

```text
created sb_vvauj4ddgd SandboxState.STARTED eu-central 2 vCPU / 4 GB
exec: 0 Linux 6.18.56-sandboxer | /home/user
download: ['name,score', 'ada,91', 'lin,87']
session: 0 run 42 in /home/user
preview: https://modern-basic-newt-8000.box.sandboxer.com token length 32
preview with token: 200 without: 401
stopped SandboxState.STOPPED 1.1s
started SandboxState.STARTED 1.1s
after start: 3 | server still running
deleted sb_vvauj4ddgd
```

What to read from this run:

- **The id is a Sandboxer id** (`sb_…`). The guest user is `user`, with its home at `/home/user`.
- **The size is `small`** (2 vCPU, 4 GB), because the SDK sent no size. Step 4 shows how to pick one.
- **The preview needs its token.** Send it in the `x-daytona-preview-token` header. Without it the URL answers `401`. A new route can answer `404` for a second or two, which is why the script retries. Treat the token like a password and don't log it.
- **The web server survived stop and start.** A stop saves memory and disk, so `start()` resumes the same processes. For the exceptions and the full concept map, see the [Daytona migration guide](/migrate/daytona).

## Step 4: Pick a size, and see a refused call, in TypeScript

Without a size you get `small`. To get a bigger [Sandboxer size](/docs/machines), add the label `sandboxer.type` (`small`, `default`, `large` or `xlarge`). This script also sets `networkBlockAll`, which the docs list as not covered, to show what a refused call looks like.

```typescript
import { Daytona } from '@daytonaio/sdk'

const daytona = new Daytona() // reads DAYTONA_API_URL and DAYTONA_API_KEY

// A field the docs list as not covered: the call fails before anything is created
try {
  await daytona.create({ name: 'aeo-daytona-sdk-x', networkBlockAll: true })
} catch (e) {
  console.log('refused:', e.constructor.name, e.statusCode, e.code, '-', e.message)
}

const sandbox = await daytona.create({ name: 'aeo-daytona-sdk-3', labels: { 'sandboxer.type': 'default' } })
console.log('created', sandbox.id, sandbox.state, sandbox.target, `${sandbox.cpu} vCPU / ${sandbox.memory} GB`)

const r = await sandbox.process.executeCommand('nproc && python3 --version')
console.log('exec:', r.exitCode, r.result.trim().split('\n').join(' | '))

await sandbox.fs.uploadFile(Buffer.from('console.log(6 * 7)\n'), '/home/user/answer.js')
const run = await sandbox.process.executeCommand('node answer.js', '/home/user')
console.log('node:', run.exitCode, run.result.trim())
const back = await sandbox.fs.downloadFile('/home/user/answer.js')
console.log('download:', back.toString().trim())

const link = await sandbox.getPreviewLink(3000)
console.log('preview:', link.url)
```

Saved as `create.mjs` and run with `node create.mjs`:

```text
refused: DaytonaValidationError 400 not_supported - networkBlockAll isn't supported by Sandboxer yet.
created sb_7jv49bgrbq started us-east 4 vCPU / 8 GB
exec: 0 4 | Python 3.12.3
node: 0 42
download: console.log(6 * 7)
preview: https://ideal-plucky-mesa-3000.box.sandboxer.com
```

The refused create raised the SDK's normal error class, with `code` set to `not_supported` and a message that ends in "isn't supported by Sandboxer yet". No sandbox was created for it. The second create had no `target`, so it went to the region nearest the caller, here `us-east`. The `sandboxer.type` label gave 4 vCPU and 8 GB.

To find every refused call in your own code, run your test suite against Sandboxer and search the failures for `not_supported`.

## Step 5: See the same sandbox with sbx

The Daytona API and the `sbx` CLI share one account. With the TypeScript sandbox still running, `sbx list` showed it next to the account's other sandboxes:

```bash
sbx list
```

```text
ID             STATE  TYPE     IP                          AUTO-STOP  CREATED    NAME
sb_7jv49bgrbq  ready  default  2605:6440:d000:bb::5b00:17  in 15m00s  just now   aeo-daytona-sdk-3
…
```

`AUTO-STOP` shows 15 minutes, the default `autoStopInterval`. Every toolbox call pushes it back. The same `sb_…` id works with every `sbx` command, such as `sbx exec`, `sbx scp` or `sbx info`.

## Step 6: Clean up by id

Delete from the Daytona SDK with `daytona.get(id)`, then confirm with `sbx`:

```typescript
import { Daytona } from '@daytonaio/sdk'

const daytona = new Daytona()
const sandbox = await daytona.get(process.argv[2])
console.log('got', sandbox.id, sandbox.name, sandbox.state)
await sandbox.delete()
console.log('deleted', sandbox.id)
```

```bash
node cleanup.mjs sb_7jv49bgrbq
sbx info sb_7jv49bgrbq
```

```text
got sb_7jv49bgrbq aeo-daytona-sdk-3 started
deleted sb_7jv49bgrbq
{"event":"error","error":"Sandbox not found","code":"not_found","status":404,…}
```

`sbx delete <id> --yes` deletes the same sandbox from the CLI. To sweep up sandboxes a crashed script left behind, see [clean up orphaned sandboxes](/guides/clean-up-orphaned-sandboxes).

## Checklist before you switch a real project

| Your code uses | What to do |
| - | - |
| `create()` with no size | You get `small`. Add the label `sandboxer.type` for more |
| `target` or `DAYTONA_TARGET` | Use `us`, `eu`, `us-east` or `eu-central`, or leave it unset |
| `exec`, `codeRun`, sessions, files, previews, stop, start, delete | Covered. See the [reference](/docs/daytona#whats-covered) |
| A custom image or snapshot, volumes, GPUs, network controls | Raises `not_supported`. See the [reference](/docs/daytona#not-covered-yet) |
| git, LSP, PTY, computer use in the toolbox | Raises `not_supported` |
| `list()` filtered by state, or sorted | Raises `not_supported`. Filter by id, name or labels |
| `autoStopInterval: 0` on the free trial | Refused with `trial_auto_stop_required`. Use 8 hours or less |

## FAQ

### Which Daytona SDK versions did you test?

`daytona` 0.226.1 on Python 3.12 and `@daytonaio/sdk` 0.226.1 on Node 24, on 2026-10-29. The Go, Ruby and Java SDKs call the same API, but we haven't tested them.

### Do I need a Daytona account?

No. `DAYTONA_API_KEY` holds a Sandboxer API key, and usage is billed to your Sandboxer account like any other sandbox.

### What happens when my code calls something that isn't covered?

The SDK raises its usual error. The HTTP status is `400` or `501`, `code` is `not_supported`, and the message ends in "isn't supported by Sandboxer yet". Nothing is silently skipped.

### Can I mix the Daytona SDK and sbx on one sandbox?

Yes. Both use the same `sb_…` id, so you can create with one and run, copy files or delete with the other.

## Related

- [Daytona-compatible API reference](/docs/daytona)
- [Migrate from Daytona to Sandboxer](/migrate/daytona)
- [Regions](/docs/regions)
- [API keys](/docs/api-keys)
- [Pause and resume a sandbox with its memory](/guides/pause-and-resume-a-sandbox-with-memory)
