A Python template for an AI data analyst. Upload a CSV to an HTTP API and ask questions about it in plain English. Claude writes and runs Python against your data in an isolated Render Sandbox, and streams back its answer, the code it ran, and any charts it made.
$ curl -N -H "Authorization: Bearer $API_TOKEN" -H 'content-type: application/json' \
-d '{"message": "Which region had the highest revenue? Plot the monthly trend."}' \
$API_URL/api/sessions/$SESSION/messages
event: code
data: {"code": "import pandas as pd\ndf = pd.read_csv('/workspace/data/sales.csv') ..."}
event: result
data: {"exit_code": 0, "output": "exit code: 0\nstdout:\nregion\nSouth 144772.35 ..."}
event: file
data: {"name": "monthly_revenue_by_region.png", "url": "/api/sessions/.../files/monthly_revenue_by_region.png"}
event: text
data: {"text": "**South** had the highest total revenue at **$144,772**, narrowly ahead of East ..."}
event: done
data: {}
Most product agents eventually need to run code: to analyze data, transform files, or check their own answers. The code is written by a model, so it's untrusted, and it can't run in the process that holds your API keys.
This template shows the standard way to build that on Render. Your agent loop runs in an ordinary Python web service, which holds every credential. Each time the model wants to run code, the service sends that code to a Sandbox that holds no credentials and can't reach the internet. The agent gets a real Linux machine with pandas and matplotlib. Your infrastructure gets a hard boundary around everything the model writes.
It's deliberately small: one web service, under 600 lines of Python, and no framework beyond FastAPI and the Anthropic and Render SDKs. Read it in one sitting, then change the prompt, tools, and packages to build your own agent.
- Sandboxes as a tool: the agent loop runs outside the sandbox and calls it through the Render SDK, so model and Render keys never enter it.
- One sandbox per session: each chat session gets its own sandbox. The sandbox suspends when idle between questions and wakes on the next command, so files and installed packages persist across turns.
- Egress allow-lists: the sandbox can reach only the Python package index, which it needs to install pandas, numpy, and matplotlib at startup.
- Web services: a FastAPI service streams each answer over server-sent events (SSE).
- Blueprints: one
render.yamlcreates the service, generates its API token, and prompts for each secret.
POST /api/sessionscreates a session. The service starts creating its sandbox in the background: it creates a Python virtual environment and installs the data packages (about 15 seconds).POST /api/sessions/{id}/filesuploads a CSV. The service copies it into the sandbox at/workspace/data/.POST /api/sessions/{id}/messagessends a question. The service calls Claude with the conversation so far and one tool,run_python.- Each
run_pythoncall runs as a fresh Python process in the sandbox, with a 120-second timeout. Its exit code, stdout, and stderr go back to Claude, which reads them and decides what to do next. - After each run, the service downloads any new files in
/workspace/output/and streams their URLs. - When Claude stops calling tools, the service streams
done. The sandbox suspends after five minutes idle, and the session is closed after 30 minutes idle.
- The sandbox holds no credentials.
RENDER_API_KEYandANTHROPIC_API_KEYexist only in the web service. - The sandbox can reach only
pypi.organdfiles.pythonhosted.org. Everything else is blocked, so code can't send your data anywhere except, in principle, through requests to those two hosts. To remove even that, see Prebuild the environment. - Each session has its own sandbox, and sessions are unguessable random IDs. Anyone with
API_TOKENcan create sessions and spend model tokens, so treat it like a password. - The service caps concurrent sessions (
MAX_SESSIONS, default 10), upload size (50 MB), downloaded file size (10 MB), and tool rounds per question (20). - Uploaded data is sent to Anthropic as part of the conversation whenever the model prints it.
Trust boundaries covers these properties and their limits.
You need a Render workspace with Sandboxes enabled, a Render API key, your workspace ID, and an Anthropic API key.
-
Click Deploy to Render, or create a Blueprint from your fork. Paste the three secrets it asks for.
-
Copy the service URL and the generated
API_TOKENfrom the service's Environment page. -
Ask a question about the bundled sample data:
export API_URL=https://<your-service>.onrender.com API_TOKEN=... SESSION=$(curl -s -X POST -H "Authorization: Bearer $API_TOKEN" $API_URL/api/sessions | jq -r .id) curl -s -H "Authorization: Bearer $API_TOKEN" -F file=@examples/sales.csv $API_URL/api/sessions/$SESSION/files curl -N -H "Authorization: Bearer $API_TOKEN" -H 'content-type: application/json' \ -d '{"message": "Which product drives the most revenue? Plot it."}' \ $API_URL/api/sessions/$SESSION/messages
To run it on your machine instead, see Run it locally. Local runs still create real sandboxes.
- docs/0_QUICKSTART.md: deploy, ask your first question, run locally, and clean up.
- docs/1_EXTENSION.md: change the prompt, model, packages, and tools, or add persistence.
- docs/2_ARCHITECTURE.md: repository layout, the request path, interfaces, and trust boundaries.
- docs/3_TAKEAWAYS.md: reusable patterns for building agents with sandboxes.
MIT. See LICENSE.