sucuri
Docs Entrar

POST /api/executions

API

Um endpoint. Você manda Python, ele roda isolado, você recebe o stdout e quanto custou. Um run não alcança nada que este deployment não tenha permitido: nenhum filesystem além de um /tmp em memória, nenhum processo, nenhum ambiente, e só os endpoints de rede que o deployment libera.

Schema para máquinas: /api/openapi.json

início rápido
curl https://sucuri.abstra.io/api/executions \
  -H "Authorization: Bearer $SUCURI_API_KEY" \
  -d '{"code": "print(sum(range(1000)))"}'

Requisição

code string obrigatório
O Python a executar.
stdin string
Texto entregue ao stdin do programa, quebrado por newline numa fila de linhas. input() tira uma linha por chamada e levanta EOFError quando a fila está vazia, como o CPython faz no fim do arquivo. O argumento opcional de prompt é escrito no stdout, como no CPython.
files {path: contents}
Arquivos escritos no /tmp antes do run, para entradas inline. Contam no mesmo orçamento maxTmpBytes contra o qual o programa escreve, então não é possível pré-encher além do limite.
mounts array
Filesystems a anexar. Veja abaixo. Sem esse campo, o run vê apenas /tmp.
maxTmpBytes integer
Teto do /tmp, em bytes. É limitado ao máximo do servidor, então só consegue estreitar. O limite vale para o total guardado, não por arquivo.
modules {dotted.name: source}
Módulos importáveis extras. É assim que você entrega código que não quer embutir em code, sem fazer deploy de nada em lugar nenhum.
net ["host:port"]
Endpoints de saída que este run pode alcançar. A correspondência é exata em host e porta: api.example.com:443 não libera subdomínio nem a porta 80. O teto é do deployment e este campo só consegue estreitá-lo; um endpoint fora do teto é descartado e recusado na hora de conectar. Sem o campo, o run recebe o teto inteiro. Quando o teto do deployment está vazio, nenhum run tem rede.
limits.timeoutMs integer
Teto de tempo de parede deste run. É limitado ao timeout do próprio deployment, então só consegue estreitá-lo.
limits.maxInstructions integer
Teto de instruções do run. Só estreita: o run para no menor entre este valor, o teto do servidor, o limite por run e o seu saldo restante, e o status volta como halted.

Montando um filesystem

Uma execução não tem filesystem próprio. Ela recebe um /tmp em memória, descartado quando termina, mais o que você montar. Um mount aponta para o seu bucket ou container e usa as suas credenciais, então o dado fica onde já está e nada é armazenado aqui. Um caminho sob nenhum mount é recusado, como toda capacidade que você não concedeu.

provider Endereçamento Credenciais Funciona com
s3 bucket, region, endpoint accessKeyId, secretAccessKey, sessionToken AWS S3, Cloudflare R2, MinIO, Backblaze B2, DigitalOcean Spaces, Wasabi
gcs bucket accessKeyId, secretAccessKey Google Cloud Storage, pela API compatível com S3 usando uma HMAC key
azure-blob account, container sasToken ou accessKey Azure Blob Storage

Campos de um mount

at string obrigatório
Caminho absoluto onde o mount aparece, por exemplo /data. Não pode ser /tmp nem estar dentro dele, não pode conter .., e não pode estar dentro de outro mount. Mounts sobrepostos são recusados em vez de resolvidos por uma regra que você teria que adivinhar.
provider s3 | gcs | azure-blob obrigatório
Com qual serviço falar.
bucket string
Nome do bucket, para s3 e gcs.
account, container string
Storage account e container, para azure-blob.
prefix string
Prefixado em toda chave, então um mount pode expor só uma subárvore do bucket. Com at: "/data" e prefix: "runs/42/", ler /data/in.csv busca a chave runs/42/in.csv.
region string
Região do SigV4. Padrão us-east-1, que é o que serviços compatíveis com S3 sem noção de região esperam ver.
endpoint string
Sobrescreve o endpoint do provedor. Necessário para R2, MinIO e B2. Tem que ser https e resolver para um endereço público; redirects não são seguidos.
readOnly boolean
Recusa toda escrita, append, remoção e rename sob este mount. Barrado antes de qualquer requisição sair deste serviço, então um mount read-only nem chega a pedir.
credentials object
Enviadas por TLS, usadas no run, e nunca armazenadas, logadas nem devolvidas. Seu código não consegue lê-las: nenhuma syscall as expõe e elas não vão para o ambiente. Omita por completo para um bucket público.

Exemplos prontos

Seu primeiro run — sem storage, sem setup

Nada para configurar: /tmp é memória, um caminho relativo cai lá porque /tmp é o working directory, e o filesystem inteiro some quando o run termina. Tudo abaixo acrescenta storage a isto.

/tmp only
{
  "code": "open('out.txt','w').write('hi')\nprint(open('out.txt').read())",
  "files": { "/tmp/in.json": "{\"n\": 1}" },
  "maxTmpBytes": 1048576
}

Alimentando o stdin

O input() tira uma linha do stdin por chamada e levanta EOFError quando não sobra nada, então um programa consegue ler até o fim da entrada como faria num terminal.

stdin
{
  "code": "total = 0\nwhile True:\n    try:\n        total += int(input())\n    except EOFError:\n        break\nprint(total)",
  "stdin": "1\n2\n3"
}

Ler do S3 e gravar o resultado de volta

Dois mounts: entradas read-only, saídas graváveis. O run nunca vê o seu bucket inteiro, só os prefixos que você montou.

s3
{
  "code": "import csv\nrows = list(csv.reader(open('/in/sales.csv')))\nopen('/out/count.txt','w').write(str(len(rows)))",
  "mounts": [
    { "at": "/in",  "provider": "s3", "bucket": "acme-data", "prefix": "2026-08/",
      "region": "us-east-1", "readOnly": true,
      "credentials": { "accessKeyId": "AKIA...", "secretAccessKey": "..." } },
    { "at": "/out", "provider": "s3", "bucket": "acme-results", "region": "us-east-1",
      "credentials": { "accessKeyId": "AKIA...", "secretAccessKey": "..." } }
  ]
}

Cloudflare R2, MinIO, Backblaze B2

Qualquer coisa que fale S3 funciona definindo endpoint. Use region "auto" no R2.

s3 + endpoint
{
  "code": "print(open('/data/model.json').read()[:80])",
  "mounts": [
    { "at": "/data", "provider": "s3", "bucket": "models", "region": "auto",
      "endpoint": "https://abc123.r2.cloudflarestorage.com", "readOnly": true,
      "credentials": { "accessKeyId": "...", "secretAccessKey": "..." } }
  ]
}

Google Cloud Storage

Pela API compatível com S3 do GCS, com uma HMAC key da sua service account. Sem fluxo OAuth para configurar.

gcs
{
  "code": "open('/bucket/out.txt','w').write('done')",
  "mounts": [
    { "at": "/bucket", "provider": "gcs", "bucket": "acme-exports",
      "credentials": { "accessKeyId": "GOOG1...", "secretAccessKey": "..." } }
  ]
}

Azure Blob Storage

Um SAS token de container é a credencial mais estreita para entregar: limite ao container e deixe expirar.

azure-blob
{
  "code": "import os\nprint(sorted(os.listdir('/models')))",
  "mounts": [
    { "at": "/models", "provider": "azure-blob", "account": "acmestore",
      "container": "models", "readOnly": true,
      "credentials": { "sasToken": "?sv=2024-11-04&se=..." } }
  ]
}

Resposta

executionId uuid obrigatório
Este run.
status completed | failed | halted obrigatório
halted significa que um limite parou o run: o teto de instruções, o timeout, o heap ou a profundidade de recursão. Seu código não consegue capturar.
stdout string obrigatório
Tudo que o programa imprimiu.
result string
O valor do último statement de expressão do code, como o repr() imprime — a regra que uma célula de notebook segue. 2 + 2 devolve "4"; x = 2 + 2 não devolve nada, porque atribuição não é expressão; print(x) não devolve nada, porque avalia para None. Ausente sempre que não houver esse valor. Um objeto de classe sua aparece pelo __repr__ dele, caindo em <ClassName object> quando não define nenhum. Limitado a 64 KiB — veja resultTruncated.
resultTruncated bool
Presente e verdadeiro quando o result bateu no teto de 64 KiB e foi cortado. O corte é só do transporte: dentro da execução o valor estava inteiro, então len(repr(x)) continua respondendo o tamanho real. Ausente significa que nada foi cortado.
resultError string
Presente quando a renderização do result levantou — um __repr__ seu que falhou. NÃO significa que a execução falhou: o programa terminou, o status é completed, e só essa renderização não deu certo. Sem ele, um __repr__ que levanta seria idêntico a uma classe que não define nenhum.
error string
O erro de Python, quando o run levantou exceção.
instructions integer obrigatório
O que é cobrado. O mesmo programa na mesma entrada sempre cobra igual, então você consegue prever e conferir.
cpuMicros integer obrigatório
Microssegundos de CPU consumidos. Um diagnóstico, não a cobrança: varia com a máquina e com quem mais estiver nela.

O Python que você recebe

Todo módulo que este sandbox consegue importar, com o que cada um exporta. A lista é gerada do próprio registro do interpretador, então ela é o que o engine em execução resolve, e não uma promessa a respeito. Importar qualquer outra coisa levanta ModuleNotFoundError, que o seu código consegue capturar. O campo `modules` da requisição acrescenta o seu próprio Python por cima. A lista nomeia o que um módulo exporta, não o que cada nome faz: um método que o motor não implementa levanta AttributeError, que o seu código também consegue capturar, então dá para sondar antes de depender. O `collections`, o mais procurado, está completo na superfície das mappings e do deque — todo método público que o CPython 3.14 dá a dict, defaultdict, Counter, OrderedDict e deque, mais os operadores deles. Um módulo precisa de aviso, e não de lista: o `random` sorteia de um gerador de verdade, mas a partir de uma semente padrão FIXA, então o mesmo programa sorteia os mesmos números em toda execução. Isso é deliberado — uma execução cujos sorteios variassem cobraria diferente a cada vez, e a contagem de instruções existe para ser previsível e auditável. O `random.seed(n)` escolhe a sequência. Use para simulação e dado de teste; nunca para segredo, token ou qualquer coisa que precise ser imprevisível.

__future__
absolute_import, annotations, division, generator_stop, nested_scopes, print_function, unicode_literals, with_statement
abc
ABC, ABCMeta, abstractmethod
asyncio
Event, Lock, Queue, Semaphore, gather, run, sleep, wait_for
base64
b64decode, b64encode
bisect
bisect, bisect_left, bisect_right, insort, insort_left, insort_right
collections
Counter, OrderedDict, defaultdict, deque, namedtuple
contextlib
Fornecido como código Python.
contextvars
ContextVar
copy
copy, deepcopy
csv
DictReader, DictWriter, reader
dataclasses
MISSING, asdict, astuple, dataclass, field, fields, is_dataclass, replace
datetime
date, datetime, time, timedelta, timezone
decimal
Decimal
enum
Enum, IntEnum, auto
fractions
Fraction
functools
partial, reduce
hashlib
md5, sha256
heapq
heapify, heappop, heappush, nlargest, nsmallest
http
client
http.client
HTTPConnection, HTTPSConnection
io
StringIO
itertools
accumulate, chain, combinations, compress, dropwhile, filterfalse, groupby, islice, pairwise, permutations, product, starmap, takewhile, zip_longest
json
dumps, loads
logging
CRITICAL, DEBUG, ERROR, Formatter, INFO, StreamHandler, WARNING, basicConfig, getLogger
math
ceil, e, factorial, floor, gcd, pi, pow, sqrt
operator
add, attrgetter, itemgetter, methodcaller, mul, sub, truediv
os
getcwd, getenv, listdir, mkdir, path, sep, stat, walk, write_text
pathlib
Path
pickle
dumps, loads
random
choice, choices, getrandbits, randbytes, randint, random, randrange, sample, seed, shuffle, uniform
re
findall, search, split, sub
shlex
join, quote, split
socket
AF_INET, AF_INET6, SOCK_DGRAM, SOCK_STREAM, socket
statistics
mean, median, mode, stdev, variance
string
Template, ascii_letters, ascii_lowercase, ascii_uppercase, digits, punctuation
struct
calcsize, pack, unpack
subprocess
check_output, run
sys
byteorder, getsizeof, maxsize
tempfile
mkdtemp
textwrap
dedent, fill, shorten, wrap
traceback
extract_tb
typing
Any, Dict, FrozenSet, Generic, List, Literal, Optional, Set, Tuple, Type, TypeVar, Union, cast, dataclass_transform, get_args, get_origin, get_type_hints, runtime_checks
typing_extensions
Any, Dict, FrozenSet, Generic, List, Literal, Optional, Set, Tuple, Type, TypeVar, Union, cast, dataclass_transform, get_args, get_origin, get_type_hints, runtime_checks
urllib
parse, request
urllib.parse
quote, unquote, urlencode, urlparse
urllib.request
Request, urlopen
weakref
WeakValueDictionary, ref

O que é levantado

BaseException
Toda classe de exceção builtin do CPython 3.14 existe aqui com o mesmo nome e com as mesmas bases, então nomear uma no except nunca custa um NameError e um handler escrito contra uma base dispara: except ArithmeticError pega um ZeroDivisionError, except LookupError pega um KeyError, e except Exception NÃO pega KeyboardInterrupt, SystemExit nem GeneratorExit. Quais delas o próprio motor levanta é outra pergunta — o resto desta seção cobre isso. ExceptionGroup e BaseExceptionGroup também existem, com .exceptions, .subgroup, .split e .derive, e o except* divide um grupo entre as clauses e relança o que nenhuma reclamou.
type(e).__name__
Capturável. Um builtin que falha levanta a classe que o CPython levanta, então o handler que você escreveria para ele dispara: [].pop() é IndexError, [1].index(9) e min([]) são ValueError, next() além do fim é StopIteration, ord('ab') e sorted([1,'a']) são TypeError. Uma falha que nenhuma regra reconhece chega como RuntimeError — a rede, para que uma falha imprevista continue capturada em vez de escapar.
TypeError: not iterable
Capturável. Um gerador é um iterável e é DRENADO onde um iterável é aceito, o que cobre map, filter, zip e enumerate, já que cada um deles devolve um gerador: sorted(map(...)), dict(zip(...)) e "".join(map(str, xs)) rodam a fonte. Drenar consome, exatamente como no CPython, então uma segunda passada pelo mesmo objeto vem vazia. Ser iterável não é ser sequência: reversed, random.choice, bisect e urlencode medem ou indexam o argumento, então recusam gerador e iterador com TypeError mesmo aceitando uma lista dos mesmos itens. O in é a exceção que NÃO drena: ele puxa só até a resposta, então 3 in gerador_infinito() retorna e o que ele não puxou continua lá. O io.StringIO também é iterável, LINHA a linha, movendo o cursor do próprio buffer — ler uma linha pelo iterador e lê-la com readline são a mesma leitura.
str.encode / bytes.decode
Capturável. Três codecs de texto estão implementados — utf-8, ascii e latin-1 — sob os apelidos que o CPython aceita para eles, e utf-8 é o padrão. Qualquer outro nome de codec levanta LookupError, que NÃO é ValueError, então um nome com erro de digitação não passa como valor ruim. Texto que o codec não representa levanta UnicodeEncodeError e bytes que ele não lê levantam UnicodeDecodeError, os dois com a mensagem do CPython e os dois capturáveis como ValueError. O argumento errors= não é lido: a falha é levantada, nunca substituída.
OSError
Capturável. Toda capacidade que o sandbox recusa e toda que falha: um caminho sob nenhum mount, uma escrita além do maxTmpBytes, um endpoint fora do teto, um subprocesso. A mensagem é a forma [Errno N] do CPython e a classe é a subclasse correspondente — PermissionError para 13, FileNotFoundError para 2, OSError puro nos demais.
ModuleNotFoundError
Capturável. Import de um módulo que este sandbox não tem. É subclasse de ImportError, então except ImportError também pega, e e.name é o módulo que faltou.
EOFError
Capturável. input() sem nada restando no stdin, exatamente onde o CPython levanta no fim do arquivo.
status: failed
Na resposta. O programa levantou e nada capturou: error carrega a exceção e stdout carrega o que foi impresso antes dela. Código que não compila também é reportado aqui, e não cobra nada.
status: halted
Não capturável. Um limite de recurso parou o run: o teto de instruções, o timeout de parede, o heap ou a profundidade de recursão. É a única coisa que o seu código não consegue capturar nem limpar depois — não há exceção, o run para. Tudo que foi executado até ali é cobrado.

Quanto custa uma chamada

A cobrança é em instruções. A interpretação conta uma por instrução, um builtin que itera conta por elemento, e uma chamada que bloqueia paga o preço fixo abaixo. Esperar em si é de graça por desenho, então uma resposta lenta não custa mais que uma rápida.

Chamada Instruções O que cobre
conectar socket50,000Abrir uma conexão: um connect de TCP, um handshake de TLS, ou um bind de UDP.
send / recv de socket10,000Um envio ou um recebimento num socket aberto, de qualquer tamanho.
leitura / escrita em mount20,000Uma ida e volta a um bucket ou container montado: ler, escrever, stat, listar.
operação em /tmp500Uma operação no /tmp em memória, que é RAM: sem rede e sem disco.
byte em /tmp1Por byte movido pelo /tmp, além do custo da operação em si.

Uma chamada é cobrada quando é tentada, tendo ela sucesso, falhando ou sendo recusada — senão um programa sondaria o sandbox de graça. Código que não compila não executa nada e não cobra nada.

Nenhum run executa mais que 100,000,000 instruções, seja qual for o saldo. Passando disso ele é parado, e tudo que ele fez até a parada é cobrado.

Respostas HTTP

400
Corpo malformado, ou um mount que não pode ser aceito: ponto de mount inválido, dois mounts sobrepostos, ou um endpoint ao qual este serviço não se conecta.
401
API key ausente ou inválida.
402
Seu saldo está zerado ou negativo. O corpo traz uma URL topUp.
503
Não conseguimos medir o run, então ele não executou e nada foi cobrado. Tente de novo.