Skip to main content
The pxt CLI ships with the pixeltable package. It covers two surfaces:
  • Catalog operations — inspect, query, and manage tables, views, and directories. Backed by a long-lived local daemon so each command takes ~40 ms after the first invocation.
  • Service deployment — run the services an application file declares with pxt service. Requires the serve extra (which pulls in fastapi[standard] and uvicorn):
Verify the installation:
On the first catalog command, pxt auto-spawns a daemon bound to 127.0.0.1:22089. The daemon survives across shells and stays warm for subsequent commands. Override the port with PXT_PORT.

Command structure

Use pxt <command> --help for per-subcommand flags and examples.

Universal flags

These flags work the same way across the catalog commands that support them and are not repeated in the per-command tables below.

Working directory

pxt cd sets a working directory that is prepended to relative paths in later commands, and pxt pwd prints it — the catalog analogue of a shell’s cd/pwd.
Absolute paths ignore the working directory: a leading / (e.g. pxt ls /other_dir) resolves from the catalog root, and a pxt://org:db/... URI addresses a hosted catalog. . and .. work in any path and resolve against the working directory; .. at the catalog root keeps the root. The working directory is scoped to the invoking terminal, not the daemon globally — it is keyed by the shell’s session, sent with every command. So separate terminals have independent working directories, and, crucially, it does not leak into subprocesses or agents you launch: a spawned process runs under its own session with no working directory, so its pxt commands resolve relative paths from the catalog root regardless of what you set interactively. Because of that isolation, scripts and agents should address the catalog with absolute paths (/... or pxt://...) and ignore the working directory; it is a convenience for interactive terminal use.

Quick reference

Inspection commands

pxt ls

List entries under a directory.
Output of pxt ls -l:
Flag letters: c = has at least one computed column, i = has at least one index.

pxt describe

Show a table’s schema and metadata. The plain form is human-readable; --json returns the full get_metadata() dict.

pxt columns / pxt computed

List columns for one or more tables. pxt computed is shorthand for pxt columns --computed. The path argument may be a single table or a directory; a directory path lists columns for every table beneath it, recursively. A directory path may be a local path or a hosted uri (pxt://org:db/...). With no path, every table in the in-process catalog is listed.

pxt idxs

List indexes. Shows both B-tree and embedding indexes by default; the --embedding flag restricts to embedding indexes. Like pxt columns, the path may be a single table or a directory (walked recursively), a hosted database root (pxt://org:db), or omitted for the whole in-process catalog.

pxt history

Show a table’s version timeline.

pxt status

Daemon and runtime state: pxt version, daemon PID, configured paths, total tables, total errors.

pxt config

Every documented configuration setting with its current value and source (env, file, or unset). Credentials show <redacted> when set; the source column reveals presence even when the value is masked.

Query commands

pxt rows

Show the first N rows of a table. Unstored computed columns are skipped by default (selecting one forces evaluation, which can invoke LLMs or expensive compute); pass them explicitly via --cols to include them.

pxt get

Look up a single row by primary key. A numeric-looking PK token is coerced to int or float; everything else stays a string. There is no quoting escape for a string-typed PK whose value looks numeric — if your PK column is a string but the value is 42, the server will reject the type mismatch. The table must declare a primary key. Unstored computed columns are skipped unless requested explicitly via --cols (consistent with rows).

pxt count

pxt errors

List rows where a stored computed column failed. The table must have a primary key (so each failing row can be identified).

Mutation commands

Every mutation accepts the universal -n/--dry-run and --json flags. The destructive ones (drop, drop-dir, revert) also prompt [y/N] with a TTY and accept -f/--force to skip the prompt; in non-interactive contexts they refuse to proceed without -f. rename and mv don’t prompt: renaming or moving a catalog entry is reversible and doesn’t lose data.

pxt drop

Drop a table or view. Use pxt drop-dir for directories.

pxt drop-dir

Remove a directory. Use pxt drop for tables/views.

pxt rename

Rename in place; the parent directory is preserved. <new_name> must be a single leaf name (no / or .). Takes only universal flags.

pxt mv

Move a table/view/dir under a different directory; the leaf name is preserved. <new_dir> can be '' or / for the root directory. Takes only universal flags.

pxt revert

Undo recent ops on a table. Each revert undoes one op; --steps repeats.
Revert is irreversible. Run pxt history my_dir/my_table first to see what would be undone.

Project layout

pxt schema and pxt service read a Python file, and the tables they create refer back to the udfs that file calls. A reference is a module path, so the file has to belong to a project. The project root is the directory holding the project configuration, and every local module path is relative to it. pxt init writes that configuration in the current directory:
The file it writes holds one entry per database the project uses:
In a directory that already holds a pyproject.toml, the same entry is appended there as [[tool.pixeltable.database]] instead, and that section is what marks the root. Every directory from the root down to a file becomes one component of that file’s module path, so each of those directory names has to be a Python identifier:
Imports resolve from the root down. This is where a single-file recipe and a larger application diverge: a file directly under the root imports its neighbors by their bare names, so recipe.py and functions.py side by side use from functions import .... Once an application moves into a subdirectory, that subdirectory joins the path: from ad_gen.functions import .... A recorded path is how a later process — the daemon, a serving worker, a hosted pod — finds the udf again, so a command given a file outside any project root is refused. pxt schema check and pxt service check validate a file on its own — it imports without touching the catalog, it declares what the verb needs, and the udfs its columns call are named by paths another process can resolve:

Schema management

The commands above act on one object at a time. pxt schema works differently: you describe the tables you want in a Python file, and the CLI reconciles a catalog directory to that description. Provisioning an empty target and evolving an existing one are the same command, so there is no separate first-time step. SCHEMA is a path to a Python file. TARGET is a catalog directory or a pxt:// URI; it is created by update if it doesn’t exist.

The schema file

A schema file defines one or more models on a pxt.model_base(). Each model becomes one table, named by name=. pxt schema example writes a file covering every construct the schema DSL supports, so you never have to start from a blank page and never have to look a construct up:
pxt schema example --brief writes the minimal version instead:
An annotation (name: type) declares a stored column; an assignment (name = expr) declares a computed column. A model with base= becomes a view of the model that query selects from. The daemon imports the file, so it must be readable there. Its own directory is added to sys.path, so it can import modules sitting next to it.

Reviewing and applying

diff prints one line per table, then one per operation:

Applying

update creates missing tables and migrates existing ones, adding and dropping columns and indexes. It takes the same flags as the other mutations, plus one of its own:
If the plan contains a destructive operation and --allow-destructive is absent, nothing at all is applied and update exits 3.
Dropping a column or an index destroys its data. Run pxt schema diff (or update -n) first: the plan marks every operation safe, DESTRUCTIVE, or UNSUPPORTED.
Some differences cannot be applied in place: a table declared where a view exists, a changed iterator, or a column whose type or properties changed. Those are reported as UNSUPPORTED, nothing is applied, and update exits 1. Adjust the schema file or the table by hand.

Exit codes

The schema commands report their outcome in the exit status, so a caller never has to parse the output: A drift check in CI is therefore one command:

Machine-readable plans

pxt schema diff --json emits the whole plan as one object: schema_file, catalog_dir, in_agreement, tables, extras, and a summary with one count per resolution. Each entry in tables carries its path, resolution, whether it is destructive, and its ops:
An op’s target is column, index, or table, and its op is add, drop, or alter. name is what it acts on — a column, an index, the differing attribute when the target is a table, or the table path for a drop — and details holds that operation’s operands, such as the type of an added column. severity is additive, destructive, or unsupported; destructive is the boolean form of the middle case. A table’s resolution is up_to_date, create, update_additive, update_destructive, or unsupported, and one with create carries no ops, because the create subsumes them. These field names and values are the catalog’s own, as returned by TableModel.get_model_diff(), so a plan read from the CLI and a diff read from Python describe a change the same way. update and prune return the same object with a status on every table and operation: Every path returns the plan, including the ones that refuse before reaching the daemon, so an --allow-destructive refusal is as machine-readable as a success: exit 3, with the offending operations marked refused and the rest skipped. prune reports its drops in a top-level ops array, each with target: "table", op: "drop", and the dropped table’s path in name.

Pruning

update only ever touches tables the schema declares, so tables it doesn’t know about accumulate. diff lists them as extras; prune drops them. A full reconcile is update followed by prune.
Only tables under TARGET are considered, so nothing elsewhere in the catalog is affected, and declared tables are never dropped. A view is dropped before its base. Prune never force-drops: a table that something outside the pruned set depends on is left in place and the drop fails, naming what depends on it.
Pruning is irreversible. Run it with -n first.

Interactive shell

For agentic or scripted workloads that issue many commands in sequence, pxt shell amortizes Python startup over the session:
Inside the shell, every pxt command is available unmodified. Errors from one command don’t kill the session. Use help, exit, quit, or Ctrl-D to leave.

Output and scripting

Most catalog commands accept --json for stable, machine-readable output (exceptions: shell is interactive, health is already JSON):
Without --json, output is column-aligned text. The schema commands additionally report drift in their exit status (0 in sync, 2 pending, 3 refused, 1 error), so a CI gate needs no output parsing at all.

Serving

pxt service runs the services in an application file. An application file is any Python source file containing table/view models and either FastAPIRouter instances (which serve routes over them) or a fastapi.FastAPI application of your own. It requires the serve extra (pip install 'pixeltable[serve]'), which pulls in fastapi[standard] and uvicorn.
A service is a full FastAPI application with auto-generated OpenAPI docs at /docs. For the API the routes are declared with, see the Python serving API.
One file declares both halves, so the same file drives both commands:
TARGET is the catalog directory the models bind against, so one file can be applied to a development directory and a production one.

Serving your own application

A file may supply its own fastapi.FastAPI object instead of leaving Pixeltable to build one. Then the file declares one service, named after its module, and pxt service update serves that application as it is:
The models in the file are bound at TARGET before the application serves, so a handler reaches them by name (Notes.insert(...)). All FastAPIRouter instances in the same source file are expected to be included in the FastAPI application (via include_router()), and the application file still produces a single service:
A router not included in app would never be served, and in that situation pxt service returns with an error. The routes of an included FastAPIRouter are diffed by their declarations (i.e., they take the data types of path parameters into account); the paths the application serves itself are simply diffed as path strings.

Hosted targets

TARGET may be a pxt://org:db uri, and the services then run in that database rather than on this machine. The database needs this project and its tables first, so the order is:
diff, update, prune, stop and list all accept a hosted target. run does not: it serves from the calling process, so it is local by definition. Two verbs differ in meaning against a hosted database, where a stopped service keeps its registration: stop stops it and leaves it there to be started again, while prune forgets it.

pxt service verbs

Like the schema verbs, diff reports drift in its exit status: 0 in agreement, 2 changes pending, 3 refused, 1 error. So a CI gate needs no output parsing.

Flags

Tracing

--otel emits OpenTelemetry traces from the served application: Pixeltable’s own spans, nested under the request spans of the FastAPI app. It needs the instrumentation package (pip install 'pixeltable[otel]'), and the endpoint and service name come from the OTEL_* environment variables or the [otel] config section, as they do for any Pixeltable process.
Tracing is configured once per process, so it is a property of the deployment rather than of the file: adding or dropping --otel restarts the service, and pxt service diff app.py my_dir --otel reports that restart as a pending change before update performs it.

Background and foreground

update starts one background process per service, each on its own port, and records it so list and stop can find it again. A service that crashes disappears from list with no cleanup step, because a record is only as live as the process it names.
run stays in the foreground until you interrupt it and does not register the service for list or stop, so it is a separate command, not a flag on update. Use it as a container entrypoint or a development loop. One service per process, as update deploys them; name the service as a third argument when the file declares more than one:

Restarts

A service binds its models once, when its process starts, so a changed declaration is applied by replacing the process. diff reports which services that would interrupt, and update says so before it does it. Adding a route is additive; changing or removing one stops serving a contract a caller may be using, so it needs --allow-destructive.

Cloud

pxt db and pxt org manage cloud-hosted databases and organizations. All cloud commands require PIXELTABLE_API_KEY. Create the key in the Cloud dashboard: Get an API key. Then set it in the environment or config.toml (Configuration). All cloud commands accept --json for machine-readable output.

Cloud configuration reference

pxt db diff and pxt db update read the entry naming the target database from the project configuration — pixeltable.toml, or pyproject.toml under [tool.pixeltable] — found from the working directory.

[[pixeltable.database]] — what a project declares about one database

Defines which project files the database gets, what its image holds, what it runs on, and which secrets it holds.
An entry with no name configures the local database. A hosted database is configured by the entry naming its uri.

pxt db

Manage cloud-hosted Pixeltable databases. A database is a hosted Pixeltable instance with its own compute, storage, and Python environment. Database URIs use the form pxt://org:db. Valid states: PROVISIONING, STARTING, AVAILABLE, UPDATING, STOPPING, STOPPED, FAILED. The URI argument is optional: with it omitted, these commands use db_uri from the Pixeltable config file (see Configuration), so a project that sets it can run pxt db status and friends with no argument.

pxt db update

This is the command that creates a hosted database, and the one that keeps it current afterwards. Put the database in a [[pixeltable.database]] entry, then run update against the URI its name holds:
The uri on the command line selects the entry by its name: an entry naming another database configures that one, and a target no entry names is an error. The first run creates the database, builds the image its environment describes and uploads the project files the pods run; every later run applies whatever has changed since. The cloud configuration reference lists everything an entry can declare. update applies secrets first so pods can read them on start, then a new image if dependencies changed, then uploaded project files if sources changed, then one resize for any changed CPU, memory, disk, or worker counts so the pods restart only once. A secret is the name of an environment variable (openai_api_key = 'env:OPENAI_API_KEY'), never a value.

pxt db list

pxt db status

Prints current state, endpoint, location, and timestamps.

pxt db start

Wake a stopped database. Polls until AVAILABLE.

pxt db stop

Stop a running database (releases compute; storage is preserved).

pxt db diff

pxt db diff compares the hosted database to that config entry (see the cloud configuration reference): capacity, secrets, the Python image, and the project file archive — so an edit to a source file is uploaded in seconds, and only a dependency change costs an image build. Exit status is 0 in agreement and 2 with changes pending; nothing is built, resized or set.

pxt db build-image

Uploads the project files and builds the image from its environment, without comparing anything first. pxt db update uploads project files or rebuilds the image only when they changed. pxt db build-image always does both, which is how you force a rebuild after a failed one. Polls until the build completes or fails; the pods restart on the new image and the new sources.

pxt db delete

Deletes the database, its storage, and all services. Irreversible.

pxt org

pxt org list

List all organizations accessible to the current API key.

pxt org status

Show the organization’s name, ID, and default database.

pxt secret

An org secret applies to every database in the org. A database secret applies to that database and wins on a key collision. list prints names, never values. A project can declare secrets in its [[pixeltable.database]] entry as the name of the environment variable holding each value. pxt db update sets them from there. A running database holds the values it started with. Run pxt db stop then pxt db start to pick up a change.

What’s next

Last modified on September 3, 2026