AI Sandbox
Pricing
Pay per usage
AI Sandbox
This Actor provides a secure execution environment for code generated by AI agents. You can interact with the sandbox through shell, REST API, MCP, and web UI.
Pricing
Pay per usage
Rating
5.0
(1)
Developer

Apify
Actor stats
0
Bookmarked
7
Total users
0
Monthly active users
a day ago
Last modified
Categories
Share
Apify AI Sandbox
Isolated sandbox for running AI coding operations in a containerized environment. 🚀
Use cases
- 🔒 Execute untrusted code safely: Run potentially unsafe code in an isolated container with controlled resources and security boundaries
- 🤖 AI agent development: Provide isolated and managed development environments where AI agents can code, test, and execute operations securely
- 📦 Sandboxed operations: Execute system commands, file operations, and custom scripts in a contained environment
- 🖥️ Interactive debugging: Access the sandbox via browser-based shell terminal for real-time exploration and troubleshooting
- 🔗 Apify Actor orchestration: Agents can access the limited permissions Apify token (available as
APIFY_TOKENenv var) to run other limited permissions Actors, process or analyze their output, and build complex data pipelines by combining results from multiple Actors
Quickstart
Start the Actor
- Run it on the Apify platform through the Console
- Check the Actor run log console for connection details (host, port, MCP endpoint URL)
- Open the landing page link from the run logs for connection details, quick links (shell + health), and endpoint URLs for the current run.
Ways to connect
Start the Actor (see Quickstart above), then choose how to interact:
- MCP client: Agent-driven access to run code or develop with LLM tooling.
- REST API: Endpoints to run code or shell commands.
- Interactive shell: Browser terminal for manual exploration.
MCP client
Use a Model Context Protocol (MCP) client to interact with this sandbox. See modelcontextprotocol.io/clients.
Connect with Claude Code:
$claude mcp add --transport http sandbox https://YOUR-RUN-ID.runs.apify.net/mcp
Replace YOUR-RUN-ID with the run ID from your Actor execution (URL is also in the landing page and logs). Then prompt your agent; it will use the sandbox tools automatically over MCP.
REST API
Available endpoints (all URLs come from the run logs/landing page):
Core endpoints
-
POST /mcp- Body: JSON-RPC over HTTP per MCP client
- Returns: JSON-RPC response
-
POST /exec- Execute shell commands OR code snippets (JavaScript, TypeScript, Python)
- Body:
{ command: string; language?: string; cwd?: string; timeoutSecs?: number } - Language options:
"js","javascript","ts","typescript","py","python","bash","sh"(omit for shell) - Returns (200 on success, 500 on error):
{ stdout: string; stderr: string; exitCode: number; language: string } - The
languagefield in response is always present:"shell"for shell commands,"js"/"ts"/"py"for code
-
GET /health- Health check endpoint
- Returns (200/503):
{ status: 'healthy' | 'initializing' | 'unhealthy'; message?: string }
-
GET /shell/- Interactive browser terminal
- Returns: Interactive terminal powered by ttyd
-
GET /llms.txt- Markdown documentation for LLMs (same usage info as landing page)
- Returns (200): Plain text Markdown with all endpoint documentation
Health status:
status: "initializing"(503) – dependencies/setup still runningstatus: "unhealthy"(503) – init script failed; check logsstatus: "healthy"(200) – ready for requests
RESTful filesystem endpoints
Direct filesystem access using standard HTTP methods. All paths are relative to /sandbox.
-
GET /fs/{path}- Read file: Returns raw file bytes with appropriate
Content-Typeheader - List directory: Returns JSON with directory contents (files and subdirectories with sizes)
- Query params:
?download=1: Download file as attachment (or directory as ZIP)
- Returns (200): File content or directory JSON, (404): Path not found
- Read file: Returns raw file bytes with appropriate
-
PUT /fs/{path}- Write/replace file: Create or replace file with request body content
- Accepts raw bytes or text in request body
- Automatically creates parent directories if they don't exist
- Returns (200):
{ success: true, path: string, size: number }
-
POST /fs/{path}?mkdir=1- Create directory: Create directory at specified path (recursive by default)
- Returns (201):
{ success: true, path: string, type: "directory" }
-
POST /fs/{path}?append=1- Append to file: Append request body to existing file (creates file if it doesn't exist)
- Returns (200):
{ success: true, path: string, size: number }
-
DELETE /fs/{path}- Delete file or directory
- Query params:
?recursive=1: Enable recursive deletion for non-empty directories
- Returns (200):
{ success: true, path: string, deleted: true }, (409): Directory not empty
-
HEAD /fs/{path}- Get metadata: Returns file/directory metadata in response headers
- Headers:
Content-Type,Content-Length,X-File-Type,Last-Modified,X-Path - Returns (200): Headers only, (404): Path not found
Path Resolution: All /fs/* paths are resolved relative to /sandbox:
/fs/app/main.py→/sandbox/app/main.py/fs/tmp/test.txt→/sandbox/tmp/test.txt
Security: Paths are validated to prevent escaping the /sandbox directory. Symlinks are followed but validated to stay within /sandbox.
Filesystem examples (curl):
# Read a filecurl https://YOUR-RUN-ID.runs.apify.net/fs/app/config.json# List directory contentscurl https://YOUR-RUN-ID.runs.apify.net/fs/app# Download directory as ZIPcurl https://YOUR-RUN-ID.runs.apify.net/fs/app?download=1 -o app.zip# Upload a filecurl -X PUT https://YOUR-RUN-ID.runs.apify.net/fs/app/config.json \-H "Content-Type: application/json" \-d '{"key": "value"}'# Create a directorycurl -X POST https://YOUR-RUN-ID.runs.apify.net/fs/app/data?mkdir=1# Append to a log filecurl -X POST https://YOUR-RUN-ID.runs.apify.net/fs/app/log.txt?append=1 \-H "Content-Type: text/plain" \-d "New log entry"# Delete a filecurl -X DELETE https://YOUR-RUN-ID.runs.apify.net/fs/app/temp.txt# Delete directory recursivelycurl -X DELETE https://YOUR-RUN-ID.runs.apify.net/fs/app/temp?recursive=1# Get file metadatacurl -I https://YOUR-RUN-ID.runs.apify.net/fs/app/data.json
Upload/download files (TypeScript):
const baseUrl = 'https://YOUR-RUN-ID.runs.apify.net';// Upload a fileconst uploadResponse = await fetch(`${baseUrl}/fs/app/document.pdf`, {method: 'PUT',headers: { 'Content-Type': 'application/pdf' },body: pdfBuffer, // File buffer or Blob});// Download a fileconst downloadResponse = await fetch(`${baseUrl}/fs/app/document.pdf`);const fileBlob = await downloadResponse.blob();// Download directory as ZIPconst zipResponse = await fetch(`${baseUrl}/fs/app?download=1`);const zipBlob = await zipResponse.blob();// List directoryconst listResponse = await fetch(`${baseUrl}/fs/app`);const { entries } = await listResponse.json();console.log(entries); // [{ name, type, size }, ...]// Create project structureawait fetch(`${baseUrl}/fs/project/src?mkdir=1`, { method: 'POST' });await fetch(`${baseUrl}/fs/project/tests?mkdir=1`, { method: 'POST' });await fetch(`${baseUrl}/fs/project/README.md`, {method: 'PUT',body: '# My Project',});
Upload/download files (Python):
import requestsbase_url = "https://YOUR-RUN-ID.runs.apify.net"# Upload a filewith open('document.pdf', 'rb') as f:resp = requests.put(f"{base_url}/fs/app/document.pdf",data=f,headers={'Content-Type': 'application/pdf'})resp.raise_for_status()# Download a fileresp = requests.get(f"{base_url}/fs/app/document.pdf")with open('downloaded.pdf', 'wb') as f:f.write(resp.content)# Download directory as ZIPresp = requests.get(f"{base_url}/fs/app?download=1")with open('app.zip', 'wb') as f:f.write(resp.content)# List directoryresp = requests.get(f"{base_url}/fs/app")data = resp.json()for entry in data['entries']:print(f"{entry['name']} ({entry['type']}) - {entry.get('size', 'N/A')} bytes")# Create project structurerequests.post(f"{base_url}/fs/project/src?mkdir=1")requests.post(f"{base_url}/fs/project/tests?mkdir=1")requests.put(f"{base_url}/fs/project/README.md", data=b"# My Project")
Code execution examples (TypeScript/Node):
const baseUrl = 'https://YOUR-RUN-ID.runs.apify.net';// Execute Python codeconst codeRes = await fetch(`${baseUrl}/exec`, {method: 'POST',headers: { 'content-type': 'application/json' },body: JSON.stringify({command: 'print("hello from python")',language: 'py',timeoutSecs: 10,}),});console.log(await codeRes.json());// Execute shell commandconst shellRes = await fetch(`${baseUrl}/exec`, {method: 'POST',headers: { 'content-type': 'application/json' },body: JSON.stringify({command: 'ls -la',cwd: '/sandbox',timeoutSecs: 5,}),});console.log(await shellRes.json());
Code execution examples (Python):
import requestsbase_url = "https://YOUR-RUN-ID.runs.apify.net"# Execute Python codepayload = {"command": "print('hello from python')", "language": "py", "timeoutSecs": 10}resp = requests.post(f"{base_url}/exec", json=payload, timeout=15)resp.raise_for_status()print(resp.json())# Execute shell commandpayload = {"command": "ls -la", "cwd": "/sandbox", "timeoutSecs": 5}resp = requests.post(f"{base_url}/exec", json=payload, timeout=15)resp.raise_for_status()print(resp.json())
Interactive shell terminal
Open the interactive shell terminal URL from the run logs (also linked on the landing page) to work directly in the browser.
Configuration
- Memory & timeout: Configure run options to set memory allocation and execution timeout
- Idle timeout: The container automatically shuts down after a period of inactivity (default: 10 minutes). Activity includes HTTP requests and shell interaction. You can adjust this via the
idleTimeoutSecondsinput. - Recommendation: For cost efficiency, set the standard Actor Execution Timeout to 0 (infinite) in the Apify Console. The internal idle logic will then manage the lifecycle based on your usage.
- Request timeout: All requests to the Actor have a 5-minute timeout ceiling. All operations (code execution, commands, file operations) must complete within this time limit. The
timeoutparameter in requests cannot exceed this 5-minute window - Check logs: Open the Actor run log console to view connection details and operation output
Sandbox environment structure
The sandbox runs on a Debian Trixie container image with Node.js 24, Python 3, and essential development tools pre-installed.
The sandbox provides isolated execution environments for different code languages:
Code execution directories
-
Python:
/sandbox/py- Python code executes in this isolated directory
- Has access to Python virtual environment at
/sandbox/py/venv - All pip packages installed in the venv
-
JavaScript/TypeScript:
/sandbox/js-ts- JS/TS code executes in this isolated directory
- Has access to node_modules at
/sandbox/js-ts/node_modules - All npm packages installed in node_modules
-
General Commands:
/sandbox(root)- Shell commands via
/execendpoint run from sandbox root - Can access all subdirectories
- Shell commands via
Dependency installation
Specify dependencies to install via Actor input:
- Node.js Dependencies: npm packages for JS/TS code execution in native npm format
- Input as a JSON object:
{"package-name": "version", ...} - Example:
{"zod": "^3.0", "axios": "latest", "lodash": "4.17.21"}
- Input as a JSON object:
- Python Requirements: pip packages for Python code execution in requirements.txt format
- Input as multi-line text: one package per line with optional version specifiers
- Example:
requests==2.31.0pandas>=2.0.0numpy
Dependencies are installed during Actor startup before any code execution, allowing your code to immediately use them.
Customization with init script
Provide a bash script via the "Initialization Script" input to customize the sandbox:
- Runs after library installation
- Executes in
/sandboxdirectory - Can install system packages, create directories, set permissions, etc.
- Errors are logged but don't prevent Actor from starting
- Note: Init scripts have a 5-minute execution timeout
Example init scripts:
# Install system packageapt-get update && apt-get install -y curl# Create custom directory with permissionsmkdir -p /sandbox/custom-data && chmod 755 /sandbox/custom-data

