# Quickstart

Create a sandbox, run a command in it and shut it down, in TypeScript or
Python. The curl tab covers create, read and kill.

## Before you start

You need an API key. Create one at
[dashboard.impello.ai/keys](https://dashboard.impello.ai/keys). Every key
starts with `imp_`, and the dashboard is the only place you can read it. See
[API keys](/docs/api-keys).

<Callout kind="warning">
A key alone is not enough. Sandboxes are billed against your team's credit,
and a team with no plan gets none. Once the credit is gone, creates are
refused. List and kill keep working. Pick a plan at
[dashboard.impello.ai/billing](https://dashboard.impello.ai/billing) first —
the plans are on [Limits and pricing](/docs/limits-and-pricing), and
[Refused creates](/docs/errors#refused-creates) covers what the refusal looks
like.
</Callout>

Put the key in your environment. Both SDKs read `IMPELLO_API_KEY`.

```bash
export IMPELLO_API_KEY=imp_...
```

Every documented SDK setting uses the `IMPELLO_` prefix. If your code already
exports the older names from another sandbox provider, the SDKs still read
those as a fallback — see [Migrate](/docs/migrate-from-e2b).

## Install

The TypeScript SDK needs Node 20.18.1 or later on the 20 line, or Node 22 or
later. Node 21 is not supported. The Python SDK needs Python 3.10 or later.

<CodeTabs>
<Tab label="TypeScript">

```bash
npm install @impello/sdk
```

</Tab>
<Tab label="Python">

```bash
pip install 'impello>=0.3.0'
```

</Tab>
</CodeTabs>

## Your first sandbox

Create a sandbox from the default `base` template, run a command in it, read
the sandbox's details, then kill it.

<CodeTabs>
<Tab label="TypeScript">

```ts
import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')

const result = await sandbox.commands.run('echo hello')
console.log(result.stdout)

const info = await sandbox.getInfo()
console.log(info.sandboxId, info.state, info.endAt)

await sandbox.kill()
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox

sandbox = Sandbox.create("base")

result = sandbox.commands.run("echo hello")
print(result.stdout)

info = sandbox.get_info()
print(info.sandbox_id, info.state, info.end_at)

sandbox.kill()
```

</Tab>
<Tab label="curl">

```bash
# Create. templateID is the only required field, timeout is in seconds, and
# metadata and envVars each take a map of string to string.
curl -X POST https://api.sandbox.impello.ai/sandboxes \
  -H "X-API-Key: $IMPELLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"templateID": "base", "timeout": 300, "envVars": {"GREETING": "hello"}}'

# The response carries sandboxID, templateID and envdVersion, plus domain when
# one is set. Copy sandboxID out of it.
SANDBOX_ID=...

# Read it back: startedAt, endAt, state, cpuCount, memoryMB.
curl "https://api.sandbox.impello.ai/sandboxes/$SANDBOX_ID" \
  -H "X-API-Key: $IMPELLO_API_KEY"

# Kill it. 204 means it is gone, 404 means there is no such sandbox.
curl -X DELETE "https://api.sandbox.impello.ai/sandboxes/$SANDBOX_ID" \
  -H "X-API-Key: $IMPELLO_API_KEY"
```

</Tab>
</CodeTabs>

Save the TypeScript as `quickstart.mts` and run it with `npx tsx
quickstart.mts`; the `.mts` extension is what lets the top-level `await` run.
Save the Python as `quickstart.py` and run it with `python quickstart.py`.

What each step does:

- `Sandbox.create('base')` starts a sandbox from the `base` template and
  returns a handle to it. The template name is optional and `base` is the
  default. The rest are on [Templates](/docs/templates).
- `commands.run('echo hello')` runs a shell command and waits for it to exit.
  The result carries `stdout`, `stderr` and `exitCode` (`exit_code` in
  Python). See [Run commands](/docs/sandbox/commands).
- `getInfo()` (`get_info()` in Python) returns the sandbox ID, the template,
  the metadata, the state, the start time and the time the sandbox expires.
- `kill()` stops the sandbox and returns `true`. It returns `false` when the
  sandbox no longer exists.

Commands are not part of the HTTP API: the SDK opens a separate connection to
the sandbox itself to run them. The curl tab stops at create, read and kill.

<Callout kind="note">
A sandbox lives for five minutes by default. Set `timeoutMs` (milliseconds) in
TypeScript or `timeout` (seconds) in Python when you create it, or extend it
later; see [Timeouts](/docs/sandbox/timeouts). Over HTTP the `timeout` field is
in seconds and defaults to 15, so send it explicitly.
</Callout>

## Python: `with`, async and a bound client

A Python `Sandbox` is a context manager, and leaving the block calls `kill()`.

```python
from impello import Sandbox

with Sandbox.create("base") as sandbox:
    print(sandbox.commands.run("uname -a").stdout)
```

For asyncio, use `AsyncSandbox`. Every method is awaited, and `async with`
kills the sandbox on the way out.

```python
import asyncio
from impello import AsyncSandbox

async def main():
    async with await AsyncSandbox.create("base") as sandbox:
        result = await sandbox.commands.run("echo hello")
        print(result.stdout)

asyncio.run(main())
```

To bind a key to a client rather than to the environment, use `Impello`. Its
`Sandbox` and `AsyncSandbox` behave like the top-level classes; a per-call
argument beats the client's, which beats the environment.

```python
from impello import Impello

client = Impello(api_key="imp_...")
sandbox = client.Sandbox.create("base")
```

The TypeScript SDK has one always-async `Sandbox` and no client class. Pass
`apiKey` per call instead, as in `Sandbox.create('base', { apiKey: 'imp_...' })`.

## If it fails

<table>
  <thead>
    <tr><th>What went wrong</th><th>TypeScript</th><th>Python</th></tr>
  </thead>
  <tbody>
    <tr><td>No key in the environment and none passed</td><td>`AuthenticationError`</td><td>`AuthenticationException`</td></tr>
    <tr><td>A key the API rejects</td><td>`AuthenticationError`</td><td>`AuthenticationException`</td></tr>
    <tr><td>The key is not a known prefix followed by hex</td><td>`AuthenticationError`</td><td>`AuthenticationException`</td></tr>
    <tr><td>The command exited non-zero</td><td>`CommandExitError`</td><td>`CommandExitException`</td></tr>
    <tr><td>The sandbox expired, a command ran past its deadline, or the request timed out</td><td>`TimeoutError`</td><td>`TimeoutException`</td></tr>
    <tr><td>The create was refused: no credit, a spend ceiling, or a plan limit</td><td>`SandboxError`, or `RateLimitError` for a `429`</td><td>`SandboxException`, or `RateLimitException` for a `429`</td></tr>
  </tbody>
</table>

A missing key is raised before any request leaves your process. A refused
create has no class of its own — read the message rather than branching on the
type, and see [Refused creates](/docs/errors#refused-creates). The SDKs differ
on keys in the older format another sandbox provider issued: Python refuses
those locally, TypeScript sends them and the server rejects them. The full
list is on [Errors](/docs/errors).

## Next steps

- [Sandbox lifecycle](/docs/sandbox) — connect to a sandbox you already have,
  and list the ones you are running.
- [Filesystem](/docs/sandbox/filesystem) — read and write files in the sandbox.
- [Pause, resume and snapshots](/docs/sandbox/pause-and-snapshots) — keep a
  sandbox's state and come back to it later.
- [Build a custom template](/docs/templates/build) — start from your own image.
- [API keys](/docs/api-keys) — pass a key per call, and revoke or rotate one.
