Ejecuta código en más de 42 lenguajes con una sola llamada a la API. Así puedes empezar:
Obtén tu clave API
Regístrate en unsandbox.com/console para obtener tus credenciales de la API.
Haz tu primera solicitud
Envía código al endpoint de ejecución y obtén resultados al instante.
Construye algo increíble
Usa nuestra ejecución en sandbox para agentes de aprendizaje automático, playgrounds de código, herramientas educativas y más.
Descargar
# Linux x86_64
wget https://unsandbox.com/downloads/un
chmod +x un
# Move to PATH (optional)
sudo mv un /usr/local/bin/
Compilar desde el código fuente
# Requirements: GCC, libcurl, libwebsockets
sudo apt-get install build-essential libcurl4-openssl-dev libwebsockets-dev
# Compile
wget https://unsandbox.com/downloads/un.c
gcc -Wall -O2 -o un un.c -lcurl -lwebsockets
Bibliotecas SDK (un-inception)
Para lenguajes que necesitan acceso programático, usa nuestras implementaciones SDK:
# Python - download un.py
wget https://unsandbox.com/cli/inception/un.py
python un.py script.py
# JavaScript - download un.js
wget https://unsandbox.com/cli/inception/un.js
node un.js script.py
# Go - download un.go
wget https://unsandbox.com/cli/inception/un.go
go run un.go script.py
Todas las solicitudes a la API requieren autenticación con firma HMAC-SHA256 por seguridad.
Encabezados requeridos
| Header | Description |
|---|---|
Authorization |
Bearer <public_key> (your unsb-pk-xxxx key) |
X-Timestamp |
Unix timestamp in seconds (±5 min tolerance) |
X-Signature |
HMAC-SHA256 signature (lowercase hex) |
Content-Type |
application/json |
Formato de firma
HMAC-SHA256(secret_key, timestamp:METHOD:path:body)
Ejemplo de cadena de firma:
1699564800:POST:/execute:{"language":"python","code":"print('hi')"}
Tu clave secreta nunca se transmite. El servidor verifica las firmas usando tu clave cifrada.
Ejecuta cualquier archivo de código. El lenguaje se auto-detecta desde la extensión del archivo o shebang.
Uso Básico
# Python
un hello.py
# JavaScript
un app.js
# Rust (compiles and runs)
un main.rs
# C (compiles and runs)
un program.c
# Go
un main.go
Con Opciones
# Extended timeout (5 minutes)
un --ttl 300 long_script.py
# With more CPU/RAM (4 vCPUs, 8GB RAM)
un -v 4 heavy_compute.py
# Maximum resources (8 vCPUs, 16GB RAM)
un --vcpu 8 ml_training.py
Respuesta
La salida fluye directamente a tu terminal:
$ un hello.py
Hello from unsandbox!
$ echo $?
0
Equivalente API
POST /execute
{
"success": true,
"stdout": "Hello from unsandbox!\n",
"stderr": "",
"exit_code": 0,
"language": "python",
"total_time_ms": 127
}
Ejecuta fragmentos de código directamente desde la línea de comandos usando la flag -s.
Uso Básico
# Python
un -s python 'print("Hello World")'
# Bash
un -s bash 'echo "Hello" && date'
# JavaScript
un -s javascript 'console.log(Math.PI)'
# Ruby
un -s ruby 'puts 2 ** 100'
# Go (full program)
un -s go 'package main; import "fmt"; func main() { fmt.Println("Hi") }'
Código Multilínea
# Using heredoc
un -s python <<'EOF'
import json
data = {"name": "unsandbox", "version": 2}
print(json.dumps(data, indent=2))
EOF
Lenguajes Soportados
Cualquiera de los 42 lenguajes soportados funciona con -s:
un -s python 'print(42)'
un -s javascript 'console.log(42)'
un -s typescript 'console.log(42 as number)'
un -s ruby 'puts 42'
un -s php '<?php echo 42;'
un -s perl 'print 42'
un -s lua 'print(42)'
un -s rust 'fn main() { println!("42"); }'
un -s c 'int main() { printf("42"); return 0; }'
un -s cpp 'int main() { std::cout << 42; }'
un -s java 'class M { public static void main(String[] a) { System.out.println(42); }}'
un -s kotlin 'fun main() = println(42)'
un -s go 'package main; import "fmt"; func main() { fmt.Println(42) }'
un -s haskell 'main = print 42'
un -s elixir 'IO.puts 42'
un -s julia 'println(42)'
un -s r 'print(42)'
Pasa variables de entorno a tu código usando la flag -e.
Variable Única
un -e DEBUG=1 script.py
un -e API_KEY=secret app.js
Múltiples Variables
un -e DEBUG=1 -e NAME=World -e COUNT=42 script.py
Con Código en Línea
un -e NAME=Claude -s python 'import os; print(f"Hello {os.environ[\"NAME\"]}")'
un -e X=10 -e Y=20 -s bash 'echo "Sum: $((X + Y))"'
Ejemplo
# config_demo.py
import os
debug = os.environ.get('DEBUG', '0')
name = os.environ.get('NAME', 'World')
if debug == '1':
print(f"Debug mode enabled")
print(f"Hello {name}!")
Ejecútalo
$ un -e DEBUG=1 -e NAME=unsandbox config_demo.py
Debug mode enabled
Hello unsandbox!
Teletransporta archivos al sandbox. CLI usa el flag -f; la API usa el arreglo input_files.
Uso CLI
# Single file
un -f data.csv process.py
# Multiple files
un -f data.csv -f config.json -f image.png script.py
# Large files (skip confirmation)
un -f large_dataset.parquet -y ml_training.py
Límites
- Máximo de archivos
- Tamaño total máximo
- Tamaño máximo de archivo individual
# Files are placed in working directory
un -f data.csv -s python 'print(open("data.csv").read())'
# Process multiple input files
un -f input.json -f config.yaml script.py
{
"language": "python",
"code": "print(open('data.csv').read())",
"input_files": [
{
"filename": "data.csv",
"content": "bmFtZSxhZ2UKQWxpY2UsMzAKQm9iLDI1"
}
]
}
El contenido debe estar codificado en Base64.
Los archivos se colocan en el directorio de trabajo:
- Python:
open('data.csv') - Node.js:
fs.readFileSync('data.csv') - Ruby:
File.read('data.csv') - Go:
os.ReadFile("data.csv") - Bash:
cat data.csv
Teletransporta archivos fuera del sandbox. Usa -a para recopilar artefactos de /tmp/artifacts/ u obtener binarios compilados.
Recopilar Archivos Generados
# Files in /tmp/artifacts/ are collected
un -a -o ./outputs script.py
Binario precompilado
# Compile C and get the binary
un -a -o ./bin main.c
# Compile Rust and get the binary
un -a -o ./bin main.rs
# Compile Go and get the binary
un -a -o ./bin main.go
Compilación WebAssembly
# Compile to WASM (supported languages)
un --wasm -o ./wasm main.c
un --wasm -o ./wasm main.rs
Ejemplo: Generar y Recopilar
# generate_report.py
import json
import os
os.makedirs('/tmp/artifacts', exist_ok=True)
data = {"users": 100, "revenue": 50000}
with open('/tmp/artifacts/report.json', 'w') as f:
json.dump(data, f, indent=2)
print("Report generated!")
Ejecútalo
$ un -a -o ./reports generate_report.py
Report generated!
$ ls ./reports
report.json
Confianza Cero (predeterminado)
Aislamiento de red completo. Sin acceso a internet.
# Default - no network
un script.py
Semi-Confiable
Acceso a internet vía proxy de salida.
# Enable network access
un -n semitrusted script.py
# Or with sessions
un session -n semitrusted
Casos de Uso
# Zero Trust - safe for untrusted code
un -s python 'print("Isolated execution")'
# Semi-Trusted - when you need network
un -n semitrusted -s python '
import requests
r = requests.get("https://api.github.com")
print(r.status_code)
'
# Semi-Trusted session for development
un session -n semitrusted --shell bash
Iniciar una sesión de shell interactiva en un contenedor sandbox.
Listar Trabajos Activos
# Default bash shell
un session
# Choose your shell
un session --shell zsh
un session --shell fish
Sesiones REPL
# Python REPL
un session --shell python3
un session --shell ipython
un session --shell bpython
# Node.js REPL
un session --shell node
# Ruby REPL
un session --shell irb
# Other REPLs
un session --shell julia
un session --shell ghci # Haskell
un session --shell iex # Elixir
Opciones de Sesión
# With network access
un session -n semitrusted
# With persistence (can reconnect)
un session --tmux
un session --screen
# With timeout (1 hour)
un session --ttl 3600
# Full audit recording
un session --audit -o ./logs
Equivalente API
POST /sessions
{
"session_id": "sess_xyz789",
"websocket_url": "wss://api.unsandbox.com/sessions/sess_xyz789/shell",
"status": "running"
}
Listar Trabajos Activos
un session --list
Salida
Active sessions: 2
SESSION ID CONTAINER SHELL TTL STATUS
sess_abc123... unsb-vm-12345 python3 45m30s active
sess_def456... unsb-vm-67890 bash 1h2m active
Obtener Detalles de Sesión
un session --info sess_abc123
Equivalente API
GET /sessions and GET /sessions/{id}
{
"sessions": [
{
"session_id": "sess_abc123",
"status": "running",
"network_mode": "zerotrust",
"shell": "python3"
}
]
}
Reconectar a sesiones persistentes (requiere --tmux o --screen).
Crear Sesión Persistente
# With tmux (recommended)
un session --tmux
# With screen
un session --screen
Desconectar
# tmux: Press Ctrl+b then d
# screen: Press Ctrl+a then d
Descuento
# By session ID
un session --attach sess_abc123
# By container name
un session --attach unsb-vm-12345
Terminar Sesión
# Kill by ID
un session --kill sess_abc123
# Kill by container name
un session --kill unsb-vm-12345
Equivalente API
DELETE /sessions/{id}
{"status": "terminated"}
Shells y REPLs disponibles para sesiones interactivas:
Shells
bash, dash, sh, zsh, fish, ksh
tcsh, csh, elvish, xonsh, ash
REPLs
# Python
python3, bpython, ipython
# JavaScript
node
# Ruby
ruby, irb
# Other
lua, php, perl, guile, scheme
ghci, erl, iex, sbcl, clisp
r, julia, clojure
Equivalente API
GET /shells devuelve la lista completa de shells disponibles.
Congela sesiones para ahorrar recursos mientras preservas el estado.
Congelar Sesión
un session --freeze sess_abc123
Descongelar Sesión
un session --unfreeze sess_abc123
Equivalente API
POST /sessions/{id}/freeze
{"status": "frozen"}
POST /sessions/{id}/unfreeze
{"status": "running", "unfreeze_time_ms": 1250}
Crear instantáneas puntuales del estado de la sesión.
Crear Instantánea
# With name
un session --snapshot sess_abc123 --name "before-upgrade"
# Quick snapshot (auto-generated name)
un session --snapshot sess_abc123
Restaurar desde Instantánea
un session --restore snap_xyz789
Equivalente API
POST /sessions/{id}/snapshot
{
"snapshot_id": "snap_abc123",
"name": "before-upgrade",
"size_bytes": 52428800
}
POST /sessions/{id}/restore
{"status": "restored"}
Create long-running services with automatic HTTPS at *.on.unsandbox.com. TLS certificates are provisioned automatically via Let's Encrypt. Custom domains supported via CNAME.
Servicio Básico
# Simple web server
un service --name myapp --ports 80 --bootstrap "python -m http.server 80"
# With multiple ports
un service --name api --ports 80,443 --bootstrap "./start.sh"
Con Archivos
# Deploy app tarball
un service --name blog --ports 8000 \
-f app.tar.gz \
--bootstrap "tar xzf app.tar.gz && cd app && ./start.sh"
Dominios Personalizados
un service --name api --ports 80 \
--domains api.example.com,www.api.example.com \
--bootstrap "./start.sh"
Nivel de Servicio:
# Web service (default)
un service --name web --ports 80
# Minecraft server
un service --name mc --type minecraft --bootstrap ./setup.sh
# Game server types
un service --name game --type mumble
un service --name game --type teamspeak
un service --name game --type source
Equivalente API
POST /services
{
"service_id": "svc_abc123",
"name": "myapp",
"url": "https://myapp.on.unsandbox.com",
"status": "starting",
"ports": [80]
}
Listar Servicios
un service --list
Obtener Info de Servicio
un service --info svc_abc123
Congelar/Descongelar Servicio
# Freeze (auto-unfreezes on HTTP request)
un service --freeze svc_abc123
# Manual unfreeze
un service --unfreeze svc_abc123
Bloquear/Desbloquear
# Prevent accidental deletion
un service --lock svc_abc123
# Allow deletion
un service --unlock svc_abc123
Destruir Servicio
un service --destroy svc_abc123
Endpoint de API accedido
GET /services- Listar todos los serviciosGET /services/{id}- Obtener detalles de servicioPOST /services/{id}/freeze- Congelar servicioPOST /services/{id}/unfreeze- Descongelar servicioPOST /services/{id}/lock- Bloquear servicioPOST /services/{id}/unlock- Desbloquear BóvedaDELETE /services/{id}- Destruir servicio
Descongelar automáticamente
Cuando está habilitado, los servicios congelados se despiertan automáticamente al recibir una solicitud HTTP.
# Enable auto-unfreeze (default for new services)
un service --auto-unfreeze svc_abc123
# Disable auto-unfreeze
un service --no-auto-unfreeze svc_abc123
Vista de la página de congelación
Controla lo que los visitantes ven al acceder a un servicio congelado.
# Show HTML payment page (default)
un service --show-freeze-page svc_abc123
# Return JSON error instead
un service --no-show-freeze-page svc_abc123
Equivalente API
PATCH /services/{id}
# Enable auto-unfreeze
{"unfreeze_on_demand": true}
# Disable freeze page (return JSON error)
{"show_freeze_page": false}
Comportamiento de la página de congelación
show_freeze_page: true- Muestra una página HTML con opciones de pagoshow_freeze_page: false- Retorna JSON: {"error": "service_frozen"}
Usa el modo JSON para servicios API donde las respuestas HTML podrían afectar a los clientes.
Ver Registros de Arranque
# All logs
un service --logs svc_abc123
# Last 9000 lines
un service --tail svc_abc123
Ejecutar Código
# Run any command
un service --execute svc_abc123 'ls -la'
# Check service status
un service --execute svc_abc123 'systemctl status myapp'
# View application logs
un service --execute svc_abc123 'journalctl -u myapp -n 50'
Volcar Script de Arranque
# Print to stdout
un service --dump-bootstrap svc_abc123
# Save to file
un service --dump-bootstrap svc_abc123 backup.sh
Equivalente API
GET /services/{id}/logs
{"log": "Bootstrap started...\nInstalling...\nServer listening on port 80"}
POST /services/{id}/execute
{
"stdout": "...",
"stderr": "",
"exit_code": 0
}
Almacenamiento cifrado para secretos de servicio. Las variables se inyectan cuando el contenedor arranca o se descongela.
Establecer Variables de Entorno
# Using the API (PUT /services/{id}/env)
# Content-Type: text/plain
# Body: .env format
DATABASE_URL=postgres://user:pass@host/db
API_SECRET=abc123
DEBUG=false
Obtener Estado de Bóveda
GET /services/{id}/env
{
"exists": true,
"variable_count": 3,
"updated_at": "2026-01-14T12:00:00Z"
}
Exportar Bóveda Descifrada
POST /services/{id}/env/export
{"env": "DATABASE_URL=postgres://...\nAPI_SECRET=abc123\nDEBUG=false"}
Eliminar
DELETE /services/{id}/env
{"status": "deleted"}
Re-ejecutar Arranque
# Re-run existing bootstrap
un service --redeploy svc_abc123
# Redeploy with new bootstrap
un service --redeploy svc_abc123 --bootstrap ./new-setup.sh
Crear Instantánea de Servicio
# Standard snapshot (pauses briefly)
un service --snapshot svc_abc123 --name "stable-v1.0"
# Hot snapshot (no pause)
un service --snapshot svc_abc123 --hot
Equivalente API
POST /services/{id}/redeploy
{"state": "redeploying"}
POST /services/{id}/snapshot
{
"snapshot_id": "snap_def456",
"name": "stable-v1.0",
"size_bytes": 104857600
}
Listar Todas las Instantáneas
un snapshot --list
Salida
Snapshots: 3
SNAPSHOT ID NAME SOURCE SIZE CREATED
snap_a1b2-c3d4-e5f6-g7h8 before-upgrade session 512 MB 2h ago
snap_i9j0-k1l2-m3n4-o5p6 stable-v1.0 service 1.2 GB 1d ago
snap_q7r8-s9t0-u1v2-w3x4 dev-checkpoint session 256 MB 3d ago
Obtener Info de Instantánea
un snapshot --info snap_a1b2-c3d4-e5f6-g7h8
Eliminar Instantánea
un snapshot --delete snap_a1b2-c3d4-e5f6-g7h8
Equivalente API
GET /snapshots
{
"snapshots": [
{
"snapshot_id": "snap_abc123",
"name": "before-upgrade",
"source_type": "session",
"size_bytes": 52428800
}
]
}
Restaurar al Original
# Restore session from snapshot
un session --restore snap_abc123
# Restore service from snapshot
un service --restore snap_def456
Clonar a Nuevo Contenedor
POST /snapshots/{id}/clone
# Clone to new session
{
"type": "session",
"shell": "bash"
}
# Clone to new service
{
"type": "service",
"name": "cloned-app"
}
Respuesta
Clonar a sesión:
{"session_id": "sess_new123"}
Clonar a servicio:
{
"service_id": "svc_new456",
"url": "https://cloned-app.on.unsandbox.com"
}
Bloquear/Desbloquear Instantáneas
POST /snapshots/{id}/lock - Prevenir eliminación
POST /snapshots/{id}/unlock - Permitir eliminación
Todas las solicitudes a la API deben hacerse a:
https://api.unsandbox.com
La API soporta dos modos de red:
- zerotrust (default) — Sin acceso a red, ejecución completamente aislada
- semitrusted — Acceso a red mediante proxy de salida, puede acceder a internet
Los ejemplos de Kotlin a continuación requieren las siguientes bibliotecas de terceros:
OkHttpGson or kotlinx.serialization
Asegúrate de que estos paquetes estén instalados en tu entorno antes de ejecutar los ejemplos.
Ejecuta código de inmediato y espera los resultados. Ideal para scripts rápidos y uso interactivo.
Parámetros de solicitud
| Parameter | Type | Required | Description |
|---|---|---|---|
language |
string | ✓ | Programming language |
code |
string | ✓ | Source code to execute |
env |
object | Environment variables (key-value pairs) | |
network_mode |
string | "zerotrust" or "semitrusted" | |
ttl |
integer | Timeout 1-900s (default: 60) | |
return_artifact |
boolean | Return compiled binary | |
return_wasm_artifact |
boolean | Return WebAssembly binary | |
vcpu |
integer | vCPUs 1-8 (each includes 2GB RAM) | |
input_files |
array | Archivos para subir (ver Archivos de Entrada) |
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.json.JSONObject
val PUBLIC_KEY = System.getenv("UNSANDBOX_PUBLIC_KEY") ?: "unsb-pk-test-0000-0000-0001"
val SECRET_KEY = System.getenv("UNSANDBOX_SECRET_KEY") ?: "unsb-sk-test0-vault-unlck-12345"
fun signRequest(method: String, path: String, body: String): Pair<String, String> {
val timestamp = (System.currentTimeMillis() / 1000).toString()
val message = "$timestamp:$method:$path:$body"
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(SECRET_KEY.toByteArray(), "HmacSHA256"))
val hash = mac.doFinal(message.toByteArray())
val signature = hash.joinToString("") { "%02x".format(it) }
return Pair(timestamp, signature)
}
fun main() {
val client = HttpClient.newHttpClient()
val path = "/execute"
val payload = JSONObject().apply {
put("language", "python")
put("code", "print('Hello from unsandbox!')")
}.toString()
val (timestamp, signature) = signRequest("POST", path, payload)
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.unsandbox.com$path"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer $PUBLIC_KEY")
.header("X-Timestamp", timestamp)
.header("X-Signature", signature)
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
val result = JSONObject(response.body())
val exitCode = result.optInt("exit_code", 1)
if (exitCode == 0) {
println("Output: ${result.optString("stdout", "")}")
} else {
println("Error: ${result.optString("stderr", "")}")
}
}
{
"success": true,
"stdout": "Hello from unsandbox!\n",
"stderr": "",
"exit_code": 0,
"language": "python",
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"network_mode": "zerotrust",
"total_time_ms": 236
}
{
"success": false,
"stdout": "",
"stderr": "SyntaxError: invalid syntax\n",
"exit_code": 1,
"language": "python",
"job_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
"network_mode": "zerotrust",
"total_time_ms": 312
}
Submit code for execution and receive a job ID to poll for results. Best for long-running scripts or when you need to decouple submission from execution.
Parámetros de solicitud
| Parameter | Type | Required | Description |
|---|---|---|---|
language |
string | ✓ | Programming language |
code |
string | ✓ | Source code to execute |
env |
object | Environment variables (key-value pairs) | |
network_mode |
string | "zerotrust" or "semitrusted" | |
ttl |
integer | Timeout 1-900s (default: 60) | |
return_artifact |
boolean | Return compiled binary | |
return_wasm_artifact |
boolean | Return WebAssembly binary | |
vcpu |
integer | vCPUs 1-8 (each includes 2GB RAM) | |
input_files |
array | Archivos para subir (ver Archivos de Entrada) |
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.json.JSONObject
val PUBLIC_KEY = System.getenv("UNSANDBOX_PUBLIC_KEY") ?: "unsb-pk-test-0000-0000-0001"
val SECRET_KEY = System.getenv("UNSANDBOX_SECRET_KEY") ?: "unsb-sk-test0-vault-unlck-12345"
fun signRequest(method: String, path: String, body: String): Pair<String, String> {
val timestamp = (System.currentTimeMillis() / 1000).toString()
val message = "$timestamp:$method:$path:$body"
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(SECRET_KEY.toByteArray(), "HmacSHA256"))
val hash = mac.doFinal(message.toByteArray())
val signature = hash.joinToString("") { "%02x".format(it) }
return Pair(timestamp, signature)
}
fun main() {
val client = HttpClient.newHttpClient()
// Submit job
val path = "/execute/async"
val payload = JSONObject().apply {
put("language", "python")
put("code", "print('Computing...')")
put("ttl", 300)
}.toString()
val (timestamp, signature) = signRequest("POST", path, payload)
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.unsandbox.com$path"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer $PUBLIC_KEY")
.header("X-Timestamp", timestamp)
.header("X-Signature", signature)
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
val job = JSONObject(response.body())
val jobId = job.getString("job_id")
println("Job submitted: $jobId")
// Poll for results
val statusPath = "/jobs/$jobId"
val maxAttempts = 20
for (i in 0 until maxAttempts) {
Thread.sleep(1000)
val (statusTimestamp, statusSignature) = signRequest("GET", statusPath, "")
val statusRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.unsandbox.com$statusPath"))
.header("Authorization", "Bearer $PUBLIC_KEY")
.header("X-Timestamp", statusTimestamp)
.header("X-Signature", statusSignature)
.GET()
.build()
val statusResponse = client.send(statusRequest, HttpResponse.BodyHandlers.ofString())
val status = JSONObject(statusResponse.body())
val jobStatus = status.getString("status")
when (jobStatus) {
"completed" -> {
val exitCode = status.optInt("exit_code", 1)
if (exitCode == 0) {
println("Output: ${status.optString("stdout", "")}")
} else {
println("Error: ${status.optString("stderr", "")}")
}
return
}
"timeout", "cancelled" -> {
println("Job $jobStatus")
return
}
}
}
}
{
"job_id": "ba943906-4ea6-a61c-9980-445f459368d",
"status": "pending",
"message": "Job accepted for execution"
}
Execute code with automatic language detection via shebang. Send raw code as the request body with Content-Type: text/plain.
Request
- Content-Type:
text/plain - Body: Raw code with shebang (e.g.,
#!/usr/bin/env python3)
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
env |
string | URL-encoded JSON object with environment variables | |
network_mode |
string | "zerotrust" or "semitrusted" | |
ttl |
integer | Timeout 1-900s (default: 60) | |
return_artifact |
boolean | Return compiled binary | |
return_wasm_artifact |
boolean | Return WebAssembly binary |
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.json.JSONObject
val PUBLIC_KEY = System.getenv("UNSANDBOX_PUBLIC_KEY") ?: "unsb-pk-test-0000-0000-0001"
val SECRET_KEY = System.getenv("UNSANDBOX_SECRET_KEY") ?: "unsb-sk-test0-vault-unlck-12345"
fun signRequest(method: String, path: String, body: String): Pair<String, String> {
val timestamp = (System.currentTimeMillis() / 1000).toString()
val message = "$timestamp:$method:$path:$body"
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(SECRET_KEY.toByteArray(), "HmacSHA256"))
val hash = mac.doFinal(message.toByteArray())
val signature = hash.joinToString("") { "%02x".format(it) }
return Pair(timestamp, signature)
}
fun main() {
val client = HttpClient.newHttpClient()
val path = "/run"
val code = "#!/usr/bin/env python3\nprint(\"Language auto-detected!\")"
val (timestamp, signature) = signRequest("POST", path, code)
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.unsandbox.com$path"))
.header("Content-Type", "text/plain")
.header("Authorization", "Bearer $PUBLIC_KEY")
.header("X-Timestamp", timestamp)
.header("X-Signature", signature)
.POST(HttpRequest.BodyPublishers.ofString(code))
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
val result = JSONObject(response.body())
println("Detected: ${result.optString("detected_language", "unknown")}")
val exitCode = result.optInt("exit_code", 1)
if (exitCode == 0) {
println("Output: ${result.optString("stdout", "")}")
} else {
println("Error: ${result.optString("stderr", "")}")
}
}
{
"success": true,
"stdout": "Language auto-detected!\n",
"stderr": "",
"exit_code": 0,
"language": "python",
"detected_language": "python",
"job_id": "f6a7b8c9-d0e1-2345-fghi-678901234567",
"network_mode": "zerotrust"
}
Submit code with automatic language detection and receive a job ID. Combines the convenience of /run with the flexibility of async execution.
Request
- Content-Type:
text/plain - Body: Raw code with shebang (e.g.,
#!/usr/bin/env ruby)
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
env |
string | URL-encoded JSON object with environment variables | |
network_mode |
string | "zerotrust" or "semitrusted" | |
ttl |
integer | Timeout 1-900s (default: 60) | |
return_artifact |
boolean | Return compiled binary | |
return_wasm_artifact |
boolean | Return WebAssembly binary |
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.json.JSONObject
val PUBLIC_KEY = System.getenv("UNSANDBOX_PUBLIC_KEY") ?: "unsb-pk-test-0000-0000-0001"
val SECRET_KEY = System.getenv("UNSANDBOX_SECRET_KEY") ?: "unsb-sk-test0-vault-unlck-12345"
fun signRequest(method: String, path: String, body: String): Pair<String, String> {
val timestamp = (System.currentTimeMillis() / 1000).toString()
val message = "$timestamp:$method:$path:$body"
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(SECRET_KEY.toByteArray(), "HmacSHA256"))
val hash = mac.doFinal(message.toByteArray())
val signature = hash.joinToString("") { "%02x".format(it) }
return Pair(timestamp, signature)
}
fun main() {
val client = HttpClient.newHttpClient()
// Submit job
val path = "/run/async"
val code = "#!/usr/bin/env ruby\nputs \"Running async with auto-detect!\""
val (timestamp, signature) = signRequest("POST", path, code)
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.unsandbox.com$path"))
.header("Content-Type", "text/plain")
.header("Authorization", "Bearer $PUBLIC_KEY")
.header("X-Timestamp", timestamp)
.header("X-Signature", signature)
.POST(HttpRequest.BodyPublishers.ofString(code))
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
val job = JSONObject(response.body())
val jobId = job.getString("job_id")
println("Job submitted: $jobId")
// Poll for results
val statusPath = "/jobs/$jobId"
val maxAttempts = 20
for (i in 0 until maxAttempts) {
Thread.sleep(1000)
val (statusTimestamp, statusSignature) = signRequest("GET", statusPath, "")
val statusRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.unsandbox.com$statusPath"))
.header("Authorization", "Bearer $PUBLIC_KEY")
.header("X-Timestamp", statusTimestamp)
.header("X-Signature", statusSignature)
.GET()
.build()
val statusResponse = client.send(statusRequest, HttpResponse.BodyHandlers.ofString())
val status = JSONObject(statusResponse.body())
val jobStatus = status.getString("status")
when (jobStatus) {
"completed" -> {
println("Detected: ${status.optString("detected_language", "unknown")}")
val exitCode = status.optInt("exit_code", 1)
if (exitCode == 0) {
println("Output: ${status.optString("stdout", "")}")
}
return
}
"timeout", "cancelled" -> {
println("Job $jobStatus")
return
}
}
}
}
{
"job_id": "ba943906-4ea6-a61c-9980-445f459368d",
"status": "pending",
"detected_language": "python",
"message": "Job accepted for execution"
}
Via GET /jobs/{id}
{
"job_id": "a7b8c9d0-e1f2-3456-ghij-789012345678",
"status": "completed",
"created_at": "2025-01-11T12:00:00Z",
"started_at": "2025-01-11T12:00:01Z",
"completed_at": "2025-01-11T12:00:02Z",
"success": true,
"stdout": "Running async with auto-detect!\n",
"stderr": "",
"exit_code": 0,
"language": "ruby",
"network_mode": "zerotrust",
"total_time_ms": 1234
}
Check the status and results of an asynchronous job. Poll this endpoint after submitting a job via /execute/async or /run/async.
URL Parameters
| Parameter | Type | Description |
|---|---|---|
id |
string | Job ID returned from async endpoint |
Possible Status Values
pending- Job queued, waiting to executerunning- Job currently executingcompleted- Job finished successfullyfailed- Job execution failedtimeout- Job exceeded TTL limitcancelled- Job was cancelled via DELETE
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.json.JSONObject
val PUBLIC_KEY = System.getenv("UNSANDBOX_PUBLIC_KEY") ?: "unsb-pk-test-0000-0000-0001"
val SECRET_KEY = System.getenv("UNSANDBOX_SECRET_KEY") ?: "unsb-sk-test0-vault-unlck-12345"
fun signRequest(method: String, path: String, body: String): Pair<String, String> {
val timestamp = (System.currentTimeMillis() / 1000).toString()
val message = "$timestamp:$method:$path:$body"
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(SECRET_KEY.toByteArray(), "HmacSHA256"))
val hash = mac.doFinal(message.toByteArray())
val signature = hash.joinToString("") { "%02x".format(it) }
return Pair(timestamp, signature)
}
fun main() {
val client = HttpClient.newHttpClient()
val jobId = "job_1234567890_abc"
val path = "/jobs/$jobId"
val (timestamp, signature) = signRequest("GET", path, "")
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.unsandbox.com$path"))
.header("Authorization", "Bearer $PUBLIC_KEY")
.header("X-Timestamp", timestamp)
.header("X-Signature", signature)
.GET()
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() == 200) {
val job = JSONObject(response.body())
println("Status: ${job.getString("status")}")
if (job.getString("status") == "completed") {
val exitCode = job.optInt("exit_code", 1)
if (exitCode == 0) {
println("Output: ${job.optString("stdout", "")}")
}
}
} else {
println("Get job returned status: ${response.statusCode()}")
}
}
{
"job_id": "c3d4e5f6-a7b8-9012-cdef-345678901234",
"status": "completed",
"created_at": "2025-01-11T12:00:00Z",
"started_at": "2025-01-11T12:00:01Z",
"completed_at": "2025-01-11T12:00:05Z",
"success": true,
"stdout": "Hello from unsandbox!\n",
"stderr": "",
"exit_code": 0,
"language": "python",
"network_mode": "zerotrust",
"total_time_ms": 4021
}
List all active (pending or running) jobs for your API key. Useful for monitoring multiple async executions.
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.json.JSONArray
val PUBLIC_KEY = System.getenv("UNSANDBOX_PUBLIC_KEY") ?: "unsb-pk-test-0000-0000-0001"
val SECRET_KEY = System.getenv("UNSANDBOX_SECRET_KEY") ?: "unsb-sk-test0-vault-unlck-12345"
fun signRequest(method: String, path: String, body: String): Pair<String, String> {
val timestamp = (System.currentTimeMillis() / 1000).toString()
val message = "$timestamp:$method:$path:$body"
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(SECRET_KEY.toByteArray(), "HmacSHA256"))
val hash = mac.doFinal(message.toByteArray())
val signature = hash.joinToString("") { "%02x".format(it) }
return Pair(timestamp, signature)
}
fun main() {
val client = HttpClient.newHttpClient()
val path = "/jobs"
val (timestamp, signature) = signRequest("GET", path, "")
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.unsandbox.com$path"))
.header("Authorization", "Bearer $PUBLIC_KEY")
.header("X-Timestamp", timestamp)
.header("X-Signature", signature)
.GET()
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
val jobs = JSONArray(response.body())
println("Found ${jobs.length()} jobs:")
for (i in 0 until jobs.length()) {
val job = jobs.getJSONObject(i)
println("${job.getString("job_id")}: ${job.getString("status")} (${job.getString("language")})")
}
}
[
{
"job_id": "ba943906-4ea6-a61c-9980-445f459368d",
"status": "completed",
"language": "python",
"network_mode": "zerotrust",
"created_at": "2025-01-11T12:00:00Z"
},
{
"job_id": "c1d2e3f4-5678-90ab-cdef-1234567890ab",
"status": "running",
"language": "rust",
"network_mode": "zerotrust",
"created_at": "2025-01-11T12:01:00Z"
}
]
Cancel a pending or running job. If the job is already executing, it will be terminated and partial output returned.
URL Parameters
| Parameter | Type | Description |
|---|---|---|
id |
string | Job ID to cancel |
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.json.JSONObject
val PUBLIC_KEY = System.getenv("UNSANDBOX_PUBLIC_KEY") ?: "unsb-pk-test-0000-0000-0001"
val SECRET_KEY = System.getenv("UNSANDBOX_SECRET_KEY") ?: "unsb-sk-test0-vault-unlck-12345"
fun signRequest(method: String, path: String, body: String): Pair<String, String> {
val timestamp = (System.currentTimeMillis() / 1000).toString()
val message = "$timestamp:$method:$path:$body"
val mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(SECRET_KEY.toByteArray(), "HmacSHA256"))
val hash = mac.doFinal(message.toByteArray())
val signature = hash.joinToString("") { "%02x".format(it) }
return Pair(timestamp, signature)
}
fun main() {
val client = HttpClient.newHttpClient()
val jobId = "job_1234567890_abc"
val path = "/jobs/$jobId"
val (timestamp, signature) = signRequest("DELETE", path, "")
val request = HttpRequest.newBuilder()
.uri(URI.create("https://api.unsandbox.com$path"))
.header("Authorization", "Bearer $PUBLIC_KEY")
.header("X-Timestamp", timestamp)
.header("X-Signature", signature)
.DELETE()
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() == 200) {
val result = JSONObject(response.body())
println("Deleted: ${result.optString("message", "Job deleted")}")
} else {
println("Delete failed with status: ${response.statusCode()}")
}
}
{
"job_id": "ba943906-4ea6-a61c-9980-445f459368d",
"status": "cancelled",
"message": "Job cancelled",
"completed_at": "2025-01-11T12:00:30Z",
"success": true,
"stdout": " Compiling hello v0.1.0 (/tmp)\n",
"artifacts": []
}
Las imágenes son instantáneas de contenedores independientes y transferibles que sobreviven a la eliminación del contenedor. A diferencia de las instantáneas (vinculadas al ciclo de vida del contenedor), las imágenes pueden ser:
- Transferida entre claves API
- Compartida con usuarios específicos mediante confianza
- Publicada en el marketplace
- Usada para crear nuevos servicios
Niveles de visibilidad
- private — Solo el propietario puede ver/usar (predeterminado)
- unlisted — Oculta en el listado, pero se puede compartir mediante confianza
- public — Visible para todos los usuarios (marketplace)
Publicar una imagen desde un servicio o instantánea.
Cuerpo de Solicitud
| Parameter | Type | Required | Description |
|---|---|---|---|
source_type |
string | ✓ | "service" or "snapshot" |
source_id |
string | ✓ | ID of service or snapshot |
name |
string | User-friendly name | |
description |
string | Optional description |
Metadatos de Solicitud:
{
"source_type": "service",
"source_id": "unsb-service-abc123",
"name": "my-app-v1.0",
"description": "Production-ready app image"
}
Respuesta
{
"id": "unsb-image-xyz789",
"name": "my-app-v1.0",
"description": "Production-ready app image",
"source_type": "service",
"source_id": "unsb-service-abc123",
"fingerprint": "abc123def456...",
"size_bytes": 524288000,
"visibility": "private",
"locked": false,
"owner_api_key": "unsb-pk-xxxx-xxxx-xxxx-xxxx",
"created_at": "2025-01-11T12:00:00Z"
}
Listar todas las imágenes que posees o que se compartieron con tu clave API.
Endpoints relacionados
GET /images/public— Listar imágenes del marketplaceGET /images/owned— Listar solo tus imágenesGET /images/shared— Listar imágenes compartidas contigo
Respuesta
{
"images": [
{
"id": "unsb-image-xyz789",
"name": "my-app-v1.0",
"fingerprint": "abc123...",
"size_bytes": 524288000,
"visibility": "private",
"locked": false,
"created_at": "2025-01-11T12:00:00Z"
}
]
}
Obtener información detallada sobre una imagen.
Parámetros de URL
| Parameter | Type | Description |
|---|---|---|
id |
string | Image ID (unsb-image-xxxx) |
Respuesta
{
"id": "unsb-image-xyz789",
"name": "my-app-v1.0",
"description": "Production-ready app image",
"source_type": "service",
"source_id": "unsb-service-abc123",
"fingerprint": "abc123def456...",
"node": "cammy",
"size_bytes": 524288000,
"visibility": "private",
"locked": false,
"owner_api_key": "unsb-pk-xxxx-xxxx-xxxx-xxxx",
"trusted_keys": [],
"created_at": "2025-01-11T12:00:00Z"
}
Crear un nuevo servicio usando esta imagen como base.
Cuerpo de Solicitud
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Service name | |
ports |
array | Ports to expose | |
network_mode |
string | "zerotrust" or "semitrusted" | |
bootstrap |
string | Optional bootstrap script |
Metadatos de Solicitud:
{
"name": "my-app-instance",
"ports": [8080],
"network_mode": "semitrusted"
}
Respuesta
{
"service_id": "unsb-service-xyz789",
"name": "my-app-instance",
"source_image": "unsb-image-abc123",
"state": "starting"
}
Visibilidad
POST /images/{id}/visibility
{"visibility": "unlisted"}
Clonar imagen
POST /images/{id}/clone
{
"name": "my-copy-of-app",
"description": "My personal copy"
}
Bloquear/Desbloquear
POST /images/{id}/lock— Prevenir eliminaciónPOST /images/{id}/unlock— Permitir eliminación
Eliminar imagen
DELETE /images/{id}
Otorgar acceso
POST /images/{id}/grant
{
"api_key": "unsb-pk-xxxx-xxxx-xxxx-xxxx"
}
Revocar acceso
POST /images/{id}/revoke
{
"api_key": "unsb-pk-xxxx-xxxx-xxxx-xxxx"
}
Listar claves de confianza
GET /images/{id}/trusted
{
"trusted_keys": [
"unsb-pk-aaaa-bbbb-cccc-dddd",
"unsb-pk-1111-2222-3333-4444"
]
}
Transferir propiedad
POST /images/{id}/transfer
{
"to_api_key": "unsb-pk-xxxx-xxxx-xxxx-xxxx"
}
La API retorna códigos de estado HTTP estándar junto con información detallada de errores.
| Code | Status | Description |
|---|---|---|
200 |
OK | Solicitud exitosa (revisa el campo 'success' para el resultado de ejecución) |
400 |
Bad Request | Cuerpo de solicitud inválido o faltan parámetros requeridos |
401 |
Unauthorized | Clave API / firma faltante o inválida |
403 |
Forbidden | Cuenta suspendida o función no disponible |
404 |
Not Found | ID de trabajo no encontrado o expirado |
429 |
Too Many Requests | Límite de tasa excedido |
500 |
Internal Error | Error del servidor, por favor reintenta |
Formato de respuesta de error
{
"error": "Invalid API key",
"code": "INVALID_KEY",
"details": "The provided public key does not exist"
}
Los límites de tasa se aplican por clave API y varían según el plan. Consulta los precios para detalles del plan →
Encabezados de límite de tasa
Los encabezados de respuesta incluyen información de límite de tasa:
X-RateLimit-Limit— Máximo de solicitudes por minutoX-RateLimit-Remaining— Solicitudes restantesX-RateLimit-Reset— Marca de tiempo Unix cuando el límite se restablece
Cuando se exceda el límite de tasa, espera el tiempo de restablecimiento o actualiza tu plan para obtener límites más altos.
Valida tu par de claves API y obtén su configuración incluyendo límites de tasa, límites de concurrencia y estado de expiración.
Esto es útil para:
- Verificando si tu par de claves es válido
- Viendo tus límites actuales
- Construyendo interfaces de gestión de claves
- Monitoreando expiración de claves
Autenticación
Usa autenticación HMAC estándar (igual que todos los demás endpoints).
# Sign the request
TIMESTAMP=$(date +%s)
PUBLIC_KEY="unsb-pk-xxxx-xxxx-xxxx-xxxx"
SECRET_KEY="unsb-sk-xxxxx-xxxxx-xxxxx-xxxxx"
BODY="{}"
MESSAGE="${TIMESTAMP}:POST:/keys/validate:${BODY}"
SIGNATURE=$(echo -n "$MESSAGE" | openssl dgst -sha256 -hmac "$SECRET_KEY" | cut -d' ' -f2)
# Make the request
curl -X POST https://api.unsandbox.com/keys/validate \
-H "Authorization: Bearer $PUBLIC_KEY" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Signature: $SIGNATURE" \
-H "Content-Type: application/json" \
-d "$BODY"
{
"valid": true,
"public_key": "unsb-pk-xxxx-xxxx-xxxx-xxxx",
"rate_per_minute": 60,
"burst": 10,
"concurrency_limit": 5,
"expires_at": "2026-12-31T23:59:59Z",
"tier": "Tier 3"
}
Retorna todos los lenguajes de programación soportados y sus alias.
Alias Comunes
node,js→javascriptts→typescriptlisp→commonlisp
Respuesta
{
"languages": [
"python",
"javascript",
"typescript",
"ruby",
"go",
"rust",
"c",
"cpp"
],
"aliases": {
"js": "javascript",
"node": "javascript",
"ts": "typescript",
"lisp": "commonlisp"
},
"count": 42
}
Retorna todos los shells y REPL soportados para sesiones interactivas, agrupados por categoría.
Categorías
- unix — bash, dash, sh, zsh, fish, ksh
- python — python, python3, ipython, bpython
- javascript — node
- ruby — ruby, irb
- lisp — scheme, guile, sbcl, clisp, clojure
- erlang — erl, iex
- data — r, julia, sqlite3
Respuesta
{
"shells": [
"bash", "bpython", "clisp",
"clojure", "dash", "fish",
"ghci", "guile", "iex",
"ipython", "irb", "julia"
],
"categories": {
"unix": ["bash", "dash", "sh", "zsh", "fish"],
"python": ["python", "python3", "ipython", "bpython"],
"javascript": ["node"],
"ruby": ["ruby", "irb"],
"lisp": ["scheme", "guile", "sbcl", "clisp", "clojure"]
},
"count": 35
}
Endpoint simple de verificación de estado. Retorna el estado del servicio.
Respuesta
{"status": "ok"}
Obtener información sobre el pool de contenedores y el estado del sistema.
Modos de Red
- zerotrust_only — Only isolated containers
- semitrusted_only — Only network-enabled containers
- hybrid — Both types available
Respuesta
{
"mode": "pooled",
"pool_size": 288,
"allocated": 5,
"available": 280,
"spawning": 0,
"total_containers": 285,
"network_mode": "hybrid",
"network_breakdown": {
"zerotrust": {
"allocated": 2,
"available": 140,
"total": 142
},
"semitrusted": {
"allocated": 3,
"available": 140,
"services": 1,
"total": 143
}
}
}
Obtener el estado de todos los pools de contenedores registrados (escalado horizontal).
Respuesta
{
"pool_count": 2,
"total_capacity": 288,
"total_allocated": 8,
"total_available": 280,
"pools": [
{
"id": "pool_manager@cammy",
"allocated": 4,
"available": 140,
"total": 144
},
{
"id": "pool_manager@ai",
"allocated": 4,
"available": 140,
"total": 144
}
]
}
Obtener estadísticas detalladas del sistema incluyendo carga y métricas de contenedores.
Respuesta
{
"containers": 285,
"load_1min": "2.15",
"load_5min": "1.89",
"load_15min": "1.45"
}
Implementaciones SDK completas con autenticación HMAC en 42 lenguajes. Úsalas cuando necesites acceso programático desde tu propio código.
Explorar las 42 implementaciones de lenguajes →
Patrón de Uso
# Download the SDK for your language
wget https://unsandbox.com/cli/inception/un.py
# Use it in your code
# un.py provides: execute_code(), create_session(), create_service()
# Or run it as a CLI
python un.py script.py
python un.py -s python 'print("Hello")'
¿Listo para comenzar?
Obtén una clave API gratuita y comienza a ejecutar código en minutos.