Skip to main content

Lizard SDK

TypeScript and Python clients for Lizard apps, managed databases, storage and Linux sandboxes.

The current platform runs sandboxes on Kubernetes. Persistent volumes keep files across sandbox lifetimes. Pause, resume, fork and snapshot restore currently return HTTP 501; the SDK exposes those endpoints but does not promise state preservation.

Requires Node.js 18+ or Python 3.10+. The optional LizardCLI adapter and billing.payX402() / billing.pay_x402() require Lizard CLI 4.0.8+ on PATH.

See CLI coverage and migration notes for native methods, CLI-backed operations and test scope.

Install

# JavaScript / TypeScript
npm install @lizard-build/sdk

# Python
pip install lizard-sdk

Quickstart

JavaScript / TypeScript

import { Lizard } from '@lizard-build/sdk'

// A client is pinned to one project — sandboxes are billed per project, so a
// project is required. It can be the project's ID, slug, or name.
// apiKey defaults to the LIZARD_API_KEY env var.
const lizard = new Lizard({ project: 'my-project' })

// Boot a sandbox from the 'base' template (Debian + Node.js 26)
const sandbox = await lizard.create('base')

// Write a file directly into the sandbox filesystem
await sandbox.fs.write('/app/server.js', `
  const http = require('http')
  http.createServer((_, res) => res.end('hello from Lizard')).listen(3000)
`)

// Execute a process inside the sandbox
await sandbox.process.exec('node /app/server.js &')

// Get a public HTTPS URL for port 3000 inside the sandbox
const url = await sandbox.getHost(3000)
console.log(`Live at https://${url}`)

// Tear down the sandbox when done
await sandbox.kill()

Python

from lizard import Lizard

# A client is pinned to one project — sandboxes are billed per project, so a
# project is required (its ID, slug, or name). api_key defaults to LIZARD_API_KEY.
lizard = Lizard(project="my-project")

# Boot a Python sandbox from the 'code-interpreter-v1' template
sandbox = lizard.create("code-interpreter-v1")

# Write a script into the sandbox filesystem
sandbox.fs.write("/app/main.py", """
import http.server, socketserver

class Handler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.end_headers()
        self.wfile.write(b"hello from Lizard")

with socketserver.TCPServer(("", 3000), Handler) as httpd:
    httpd.serve_forever()
""")

# Execute a process inside the sandbox
sandbox.process.exec_("python /app/main.py &")

print(f"Live at https://{sandbox.get_host(3000)}")

sandbox.kill()

Persisting Work Across Sandboxes

Sandboxes are ephemeral: killing one, or letting it hit its timeout, discards everything written inside it. State that has to outlive a sandbox goes on a volume — a separate disk mounted at /workspace that a later sandbox re-attaches.

A volume is node-local, so it fixes the region too. You don't thread a region through both calls: the sandbox is placed wherever its volume already lives.

const vol = await lizard.volumes.getOrCreate('agent-scratch', { sizeGb: 10 })

const first = await lizard.create('codex', { volumeName: 'agent-scratch' })
await first.process.exec('pip install numpy pandas && echo "notes" > /workspace/notes.txt')
await first.kill()          // sandbox gone, /workspace survives

const second = await lizard.create('codex', { volumeName: 'agent-scratch' })
console.log(await second.fs.read('/workspace/notes.txt'))   // "notes"
vol = lizard.volumes.get_or_create("agent-scratch", size_gb=10)

first = lizard.create("codex", volume_name="agent-scratch")
first.process.exec_('echo "notes" > /workspace/notes.txt')
first.kill()                # sandbox gone, /workspace survives

second = lizard.create("codex", volume_name="agent-scratch")
print(second.fs.read("/workspace/notes.txt"))   # "notes"

Note that only /workspace survives — installed packages and in-memory state do not. Bake tooling into a template instead of reinstalling it per sandbox.

pause() / resume() exist on the client but are not implemented for the current runtime and always fail with HTTP 501. Use a volume.

Giving Each of Your Users Their Own Workspace

If you are building on top of Lizard and your users each need isolated resources, give each one a workspace and an API key scoped to it. They get isolation from each other; you keep one bill.

const lizard = new Lizard({ apiKey: process.env.LIZARD_API_KEY })

const ws  = await lizard.workspaces.create({ name: `user-${userId}` })
const prj = await lizard.projects.create({ workspaceId: ws.id, name: 'default' })
const key = await lizard.apiKeys.create({ name: `user-${userId}`, workspaces: [ws.id] })

// key.key is returned exactly once. Store it now.
await db.users.update(userId, { lizardKey: key.key })

That key reaches nothing outside ws, and it cannot mint a broader one — the server rejects that with an exact subset check. So it is safe to hand to the user, and safe to put inside a sandbox so agent code can use the CLI as that user:

const sandbox = await Sandbox.create('codex', {
  projectId: prj.id,
  lizardToken: key.key,        // scoped — bounded if it leaks
})
await sandbox.process.exec('lizard volume list')   // sees only this workspace
ws = lizard.workspaces.create(name=f"user-{user_id}")
prj = lizard.projects.create(workspace_id=ws.id, name="default")
key = lizard.api_keys.create(name=f"user-{user_id}", workspaces=[ws.id])

sandbox = Sandbox.create("codex", project_id=prj.id, lizard_token=key.key)
sandbox.process.exec_("lizard volume list")

A key with no scope has full access to everything the creating account can reach — pass workspaces or projects unless you mean that.

What a scoped key deliberately cannot see

Billing is account-level: one balance, one ledger, one set of saved cards, shared by every workspace. There is no per-workspace view of it, so a scoped key is refused outright (403 ACCOUNT_SCOPE_REQUIRED) on lizard.billing.* and on account-wide usage, and whoami() returns only identity plus the key's own scopes — not the account's email, balance or plan.

That matters because of where these keys end up. A key you hand to a user, or inject into a sandbox with lizardToken, is readable by anything running there. It should not be a way to read your card details or spend against them.

For per-workspace spend, use lizard.metrics — that is scoped, and is what you would bill a user from.

API

new Lizard({ project, apiKey?, apiUrl?, timeoutMs? })

Create a client pinned to a project. Every sandbox is billed per project, so project is required — pass its ID, slug, or name (resolved to an ID on first use and cached). apiKey defaults to the LIZARD_API_KEY env var.

const lizard = new Lizard({ project: 'my-project' })
const sandbox = await lizard.create('base')
const sandbox = await lizard.create('code-interpreter-v1', { timeoutMs: 10 * 60 * 1000 })

Sandbox.create(template?, opts?)

Create a Lizard sandbox using a template such as base or code-interpreter-v1. Template availability depends on the platform and region. A project is required — pass project (ID, slug, or name) or an exact projectId in opts, or use a Lizard client, which pins one for you.

const sandbox = await Sandbox.create('base', { project: 'my-project' })
const sandbox = await Sandbox.create('code-interpreter-v1', { project: 'my-project', timeoutMs: 10 * 60 * 1000 })

Sandbox.connect(sandboxId, opts?)

Connect to an existing sandbox by ID. Throws NotFoundError if it has been killed or has expired.

Sandbox.list(opts?)

List all running sandboxes for the authenticated account.


Account and provisioning

Namespace Methods
lizard.workspaces list(), create({ name }), delete(id, { force? }), find(nameOrSlugOrId)
lizard.apiKeys list(), create({ name, workspaces?, projects? }), delete(id)
lizard.projects list({ workspaceId? }), get(id), create({ workspaceId, name }), update(id, { name }), delete(id)
lizard.regions list()
lizard.billing balance(), transactions({ limit?, cursor?, includeUsage? }), summary(), live() — unscoped keys only
lizard.whoami() The account behind the credential; a scoped key gets identity and its own scopes, not the account's email or balance
lizard.platform The raw HTTP client, for endpoints not wrapped yet

workspaces.delete() is empty-only by default; the server refuses while any project, sandbox or volume remains. { force: true } deletes the workspace and everything in it, irreversibly.

In Python the namespaces are lizard.workspaces, lizard.api_keys, lizard.regions, lizard.billing, and arguments are snake_case (workspace_id, include_usage).

Volumes

Method Description
lizard.volumes.getOrCreate(name, { sizeGb?, region? }) The volume with this name, created if absent
lizard.volumes.create(name, { sizeGb?, region? }) Create; throws ConflictError if the name is taken
lizard.volumes.get(nameOrId) Look one up
lizard.volumes.list() Every volume in the client's project
lizard.volumes.delete(nameOrId) Delete

A volume's name is its key inside a project, so an agent can reconstruct it between runs without storing an id. The static Volume.* forms take an explicit projectId as their first argument.

region places the volume (see lizard.regions.list() for valid ids) and, because a volume is node-local, also fixes the region of any sandbox that mounts it. You normally set it here or nowhere.


sandbox.fs

Read and write files inside the sandbox filesystem.

Method Description
fs.write(path, data) Write a file (string or bytes)
fs.read(path) Read a file as a string
fs.list(path) List directory contents
fs.remove(path) Delete a file or directory
fs.makeDir(path) Create a directory and parents

sandbox.process

Execute commands inside the sandbox.

Method Description
process.exec(cmd, opts?) Run a command and wait for it to finish

exec returns { stdout, stderr, exitCode } (JS) or ProcessResult (Python). In Python the method is named exec_ because exec is a reserved keyword.

sandbox.getHost(port)

Returns a public HTTPS URL for a port listening inside the sandbox — no tunneling required.

await sandbox.process.exec('npx -y serve -p 3000 &')
const url = await sandbox.getHost(3000)
// https://{sandboxId}-3000.sandbox.{region}.onlizard.com

sandbox.pause() / sandbox.resume()

Not implemented for the current runtime — both always fail with HTTP 501. Use a volume to carry work across sandboxes.

sandbox.kill()

Terminate the sandbox and release all resources.

sandbox.setTimeout(ms)

Extend or reduce the sandbox timeout.


Environment Variables

Variable Description
LIZARD_API_KEY API key (required — get one at lizard.build)
LIZARD_API_URL Override the API base URL (default: https://lizard.build)

The X-API-Key header is used for all authenticated requests.

Deploy What You Build

Once your agent has produced a working app inside a sandbox, deploy it as a persistent Lizard service — no Dockerfile needed:

lizard up

Lizard manages the sandbox runtime.

License

Apache-2.0

Metadata

Release files for lizard-sdk 0.1.40

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lizard-sdk 0.1.40
File Size Uploaded
lizard_sdk-0.1.40.tar.gz 37.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lizard-sdk 0.1.40
File Interpreter ABI Platform
lizard_sdk-0.1.40-py3-none-any.whl Python 3 none any Details

Total release size: 82.9 kB

Release files / lizard_sdk-0.1.40.tar.gz

Download URL lizard_sdk-0.1.40.tar.gz
Size 37.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ae57b4993861341fa0e5867bf10512f5edead61c49d24109d85f7f4ef874610f
BLAKE2b-256 checksum
How to use checksums
10cb97eb0b07874a118824f7928f52714a81f6f3a64430b4773ac6e2e3df98ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / lizard_sdk-0.1.40-py3-none-any.whl

Download URL lizard_sdk-0.1.40-py3-none-any.whl
Size 45.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
129dbb0a9a985c28b8b2d071926bf53bf9202b87c6d84182e6bfce216e73d9bd
BLAKE2b-256 checksum
How to use checksums
082f86c0139e6011c49da805518cba46b1bcf3e409314ab4140d2226dc338013
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.49

2 release files

0.1.48

2 release files

0.1.47

2 release files

0.1.46

2 release files

0.1.45

2 release files

0.1.44

2 release files

0.1.43

2 release files

0.1.42

2 release files

0.1.41

2 release files

This release

0.1.40 This release

2 release files

0.1.39

2 release files

0.1.38

2 release files

0.1.37

2 release files

0.1.36

2 release files

0.1.35

2 release files

0.1.34

2 release files

0.1.33

2 release files

0.1.32

2 release files

0.1.31

2 release files

0.1.30

2 release files

0.1.29

2 release files

0.1.28

2 release files

0.1.26

2 release files

0.1.25

2 release files

0.1.24

2 release files

0.1.23

2 release files

0.1.22

2 release files

0.1.21

2 release files

0.1.20

2 release files

0.1.19

2 release files

0.1.18

2 release files

0.1.17

2 release files

0.1.16

2 release files

0.1.15

2 release files

0.1.14

2 release files

0.1.13

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page