---
title: "Run Python in the Ploti sandbox"
method: POST
path: "/v1/tools/execute_python"
---

# Run Python in the Ploti sandbox

`POST /v1/tools/execute_python`

## Request

- Base URL: `https://api.ploti.ai`
- URL: `https://api.ploti.ai/v1/tools/execute_python`
- Auth: HTTP bearer

Execute Python in this session's sandbox. Import tools or a specific module in every call (database, schema, checkpoint, materialized_tables, saved_query, filters, location, municode, text_documents, files, map_image, boundary, spatial_relations, branding) — the python/<module> guides carry the exact signatures.

Requires a viewport — call navigate first. The sandbox can reach any host on the internet. Each execution starts with a fresh namespace: imports and variables do not carry into the next call, but files under /home/daytona persist for the session. stdout and stderr both come back (print() output and tracebacks); the last expression's value is not returned. Tracebacks cite your code as File "<agent-code>" with correct line numbers; frames labeled "<string>" are runtime wrapper, not your code.

Calls that finish within ~45 seconds return synchronously; longer calls are promoted to a background job automatically — you get the job id, the code keeps running (up to 15 minutes), and get_result returns the output (kept for one hour). The wait covers everything in the call, not each query separately. Set background:true to skip the wait for known-heavy work (map_image renders, broad scans). Executions run one at a time, so another execute_python call will queue. If a call still fails with a transport error, the code may have run: check list_outputs before re-running anything that creates artifacts. Only checkpoints can be deleted (delete_checkpoint); tables, saved queries, filters and files cannot.

Do a whole step in one call (query, save the checkpoints, build the map): hosts cap how many tool calls a reply may make. When a call fails, fix it from what it says rather than rerunning it or stopping, and split a query that timed out. Never select bare geometry columns or print whole tables. Something is saved only once its create call has returned it; tell the user plainly when one failed.

## Request body

- object
  - `sessionId` string, required: The id start_session returned. Lost it? get_session without an id lists this workspace's open sessions.
  - `description` string, required: What this code does, as the one line the user sees in Ploti, e.g. 'Querying vacant industrial parcels'.
  - `code` string, required: The Python source to run, importing what it uses (see the tool description) and printing what you need to see.
  - `background` boolean: true returns a job id at once, without waiting; poll get_result within an hour of the job finishing. Use it for work known to be heavy. Left out, the call waits up to ~45 seconds and returns the output, or a job id if the code is still running then.

## Response `200`

What the tool returned: its text, any images, and structured data.

- object
  - `text` string, required
  - `images` object[], required
    - `mimeType` string
    - `data` string, byte
  - `data` unknown

## Other responses

- `400`: invalid_input: the arguments do not fit the tool's input schema.
- `403`: insufficient_scope: the credential may not use this tool.
- `404`: not_found: there is no such tool.
- `413`: payload_too_large: the body is over 256 KB (2 MB for /v1/tools).
- `415`: unsupported_media_type: a body was sent without Content-Type: application/json.
- `422`: tool_error: the tool refused or failed; error.message says why.
- `429`: rate_limited: this credential is over its rate. Retry after the Retry-After header's seconds.
- `500`: internal: Ploti failed. Retry; if it keeps happening, contact support.

---

[API](https://skmtc.dev/ploti/apis/ploti-api.md) · [All operations](https://skmtc.dev/ploti/apis/ploti-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ploti/apis/ploti-api/revisions/8c2c91c72d52?raw)
