Skip to main content
Install FastAPI and uvicorn first:
How the process starts is on Self-hosting. This page covers the route API once you have chosen which process serves it. A local service exposes FastAPI’s interactive docs at /docs and its OpenAPI schema at /openapi.json. pxt service update assigns a local port; look up the endpoint by catalog path and service name rather than assuming port 8000. For the README’s my_app/ingest example:
Replace my_app and ingest when using another target or service. Each column named in a route’s outputs becomes a typed field, computed columns included. Regenerate types after changing the Python routes. For a hosted service, copy its full endpoint URL from the Cloud dashboard or find it with pxt service list pxt://org:db --json using a credential that can list services. A key scoped only to invoke a service cannot list services, but can download that service’s schema when granted access. The gateway requires a credential to download the schema:
The hosted schema declares the gateway’s X-api-key header on every route except /health; local service schemas declare no key. In the Cloud dashboard, expand API docs on the service detail page to inspect and try routes as your signed-in user, without an API key. Trying an insert, update, or delete route can change data. Routes with return_fileresponse=True advertise a binary response for any media type. Check the response’s Content-Type when handling the returned image, video, audio, or document.

Call from a TypeScript app server

Keep a Cloud API key in your application server’s environment, never in browser code. Generated types do not check raw fetch calls, so call routes through openapi-fetch with the generated paths. The same module calls a local service (no key) or a hosted one. Set PIXELTABLE_SERVICE_URL to the full service endpoint, as above, run npm install openapi-fetch server-only, and put this in src/lib/pixeltable-client.ts. With the README’s /titles compute route:
Call it from a Server Component, or from your own Route Handler or Server Action; protect any that expose writes to browser users with your application’s authorization checks. Regenerate src/pixeltable.d.ts when routes change, and run your TypeScript check so request and response mismatches fail before deployment. The Cloud dashboard’s Use this endpoint TypeScript and JavaScript snippets are server-side examples. A return_fileresponse=True route needs parseAs: 'blob', or parseAs: 'stream' to pipe response.body through your Route Handler with the upstream Content-Type; without it the client parses the file as JSON and throws. Generated types describe upload fields as strings, so send an upload with fetch, a FormData body, and the same X-api-key header, and let fetch set the multipart boundary. PixeltableError.body is the parsed error response. Its detail is an object with error_code, message, and retryable (sometimes retry_after) for a Pixeltable runtime error and for an error the Cloud gateway answers itself: 401 for a missing or invalid key, 403 for a key without access to the service, 404 for an unknown service, 429 when the key is rate limited, and 503 when the service is not running. It is a string for a missing row and an array for a request-validation error, so branch on its shape, not the status. Retry a write only when detail.retryable is true, after retry_after seconds when present.

Mount on your own FastAPI app

@pxt.query evaluates the function body at decoration time. Define @pxt.query functions only after pxt schema update, because the decorator runs the function body immediately. Use a plain FastAPI @app.post() when one FastAPIRouter helper cannot express the request.

Compute without inserting

Table.compute() runs computed columns and returns the values without writing a row.
Same over HTTP:

Decorator-style routes

add_insert_route() builds the response model from the column schema. To return a custom JSON body, use @router.insert_route instead of add_insert_route(). The function receives outputs as keyword arguments and returns a pydantic.BaseModel.
At registration:
  • Every parameter is keyword-only and annotated.
  • Parameter names match outputs exactly.
  • Annotations match column types (nullable column: T | None). Media columns arrive as URL strings: annotate str.
  • Return type is a pydantic.BaseModel subclass.
background=True works the same as the non-decorator forms. Decorator routes are Python-only.

Export to an external database

SqlExport writes each successful insert or update to another SQL table. Pixeltable commits first; then the external write. If the external write fails, the request is HTTP 500. No rollback.
The row is the response body (outputs). Media columns are URL strings. The target table must already exist. SqlExport.method:
  • 'insert' (default): append. Replaying the request duplicates the row.
  • 'update': match on the target primary key. Not an upsert. No match: HTTP 500. Response columns must include every target PK plus at least one non-PK.
  • 'merge': not supported.
A Pixeltable insert with method='update' is allowed: append-only here, current-state there. export_sql= cannot combine with return_fileresponse=True. It works with background=True (the SQL write runs in the worker). SqlExport
A connection string with an embedded password is plaintext in the application file. Pull credentials from the environment, a .pgpass-style file, or pxt secret set.

Full example

Return computed columns

insert(), update(), and batch_update() can return computed columns without a follow-up query:
status.rows is a list of dicts. For typed access, model_validate() with extra="ignore":
After .collect(), use to_pydantic(). After return_rows=True, use model_validate().

Background jobs

background=True returns a job handle immediately:
Poll job_url (pxt service list prints the base URL):
Poll through your application server with the same Cloud key when hosted. Cloud returns an HTTPS job_url. The polling path is in the generated schema, so polling is a typed call on the same TypeScript client: pixeltable.GET('/_pxt/jobs/{job_id}', { params: { path: { job_id: id } } }). A failed job’s error_detail has the same shape as an HTTP error’s detail, so the same retry rule applies. background cannot combine with return_fileresponse. Flags: CLI.
Last modified on September 23, 2026