DocsMigrate from E2B

Migrate from E2B

The Impello SDKs are a copy of E2B's clients, so most of your code does not change. Swap the package, swap the key, rebuild your templates.

Install the new packages

@impello/sdk replaces e2b on npm and impello replaces e2b on PyPI. Neither package depends on the one it replaces.

npm uninstall e2b
npm install @impello/sdk

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.

Only the import line changes in most files. The class, the modules and the method names are the same.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')
const result = await sandbox.commands.run('uname -a')

console.log(result.stdout)
await sandbox.kill()

Change the key

An Impello key is imp_ followed by hexadecimal characters. Make one at dashboard.impello.ai/keys; see API keys.

A key minted under the older e2b_ prefix cannot be made to work by any client. The API compares the whole prefix before it hashes anything, so no client-side flag or option makes one pass. Create a new key and replace the old value everywhere it is used.

The two SDKs fail differently on an old key. The Python SDK recognises the e2b_ prefix and raises AuthenticationException before a request leaves the process. The TypeScript SDK accepts both shapes on the client side and lets the API answer, so you get HTTP 401 raised as AuthenticationError. See Errors.

Check a new key against the API before you change anything else.

export IMPELLO_API_KEY=imp_...

curl "https://api.sandbox.impello.ai/v2/sandboxes" \
  -H "X-API-Key: $IMPELLO_API_KEY"

A 200 with a JSON list means the key is good. A 401 means it is not.

Environment variables

Every connection setting is read under its IMPELLO_ name first and the matching E2B_ name second. An environment that already exports the older names keeps working, so you can rename on your own schedule.

Impello nameAlso read asDefaultWhat it does
IMPELLO_API_KEYE2B_API_KEYnoneThe key. Starts with imp_
IMPELLO_DOMAINE2B_DOMAINsandbox.impello.aiThe domain the API and the sandboxes sit on
IMPELLO_API_URLE2B_API_URLhttps://api. plus the domainThe API address
IMPELLO_VALIDATE_API_KEYE2B_VALIDATE_API_KEYtrueSet to false to skip the key shape check. TypeScript only
IMPELLO_DEBUGE2B_DEBUGfalseTalk to http://localhost:3000 instead of the API, and address sandboxes at localhost:<port>
IMPELLO_SANDBOX_URLE2B_SANDBOX_URLnoneForces every sandbox connection to one fixed host; leave it unset

Two smaller groups follow different rules.

  • The TypeScript connection-pool variables have no IMPELLO_ name at all: E2B_API_CONNECTIONS, E2B_API_INFLIGHT_REQUESTS, E2B_ENVD_RPC_CONNECTIONS, E2B_ENVD_INFLIGHT_REQUESTS and E2B_ENVD_RPC_INFLIGHT_REQUESTS. Keep the old names; renaming them silently restores the defaults.
  • The Python pool settings take both prefixes: IMPELLO_CONNECTION_RETRIES (3), IMPELLO_KEEPALIVE_EXPIRY (300 seconds) and IMPELLO_MAX_KEEPALIVE_CONNECTIONS (20).

IMPELLO_ACCESS_TOKEN is read by the TypeScript SDK only, and it is deprecated. Send the token as an Authorization header through apiHeaders instead. The Python SDK never reads it.

Rebuild your templates

Templates do not transfer. There is no import, and nothing copies an image between fleets, so every custom template has to be built again here. Snapshots and paused sandboxes do not transfer either: a paused sandbox exists only on the fleet that paused it.

The default template names carry over. base, code-interpreter, claude, codex, opencode, pi, amp, droid, grok, openclaw, k3s, desktop and omarchy all exist here under those names. A Sandbox.create() call that names one of them needs no edit. claude also answers to claude-code, and code-interpreter to code-interpreter-v1. desktop is the one to check: it exists under the same name, but it declares no start command, so the graphical stack is not running when the sandbox boots. See Templates.

For a custom template, rewrite the definition against the builder at @impello/sdk/template and build it once. The builder reads your key from IMPELLO_API_KEY (or E2B_API_KEY), or from an apiKey passed in the build options.

import { Template } from '@impello/sdk/template'

const template = Template()
  .fromTemplate('base')
  .aptInstall(['ffmpeg'])

await Template.build(template, 'my-app:v1')

The builder is a subpath rather than a root export, so code that only creates sandboxes never loads a Dockerfile parser. Full reference: Build a custom template.

What is the same

Both packages are E2B's clients vendored under the MIT licence, with the API address, the key check and the differences listed below changed. The TypeScript SDK tracks E2B's client at 2.32.0; @impello/sdk carries its own version number. That means:

  • The same class and the same modules: Sandbox, sandbox.files, sandbox.commands, sandbox.pty, sandbox.git.
  • The same methods, including create, connect, list, kill, pause, setTimeout (set_timeout), getInfo (get_info) and getMetrics (get_metrics).
  • The same error and exception class names, so existing catch and except blocks still match. See Errors.
  • The same sandbox options: template, timeoutMs (timeout), envs, metadata and the network options.
  • The same authentication header. HTTP callers send X-API-Key, now against https://api.sandbox.impello.ai.

What is different

  • Package names. @impello/sdk and impello.
  • Key prefix. imp_, and old keys cannot be reused.
  • Default domain. sandbox.impello.ai, not e2b.app. Leave the domain unset and the SDK resolves the right fleet on its own.
  • Sandbox hosts. Every sandbox is reached at https://<port>-<sandboxId>.sandbox.impello.ai, routed by the Host header. There is no single stable sandbox.<domain> host, and sandbox.impello.ai is deliberately left out of the SDK's list of domains that serve one, because adding it would build https://sandbox.sandbox.impello.ai. Use getHost(port) (get_host(port) in Python); see Network and public URLs.
  • The template builder is a subpath. @impello/sdk/template, not the root import.
  • Static calls honour apiUrl. Sandbox.list, kill, pause and setTimeout take SandboxApiOpts, which now includes apiUrl. Upstream left it off the type while reading it at runtime. Passing one option bag to both create and list used to drop the URL silently and send the call to the default host.
  • Python has three things TypeScript does not. Impello binds connection options instead of reading the environment. Sandbox.fork checkpoints a running sandbox and starts copies from the snapshot. iam types workload identity on Sandbox.create; it has no TypeScript counterpart and is not supported.

What Impello does not ship

These exist upstream and have no counterpart here. If your integration uses one, it has to be replaced before you move.

  • A CLI. There is no impello command in any language, and no package installs a binary. Everything is done through the SDKs, the HTTP API or the dashboard.
  • Volumes. No volume client in either SDK and no persistent volume to mount. Both SDKs still type a volumeMounts (volume_mounts) option on Sandbox.create. There is nothing to mount; do not use it. Use the sandbox filesystem and snapshots instead.
  • Secrets. No secrets client in either SDK. Pass values with envs on Sandbox.create and keep them in your own secret store.
  • A code-interpreter client package. The code-interpreter template exists and starts Jupyter with the sandbox, behind an HTTP server on port 49999, but neither SDK ships a client for it. Drive it with sandbox.commands, or reach that HTTP server with getHost(49999) (get_host(49999) in Python); Jupyter itself listens on localhost:8888 inside the sandbox. See Templates.
  • A desktop client. Neither SDK has screenshot, click or stream methods. The omarchy template serves a desktop over noVNC on port 6080, with no VNC password. The port is public unless you set network: { allowPublicTraffic: false } (network={"allow_public_traffic": False} in Python). See Network and public URLs. You reach the desktop yourself.
  • An MCP gateway. The SDK types still carry MCP server names and an mcp option, but the gateway template they resolve to is not built here. With mcp and no template, create boots a template named mcp-gateway that was never built here, so the call fails. Do not use them.
  • Other names the types still carry. lifecycle.autoResume (auto_resume), network.maskRequestHost (mask_request_host) and getMcpUrl() and getMcpToken() (get_mcp_url(), get_mcp_token()) are in the types but are not part of the supported surface. Leave them unset.
  • A template builder in Python. TypeScript only, as above.

Errors lists every class each SDK raises.