logologo
logo
热门活动
HOT
大模型
音视频
客户价值
文档
支持与帮助
售前咨询
快手万擎(Vanchin)
沙箱服务
文档中心
沙箱服务沙箱服务 API 调用指南

沙箱服务 API 调用指南


本文档介绍如何通过 RESTful API 或 Python SDK 接入沙箱服务,包括沙箱的创建、连接、执行命令、生命周期管理,以及模板(沙箱运行环境)的创建与构建。

占位符约定

文档中出现的以下占位符需要替换成你自己的实际值:

占位符

说明

{{DOMAIN}}

服务的 API 域名(control-plane,管理沙箱/模板元数据用)

{{SANDBOX_DOMAIN}}

沙箱内部服务(envd,执行命令/文件读写用)的代理域名,跟 {{DOMAIN}} 是两个不同的域名,不能混用

{{API_KEY}}

你的 API Key

{{TEMPLATE_ID}}

沙箱模板 ID

这四个值可以在控制台的「沙箱服务-沙箱接入」页面中,选择 API Key、模板后统一获取,示例代码可以直接复制使用。

认证方式

所有接口统一使用 X-API-Key 请求头传递 API Key:

X-API-Key: {{API_KEY}}

通用响应格式

  • 成功响应:HTTP 状态码 2xx,响应体是对应接口的数据结构(见各接口的「响应结构」);部分接口(如暂停、销毁)成功后只返回状态码,没有响应体。
  • 错误响应:HTTP 状态码 4xx/5xx,响应体统一为:
{
"code": 400,
"message": "具体错误描述"
}

字段

类型

说明

code

integer

错误码,与 HTTP 状态码一致

message

string

人类可读的错误描述

常见状态码含义:

状态码

含义

200

请求成功

201

资源创建成功

202

请求已接受,正在异步处理(如构建、快照上传)

204

请求成功,无响应体

400

请求参数不合法(字段缺失、格式错误、超出限制等)

401

未认证或 API Key 无效

403

无权限访问该资源

404

资源不存在

409

资源状态冲突,暂时无法处理(详见对应接口说明)

410

资源已永久失效,需要重新创建(详见对应接口说明)

429

请求过于频繁,触发限流

500

服务端内部错误

503

服务暂时不可用(如并发配额已满)


一、沙箱生命周期管理接口

1.1 创建沙箱

[POST] /sandboxes

基于指定模板创建一个新的沙箱实例。

  • 请求 Body:

字段

类型

必填

说明

templateID

string

模板 ID 或别名

timeout

int32

存活时长(秒),从创建时刻起计时。不传时沙箱没有显式过期时间(仅受账号套餐允许的最长时长约束,见下方说明);传了则不能超过账号套餐允许的最长时长,超过会返回 400

autoPause

boolean

否,默认 false

超时后是否自动暂停(而不是销毁)

secure

boolean

是否启用安全访问。启用后,连接沙箱内部服务(执行命令等)需要额外携带创建响应里的 envdAccessToken

allow_internet_access

boolean

是否允许沙箱访问公网。传 false 效果等同于在 network.denyOut 中封禁 0.0.0.0/0

network

object

网络策略配置,见下方 network 字段说明。不传时,如果模板配置了默认网络策略会自动套用,否则不限制

metadata

object

自定义键值对(均为字符串),创建响应及查询接口会原样返回

envVars

object

沙箱内的环境变量(键值对,均为字符串)。不传时,如果模板配置了默认环境变量会自动套用

mcp

object

MCP(Model Context Protocol)相关配置,自由格式对象

nodeName

string

指定调度到某个具体节点,跳过默认调度策略;该节点不存在或不可用时创建直接失败,不会自动回退到其它节点

volumeMounts

array

需要挂载的存储卷列表,见下方 volumeMounts 字段说明。不传时,如果模板配置了默认存储挂载会自动套用

name

string

沙箱的展示名称

desc

string

沙箱的展示描述

projectId

string

调用方自己业务系统里的项目 ID,原样存储、透传,不做校验

streamLakeProjectId

string

计费/统计系统使用的项目标识,原样存储,不会在任何查询接口中返回

idlePolicy

object

空闲自动处理策略,见下方 idlePolicy 字段说明。不传时,如果模板配置了默认策略会自动套用

📌 关于「不传字段回退到模板默认值」:envVars / network / volumeMounts / idlePolicy 这几个字段,只有请求体里完全不出现这个字段(或显式传 null)才会触发「套用模板默认配置」;如果传了一个空对象/空数组(如 "envVars": {}),会被当作「我就是要传空值」,不会去看模板配置了什么。

network 对象:

字段

类型

说明

allowPublicTraffic

boolean,默认 true

沙箱对外暴露的地址是否允许匿名访问(false 则需要鉴权)

allowOut

array<string>

允许的出站 CIDR/IP 列表,优先级高于 denyOut

denyOut

array<string>

禁止的出站 CIDR/IP 列表

maskRequestHost

string

沙箱内所有出站请求统一使用的 Host 头

mode

enum

denyAll / allowAll / whitelist,粗粒度出网策略,与 allowOut/denyOut 叠加生效(不是互斥关系):denyAll 等价于 allow_internet_access=false;allowAll 是默认行为;whitelist 需要同时提供 whitelistDomains

whitelistDomains

array<string>

域名白名单,mode=whitelist 时必填

volumeMounts 数组,每项对象:

字段

类型

必填

说明

name

string

存储卷名称(需已在你的账号下注册)

path

string

挂载到沙箱内的绝对路径

subPath

string

只挂载存储卷内的某个子目录,而不是整个卷;相对路径,不能包含 ..

readOnly

boolean,默认 false

是否只读挂载

hiddenGlobs

array<string>

对沙箱隐藏的文件名 glob 匹配规则(如 *.png),命中的文件在沙箱内不可见

type

enum

kfs / oss,直接内联指定存储卷类型(不依赖预先注册),设置后下方对应字段生效

sharedStorageId / kfsPath

string

type=kfs 时必填

共享存储实例 ID / 存储内的路径

credentialId / bucket

string

type=oss 时必填

已创建的存储凭证 ID / 存储桶名称

endpoint / region

string

type=oss 时的可选覆盖项,不传则使用账号默认配置

idlePolicy 对象:

字段

类型

必填

说明

autoSleep

boolean

是否启用空闲自动处理

maxIdleTime

integer

autoSleep=true 时必填

空闲多少秒后触发处理

maxSleepTime

integer

autoSleep=true 时必填

处理后的最长保留时长(秒)

  • 请求示例
curl -X POST "https://{{DOMAIN}}/sandboxes" \
-H "X-API-Key: {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"templateID": "{{TEMPLATE_ID}}",
"timeout": 300,
"envVars": {"MY_VAR": "hello"},
"metadata": {"app": "demo"}
}'
  • 响应示例(201 Created)
{
"sandboxID": "ix0it63ws9rx20wbwp56c",
"templateID": "glsf46v0atv98q90g3or",
"clientID": "6532622b",
"alias": "my-template",
"envdVersion": "0.2.0",
"envdAccessToken": null,
"domain": "kwai-sandbox-proxy-commercial.corp.kuaishou.com",
"envVars": {"MY_VAR": "hello"},
"volumeMounts": [],
"network": {
"allowPublicTraffic": true
}
}
  • 响应结构

字段

说明

sandboxID

沙箱唯一标识,后续所有操作都基于这个 ID

templateID

创建该沙箱所用的模板 ID

clientID

已弃用字段,为兼容保留

alias

模板别名(如果有)

envdVersion

沙箱内部服务版本号

envdAccessToken

仅当请求体 secure: true 时返回,是访问沙箱内部服务需要的令牌。这个值只在创建/恢复响应里出现一次,后续查询接口不会再返回,需要自行妥善保存

domain

沙箱流量可访问的基础域名

envVars

实际生效的环境变量(回显)

volumeMounts

实际生效的存储挂载(回显)

network

实际生效的网络策略(回显)

  • 状态码

1.2 查询沙箱详情

[GET] /sandboxes/{sandboxID}

  • Path 参数

参数

说明

sandboxID

沙箱 ID

无请求体、无 query 参数。

  • 请求示例
curl "https://{{DOMAIN}}/sandboxes/{sandboxID}" \
-H "X-API-Key: {{API_KEY}}"
  • 响应示例(200 OK)
{
"sandboxID": "ix0it63ws9rx20wbwp56c",
"templateID": "glsf46v0atv98q90g3or",
"clientID": "6532622b",
"alias": "my-template",
"startedAt": "2026-09-14T10:00:00Z",
"endAt": "2026-09-14T10:05:00Z",
"envdVersion": "0.2.0",
"domain": "kwai-sandbox-proxy-commercial.corp.kuaishou.com",
"cpuCount": 2,
"memoryMB": 1024,
"diskSizeMB": 1024,
"metadata": {"app": "demo"},
"state": "running",
"envVars": {"MY_VAR": "hello"},
"volumeMounts": [],
"network": {"allowPublicTraffic": true}
}
  • 响应结构

字段

说明

sandboxID / templateID / alias / clientID

同创建响应

startedAt

沙箱本次启动时间

endAt

沙箱预计过期时间;如果创建/恢复该沙箱时始终没有指定过存活时长(timeout),此字段为 null,表示没有一个明确的到期时间点

cpuCount / memoryMB / diskSizeMB

沙箱的规格

metadata

创建时传入的自定义元数据

state

沙箱状态,running(运行中)或 paused(已暂停)

createdBy

创建者信息(仅部分接入方式下返回,否则为 null)

envVars / volumeMounts / network

同创建响应,反映当前实际生效的配置

  • 状态码

1.3 查询沙箱状态(就绪轮询)

[GET] /sandboxes/{sandboxID}/status

沙箱创建/恢复后,内部服务需要一定时间完成初始化;建议在执行命令前先轮询这个接口到 ready,避免过早发起请求失败。

  • 请求示例
curl "https://{{DOMAIN}}/sandboxes/{sandboxID}/status" \
-H "X-API-Key: {{API_KEY}}"
  • 响应示例(200 OK)
{
"state": "ready",
"createElapsedMs": 850
}
  • 响应结构

字段

说明

state

initializing(初始化中)/ ready(就绪,可以执行命令)/ broken(初始化失败,沙箱即将被回收,不要重试)

createElapsedMs

从沙箱开始创建到现在经过的毫秒数

errorMessage

仅 state=broken 时有意义,说明失败原因

  • 状态码

状态码

说明

200

查询成功

401

未认证

404

沙箱不存在

500

服务端错误

1.4 暂停沙箱

[POST] /sandboxes/{sandboxID}/pause

保存沙箱当前的文件系统和内存状态,之后可以通过「恢复沙箱」接口还原现场;暂停后的沙箱不会因为超时被自动销毁。

  • Query 参数

参数

类型

必填

说明

wait_upload

boolean

否,默认 false

传 true 时接口会阻塞到状态数据完全持久化后才返回(耗时可能达到数十秒,视沙箱内存大小而定),保证紧接着的恢复操作一定能成功;默认 false 时接口在本地状态保存完成后即返回(约几百毫秒),持久化在后台继续进行,此时立即恢复有极小概率失败,需要按第 6 节的方式重试

  • 请求示例
curl -X POST "https://{{DOMAIN}}/sandboxes/{sandboxID}/pause" \
-H "X-API-Key: {{API_KEY}}"
  • 响应

成功无响应体。

  • 状态码

状态码

说明

204

暂停成功

401

未认证

404

沙箱不存在

409

沙箱当前状态不支持暂停

500

服务端错误

1.5 恢复沙箱(推荐)

[POST] /sandboxes/{sandboxID}/connect〔推荐〕

将一个已暂停的沙箱恢复运行;如果沙箱本来就在运行中,效果等同于续期。

  • 请求 Body

字段

类型

必填

说明

timeout

int32

从当前时刻起的存活时长(秒)

  • 请求示例
curl -X POST "https://{{DOMAIN}}/sandboxes/{sandboxID}/connect" \
-H "X-API-Key: {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{"timeout": 300}'
  • 响应示例

响应体结构同「创建沙箱」接口。

  • 状态码

状态码

说明

200

沙箱本来就在运行中,已直接续期

201

沙箱已从暂停状态恢复

400

请求参数不合法

401

未认证

404

沙箱不存在

409

暂停时的状态数据仍在后台持久化中,稍后重试即可(一般数秒到数十秒内完成),可以参考文末 Python 示例第 6 步的重试写法

410

暂停时的状态数据持久化失败或长时间未完成,无法恢复,需要重新创建沙箱

500

服务端错误

1.6 恢复沙箱(不推荐)

[POST] /sandboxes/{sandboxID}/resume〔不推荐〕

功能与第 5 节的 connect 相同,额外支持「钉」到指定节点恢复;connect 是当前推荐的恢复方式,这个接口仍可正常使用,但后续可能被移除。

  • 请求 Body

字段

类型

必填

说明

timeout

int32

存活时长(秒)

nodeName

string

指定恢复到某个具体节点;该节点不存在或不可用时直接失败,不会自动回退

autoPause

boolean

已弃用字段

  • 请求示例
curl -X POST "https://{{DOMAIN}}/sandboxes/{sandboxID}/resume" \
-H "X-API-Key: {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{"timeout": 300}'
  • 状态码

同第 5 节 connect(201 表示恢复成功,不会返回 200)。

1.7 设置超时(覆盖式)

[POST] /sandboxes/{sandboxID}/timeout

以当前请求时刻为起点重新计时,不是在原有剩余时间上累加。

  • 请求 Body

字段

类型

必填

说明

timeout

int32

从当前时刻起,多少秒后过期

  • 请求示例
curl -X POST "https://{{DOMAIN}}/sandboxes/{sandboxID}/timeout" \
-H "X-API-Key: {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{"timeout": 7200}'
  • 响应

成功无响应体。

  • 状态码

状态码

说明

204

设置成功

401

未认证

404

沙箱不存在

500

服务端错误

1.8 续期保活

[POST] /sandboxes/{sandboxID}/refreshes

在沙箱当前剩余存活时间的基础上续期,语义上和「设置超时」接近,但字段名不同(duration),且请求体本身是可选的。

  • 请求 Body

字段

类型

必填

说明

duration

integer

续期秒数,最大 3600(1 小时)

  • 请求示例
curl -X POST "https://{{DOMAIN}}/sandboxes/{sandboxID}/refreshes" \
-H "X-API-Key: {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{"duration": 300}'
  • 响应

成功无响应体。

  • 状态码

状态码

说明

204

续期成功

401

未认证

404

沙箱不存在

1.9 创建快照

[POST] /sandboxes/{sandboxID}/snapshots

对正在运行的沙箱做一次不中断运行的时间点快照,可以作为新的 templateID 用于创建沙箱(数据回滚点/分支/检查点)。这条接口不接受请求体。

  • 请求示例
curl -X POST "https://{{DOMAIN}}/sandboxes/{sandboxID}/snapshots" \
-H "X-API-Key: {{API_KEY}}"
  • 响应示例(202 Accepted)
{
"snapshotId": "sn1x2y3z4a5b6c7d8e9f0",
"buildId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"createdAt": "2026-09-14T10:10:00Z",
"envVars": {"MY_VAR": "hello"},
"volumeMounts": [],
"network": {"allowPublicTraffic": true}
}
  • 响应结构

字段

说明

snapshotId

快照 ID,可直接作为 templateID 用于创建沙箱

buildId

快照对应的构建 ID,配合 snapshotId 轮询上传进度(见下方说明)

createdAt

快照捕获时刻(不是上传完成时刻)

envVars / volumeMounts / network

快照捕获那一刻源沙箱的配置

📌 注意:202 只表示本地状态捕获完成,数据持久化上传仍在后台异步进行(通常需要数十秒)。上传完成前,用这个 snapshotId 创建沙箱会失败。需要轮询第 15 节「查询构建状态」接口(把 templateID 换成这里的 snapshotId,buildID 换成这里的 buildId)直到状态变为 ready 才能使用。
  • 状态码

状态码

说明

202

已接受,正在处理

401

未认证

404

沙箱不存在

500

服务端错误

1.10 销毁沙箱

[DELETE] /sandboxes/{sandboxID}

  • 请求示例
curl -X DELETE "https://{{DOMAIN}}/sandboxes/{sandboxID}" \
-H "X-API-Key: {{API_KEY}}"
  • 响应

成功无响应体。

  • 状态码

状态码

说明

204

销毁成功

401

未认证

404

沙箱不存在

500

服务端错误


二、连接沙箱执行命令

在沙箱内执行命令(exec)走的不是上面这套接口,而是沙箱内部服务({{SANDBOX_DOMAIN}},与管理沙箱元数据用的 {{DOMAIN}} 是两个不同的域名,不能互相替代),协议也不是普通的 JSON REST,而是基于 HTTP 的流式协议(Connect 协议(https://connectrpc.com/),请求体和响应体都需要按固定格式包一层帧头,不能直接发送裸JSON。

生产环境建议直接使用 SDK(如 sandbox.commands.run(...)),SDK 内部已经处理好了这些细节,本节的 curl 示例仅用于临时调试或没有可用 SDK 的场景。

2.1 执行命令

[POST] /process.Process/Start

执行命令(Connect 协议,走 {{SANDBOX_DOMAIN}})

  • 路由方式

请求需要携带以下两个请求头,用于告知网关把请求转发给哪个沙箱:

请求头

说明

E2b-Sandbox-Id

沙箱 ID

E2b-Sandbox-Port

沙箱内部服务端口,固定为 49983

如果沙箱是以 secure: true 创建的,还需要额外携带创建响应里返回的 envdAccessToken:

请求头

说明

X-Access-Token

创建/恢复该沙箱响应中的 envdAccessToken 值

  • 请求示例
# 1. 拼一个符合协议要求的请求体:5 字节头(1 字节 flag=0x00 + 4 字节大端长度)
# 加上 JSON 内容。真正要执行的命令放进 args 的 -c 参数里。
CMD="echo hello && whoami"
BODY=$(printf '{"process":{"cmd":"/bin/bash","args":["-l","-c","%s"]},"stdin":false}' "$CMD")
LEN=$(printf '%s' "$BODY" | wc -c | tr -d ' ')
{ printf '%02x%08x' 0 "$LEN" | xxd -r -p; printf '%s' "$BODY"; } > /tmp/start_req.bin

# 2. 发送请求。Content-Type 必须是 application/connect+json。
curl -sS --max-time 15 \
-H "X-API-Key: {{API_KEY}}" \
-H "E2b-Sandbox-Id: {sandboxID}" \
-H "E2b-Sandbox-Port: 49983" \
-H "Content-Type: application/connect+json" \
-H "Connect-Protocol-Version: 1" \
--data-binary @/tmp/start_req.bin \
"https://{{SANDBOX_DOMAIN}}/process.Process/Start"
  • 响应示例

响应同样是分帧格式的一串 JSON 事件(下面省略帧头只展示内容,实际收到的是二进制流,不建议直接打印到终端,建议先落盘再过滤不可打印字符查看):

{"event":{"start":{"pid":413}}}
{"event":{"data":{"stdout":"aGVsbG8Kcm9vdAo="}}}
{"event":{"end":{"exited":true,"status":"exit status 0"}}}
  • 响应结构

事件

说明

start

命令已启动,pid 是进程号

data

命令的标准输出/标准错误片段,stdout/stderr 均为 base64 编码,需要自行解码

end

命令已结束,exited 表示是否正常退出,status 是退出状态描述

流正常结束时,末尾还会有一个空的结束标记帧,表示这次调用本身(不是命令本身)没有出错。

  • 状态码

状态码

说明

200

请求已成功建立连接(命令本身是否成功以响应流里的 end.exited/status 为准)

401

未认证,或沙箱是 secure 创建但未携带/携带了错误的 X-Access-Token

404

沙箱不存在


三、模板管理接口

模板(template)定义了沙箱的运行环境(基础镜像、预装依赖、启动命令等)。

3.1 列表模板

[GET] /templates

  • Query 参数

参数

类型

说明

keyword

string

按模板 ID/名称/别名模糊匹配(最长 128 字符)

status

array<string>

按构建状态过滤,可选值见「查询构建状态」接口的状态说明,多个值用逗号分隔

sandboxCategory

array<string>

按沙箱类型过滤:code / browser / custom,多个值用逗号分隔

type

array<string>

按模板来源过滤:system(平台预置)/ custom(自建),多个值用逗号分隔

sortBy

string,默认 createdAt

排序字段:createdAt / updatedAt

sortOrder

string,默认 asc

排序方向:asc / desc

curPage

integer

页码(从 1 开始)。与 pageSize 一起使用才会分页,都不传则返回全部匹配结果

pageSize

integer

每页数量。设置后响应会带上 X-Total-Count 响应头,表示分页前的总匹配数

includeDeleted

boolean,默认 false

是否包含已删除的模板(通过响应里的 deletedAt 字段区分)

available

boolean

按「当前是否有可用构建」过滤,不传表示都要

  • 请求示例
curl "https://{{DOMAIN}}/templates?keyword=my-python-tpl" \
-H "X-API-Key: {{API_KEY}}"
  • 响应示例(200 OK)
[
{
"templateID": "glsf46v0atv98q90g3or",
"buildID": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"cpuCount": 2,
"memoryMB": 1024,
"diskSizeMB": 1024,
"public": false,
"name": "my-python-tpl",
"sandboxCategory": "code",
"type": "custom",
"aliases": ["my-python-tpl"],
"createdAt": "2026-09-01T00:00:00Z",
"updatedAt": "2026-09-01T00:10:00Z",
"lastSpawnedAt": null,
"spawnCount": 0,
"buildCount": 1,
"envdVersion": "0.2.0",
"buildStatus": "ready",
"available": true,
"sharedStorageCount": 0
}
]
  • 响应结构(数组,每项字段)

字段

说明

templateID

模板 ID

buildID

最近一次成功构建的 ID

cpuCount / memoryMB / diskSizeMB

沙箱规格

public

是否为公开模板(团队外也可使用)

name / description

展示名称/描述

sandboxCategory

沙箱类型:code / browser / custom

type

模板来源:system(平台预置)/ custom(自建)

tags

自定义标签

aliases

别名列表,可代替 templateID 使用

createdAt / updatedAt

创建/更新时间

lastSpawnedAt

最近一次被用于创建沙箱的时间

deletedAt

软删除时间,正常模板为 null

spawnCount / buildCount

被创建沙箱次数 / 构建次数

envdVersion

内置的沙箱内部服务版本

buildStatus

最近一次构建尝试的状态:building / waiting / ready / error

errorMessage

构建失败原因,仅 buildStatus=error 时有值

available

当前是否有可用的成功构建(即用这个模板创建沙箱现在能否成功)

effectiveImageId / effectiveCpuCount / effectiveMemoryMB

当前实际生效的构建对应的镜像/规格(可能与最近一次构建尝试不同,比如正在重新构建中)

envVars / network / volumeMounts / idlePolicy / timeout

模板级别的默认配置,创建沙箱时相应字段留空会自动套用(timeout 除外,见「注册模板」一节说明)

startupCommand

模板级别的默认启动命令

sharedStorageCount

配置了多少个默认存储挂载

设置了 pageSize 时,响应头会带上:

响应头

说明

X-Total-Count

分页前的总匹配数量

  • 状态码

状态码

说明

200

查询成功

401

未认证

500

服务端错误

3.2 注册模板

[POST] /v3/templates

注册一个新模板(或复用已有别名)。这一步只是登记元数据、生成 templateID/buildID,还不会真正开始构建,需要接着调用第 14 节「触发构建」。如果 alias 已存在且属于你,会复用同一个 templateID,只产生一个新的 buildID(相当于给已有模板发起一次新的构建)。

  • 请求 Body

字段

类型

必填

说明

alias

string

模板别名,作为可读标识使用

cpuCount

integer

CPU 核数,最小 1

memoryMB

integer

内存大小(MiB),最小 128

name / description

string

展示名称/描述

imageId

string

基于已有镜像构建(需要先通过镜像管理接口创建/查看可用镜像),与不传时走 Dockerfile 构建二选一

tags

array<string>

自定义标签

startupCommand

string

模板默认启动命令

envVars

object

模板默认环境变量

network

object

模板默认网络策略,结构同「创建沙箱」接口的 network 字段

volumeMounts

array

模板默认存储挂载,结构同「创建沙箱」接口的 volumeMounts 字段

idlePolicy

object

模板默认空闲处理策略,结构同「创建沙箱」接口的 idlePolicy 字段

timeout

integer

模板默认存活时长(秒)。传了会校验是否超过账号套餐允许的最长时长,超过返回 400

projectId / streamLakeProjectId

string

同「创建沙箱」接口的同名字段

nodeName

string

指定在某个具体节点上构建

  • 请求示例
curl -X POST "https://{{DOMAIN}}/v3/templates" \
-H "X-API-Key: {{API_KEY}}" -H "Content-Type: application/json" \
-d '{"alias": "my-python-tpl", "cpuCount": 2, "memoryMB": 1024}'
  • 响应示例(202 Accepted)
{
"templateID": "5cdyt5whuantlxrnf2cf",
"buildID": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"public": false,
"aliases": ["my-python-tpl"],
"envVars": {},
"volumeMounts": [],
"network": null
}
  • 响应结构

字段

说明

templateID

模板 ID,后续构建/查询/删除都用这个

buildID

本次构建的 ID,触发构建时需要

public

是否为公开模板

aliases

别名列表

envVars / volumeMounts / network

回显本次请求提交的配置

  • 状态码

状态码

说明

202

注册成功,等待触发构建

400

请求参数不合法(包括别名冲突、超出存活时长限制等)

401

未认证

500

服务端错误

3.3 上传构建上下文(可选)

[GET] /templates/{templateID}/files/{hash}

如果构建步骤中需要用到本地文件(比如 Dockerfile 风格构建里的 COPY 指令),需要先把打包好的 tar 文件上传。这一步是幂等的:先查询内容的哈希是否已经在缓存中,命中则跳过上传。

  • Path 参数

参数

说明

templateID

模板 ID

hash

待上传文件内容的 SHA-256 哈希

  • 请求示例
# 1. 计算 tar 包内容的 hash
HASH=$(sha256sum build-context.tar | awk '{print $1}')

# 2. 查询是否已在缓存中
curl "https://{{DOMAIN}}/templates/{templateID}/files/${HASH}" \
-H "X-API-Key: {{API_KEY}}"
  • 响应示例(201 Created)
{
"present": false,
"url": "https://storage.example.com/upload-signed-url..."
}
  • 响应结构

字段

说明

present

该内容哈希是否已经在缓存中;true 时可以跳过后续上传步骤

url

上传地址(预签名 URL);仅 present=false 时需要用到

present=false 时,用这个 url 直接 PUT 文件内容完成上传:

curl -X PUT "$UPLOAD_URL" --data-binary @build-context.tar
  • 状态码

状态码

说明

201

查询成功

400

请求参数不合法

401

未认证

404

模板不存在

500

服务端错误

3.4 触发构建

[POST] /v2/templates/{templateID}/builds/{buildID}

正式开始构建。templateID/buildID 来自第 12 节「注册模板」的响应。

  • Path 参数

参数

说明

templateID

模板 ID

buildID

构建 ID

  • 请求 Body

字段

类型

必填

说明

fromImage

string

与 fromTemplate 二选一

作为构建基础的镜像标识,必须是你能访问到的镜像(平台预置的系统镜像,或你自己账号下的自建镜像)

fromTemplate

string

与 fromImage 二选一

基于某个已有模板继续构建(链式构建)

fromImageRegistry

object

如果需要从私有镜像仓库拉取而不是走平台镜像目录,在这里提供仓库认证信息;设置后 fromImage 会被当作该仓库里的镜像地址

steps

array

否,默认 []

构建步骤列表,见下方说明

startCmd

string

构建完成后,沙箱启动时执行的命令

readyCmd

string

就绪检查命令:startCmd 启动服务后,这个命令返回成功(退出码 0)才认为沙箱真正就绪,常用于轮询等待端口起来

force

boolean

否,默认 false

是否忽略缓存强制重新构建全部步骤

capabilities

object

可选的高级特性开关,不需要时不传即可

steps 数组,每项对象:

字段

类型

必填

说明

type

string

步骤类型,支持 RUN / ENV / ARG / USER / WORKDIR / COPY / ADD

args

array<string>

参数,随步骤类型不同含义不同(见下表)

filesHash

string

COPY/ADD 时必填

第 13 节上传的构建上下文哈希

force

boolean

是否忽略缓存重新执行这一步

各步骤类型的 args 含义:

类型

args 含义

RUN

单个 shell 命令字符串,如 ["pip install numpy pandas"]

ENV / ARG

一个或多个 KEY=VALUE 形式的字符串

USER

单个用户名

WORKDIR

单个路径

COPY / ADD

[源路径, 目标路径],源路径相对于上传的构建上下文

  • 请求示例
curl -X POST "https://{{DOMAIN}}/v2/templates/{templateID}/builds/{buildID}" \
-H "X-API-Key: {{API_KEY}}" -H "Content-Type: application/json" \
-d '{
"fromImage": "registry.example.com/base-images/python:3.11",
"steps": [{"type": "RUN", "args": ["pip install numpy pandas"]}],
"startCmd": "",
"readyCmd": ""
}'
  • 响应

成功无响应体,仅返回状态码。

  • 状态码

状态码

说明

202

构建请求已接受,开始异步执行

401

未认证

500

服务端错误

3.5 查询构建状态

[GET] /templates/{templateID}/builds/{buildID}/status

轮询这个接口直到状态变为 ready 或 error,用于确认构建何时完成。

  • Query 参数

参数

类型

说明

logsOffset

integer,默认 0

返回构建日志的起始偏移,用于增量获取日志

level

string

只返回该级别及以上的日志:debug / info / warn / error

  • 请求示例
curl "https://{{DOMAIN}}/templates/{templateID}/builds/{buildID}/status" \
-H "X-API-Key: {{API_KEY}}"
  • 响应示例(200 OK)
{
"templateID": "5cdyt5whuantlxrnf2cf",
"buildID": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"status": "building",
"logs": ["Step 1/3: RUN pip install numpy pandas"],
"logEntries": [
{"timestamp": "2026-09-14T10:00:01Z", "message": "Step 1/3: RUN pip install numpy pandas", "level": "info"}
]
}
  • 响应结构

字段

说明

status

构建状态:waiting(已注册,尚未开始)/ building(构建中)/ ready(构建成功,可以使用)/ error(构建失败)

reason

仅 status=error 时有值,包含 message(失败原因)和可选的 step(失败的步骤)、logEntries(相关日志)

logs

纯文本形式的构建日志(从 logsOffset 开始)

logEntries

结构化的构建日志,每条包含 timestamp/message/level/step

  • 状态码

状态码

说明

200

查询成功

401

未认证

500

服务端错误

3.6 查询构建详情(完整日志)

[GET] /templates/{templateID}/builds/{buildID}

一次性返回某次构建的完整日志,不用于轮询进行中的构建(轮询请用第 15 节)。

  • 请求示例
curl "https://{{DOMAIN}}/templates/{templateID}/builds/{buildID}" \
-H "X-API-Key: {{API_KEY}}"
  • 响应示例(200 OK)
{
"buildID": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"status": "ready",
"createdAt": "2026-09-14T10:00:00Z",
"updatedAt": "2026-09-14T10:02:30Z",
"finishedAt": "2026-09-14T10:02:30Z",
"cpuCount": 2,
"memoryMB": 1024,
"diskSizeMB": 1024,
"envdVersion": "0.2.0",
"logs": ["..."],
"logEntries": [{"timestamp": "2026-09-14T10:00:01Z", "message": "...", "level": "info"}]
}
  • 响应结构

字段

说明

buildID

构建 ID

status

同第 15 节

createdAt / updatedAt / finishedAt

创建/最近更新/完成时间

cpuCount / memoryMB / diskSizeMB

该次构建产物的规格

envdVersion

内置的沙箱内部服务版本

logs / logEntries

完整构建日志(纯文本/结构化两种形式)

  • 状态码

状态码

说明

200

查询成功

401

未认证

404

构建记录不存在

500

服务端错误

3.7 查询模板详情(含构建历史)

[GET] /templates/{templateID}

  • Query 参数

参数

说明

nextToken

分页游标,从上一页响应的 X-Next-Token 响应头获取

limit

每页返回的构建记录数量

  • 请求示例
curl "https://{{DOMAIN}}/templates/{templateID}" \
-H "X-API-Key: {{API_KEY}}"
  • 响应示例(200 OK)
{
"templateID": "5cdyt5whuantlxrnf2cf",
"public": false,
"name": "my-python-tpl",
"sandboxCategory": "custom",
"type": "custom",
"aliases": ["my-python-tpl"],
"createdAt": "2026-09-14T10:00:00Z",
"updatedAt": "2026-09-14T10:02:30Z",
"lastSpawnedAt": null,
"spawnCount": 0,
"sharedStorageCount": 0,
"builds": [
{
"buildID": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"status": "ready",
"createdAt": "2026-09-14T10:00:00Z",
"updatedAt": "2026-09-14T10:02:30Z",
"finishedAt": "2026-09-14T10:02:30Z",
"cpuCount": 2,
"memoryMB": 1024
}
]
}
  • 响应结构

模板本身的字段同第 11 节「列表模板」响应,额外增加:

字段

说明

builds

该模板的构建历史列表,每项结构同第 16 节「查询构建详情」(不含完整日志,仅摘要字段)

  • 状态码

状态码

说明

200

查询成功

401

未认证

500

服务端错误

3.8 删除模板

[DELETE]/templates/{templateID}

  • 请求示例
curl -X DELETE "https://{{DOMAIN}}/templates/{templateID}" \
-H "X-API-Key: {{API_KEY}}"
  • 响应

成功无响应体。

  • 状态码

状态码

说明

204

删除成功

401

未认证

409

有其它模板通过链式构建(fromTemplate)依赖这个模板,需要先处理下游模板再删除

500

服务端错误


四、Python SDK 完整示例

以下示例完全使用开源 e2b SDK(pip install e2b,2.10+ 版本写法),覆盖创建 / 查详情 / 查状态 / 连接执行命令(含常用的 exec / 文件读写)/ 暂停 / 恢复(推荐用 connect)/ 设置超时 / 续期保活 / 创建快照 / 销毁沙箱,以及模板管理(注册 / 构建 / 查询 / 删除)全部操作。

e2b 官方 SDK 目前未覆盖的几个接口(查询就绪状态、续期保活、创建快照、模板管理)通过 httpx 直接调用上面介绍的 RESTful 接口,复用同一套 domain / api_key。

"""
KwaiSandbox 完整接入示例(完全使用开源 e2b SDK,pip install e2b,2.10+ 版本
写法;不依赖 kwaisandbox 包)。覆盖 create/查详情/查状态/连接执行命令
(含最常用的 exec/文件读写)/pause/connect(推荐恢复)/set_timeout/
keepAlive/snapshot/kill 全部操作,以及模板管理(注册/构建/查询/删除)。

e2b 官方 SDK 目前没有覆盖的几个接口(查 envd 就绪状态 /status、续期
keepAlive /refreshes、创建快照 /snapshots、模板管理 /templates*)是这个
平台在 e2b 协议基础上加的扩展,SDK 没有对应方法,直接用 httpx 发原始
请求,复用同一套 domain/api_key,不是另起一个 client。
"""
import hashlib
import os
import time

import httpx
from e2b import Sandbox
from e2b.connection_config import ConnectionConfig
from e2b.exceptions import SandboxException

DOMAIN = "{{DOMAIN}}"
SANDBOX_DOMAIN = "{{SANDBOX_DOMAIN}}"
API_KEY = "{{API_KEY}}"

os.environ["E2B_DOMAIN"] = DOMAIN
os.environ["E2B_API_URL"] = f"https://{DOMAIN}"
# envd(执行命令/文件系统,第 4/6 步要用)走独立的 proxy 域名,不能用上面
# 那个 control-plane 域名——本平台两者证书/AccessProxy 网关注册都是分开的,
# 混用会导致 envd 的 Connect RPC 请求被打到 API 服务,报
# "no matching operation was found"(API 的 OpenAPI 校验中间件在拒绝一个
# 不在 spec/openapi.yml 里的路径)。同时 e2b 默认的
# `{port}-{sandboxID}.{domain}` 泛子域名寻址也会被 AccessProxy 拒绝,所以
# 不能用默认 domain 拼子域名,只能整个换成这个独立注册的 proxy 域名。
os.environ["E2B_SANDBOX_URL"] = f"https://{SANDBOX_DOMAIN}"
os.environ["E2B_API_KEY"] = API_KEY

# 平台扩展接口(e2b SDK 未覆盖)统一走这个 httpx client,复用同一个 domain/api_key
api = httpx.Client(base_url=f"https://{DOMAIN}", headers={"X-API-Key": API_KEY}, timeout=30)

# 1. 创建沙箱
sandbox = Sandbox.create(template="{{TEMPLATE_ID}}", timeout=300)
print("created:", sandbox.sandbox_id)

# 2. 查询沙箱详情
info = sandbox.get_info()
print("state:", info.state)

# 3. 查询沙箱状态(envd 就绪轮询;GET /sandboxes/{id}/status 是平台扩展
# 接口,e2b SDK 没有对应方法,直接调 REST)
while api.get(f"/sandboxes/{sandbox.sandbox_id}/status").json()["state"] != "ready":
time.sleep(2)

# 4. 连接沙箱后执行命令(最常用):exec + 文件读写
# 刚 create() 出来的这个 sandbox 实例已经自动带好了 envd 需要的
# E2b-Sandbox-Id/E2b-Sandbox-Port 请求头,可以直接用;如果是拿一个
# 已有 sandboxID 重新 Sandbox.connect() 连接(比如换了一个进程/脚本),
# 这两个 header 不会自动带上,要手动传,见下面"6. 恢复沙箱"的写法。
res = sandbox.commands.run("echo $HOME && whoami")
print("stdout:", res.stdout, "exit_code:", res.exit_code)

# 传环境变量 / 指定用户
res = sandbox.commands.run("env | grep MY_VAR", envs={"MY_VAR": "hello"}, user="user")

# 后台执行,拿 handle 自己控制生命周期
handle = sandbox.commands.run("sleep 30", background=True)
# ...干别的事...
handle.kill()

# 写文件 / 读文件 / 列目录
sandbox.files.write("/tmp/hello.txt", "hello from e2b sdk\n")
content = sandbox.files.read("/tmp/hello.txt")
print("file content:", content)
for entry in sandbox.files.list("/tmp"):
print(entry.name, entry.type)

# 5. 暂停沙箱(beta 接口,2.10 版本 SDK 里 pause 挂在 beta_pause 下)
sandbox.beta_pause()

# 6. 恢复沙箱(推荐:connect)。重新连接一个不是当前进程刚建出来的已有
# 沙箱时,要手动补上 E2b-Sandbox-Id/E2b-Sandbox-Port 两个 header,
# envd 的请求才能被正确路由到这台沙箱,见本文档第二章"连接沙箱执行
# 命令"一节。connect() 没有内置 409 退避重试(快照可能还在异步上传),
# 需要自己包一层重试。
for _ in range(10):
try:
sandbox = Sandbox.connect(
sandbox.sandbox_id,
timeout=300,
headers={
"E2b-Sandbox-Id": sandbox.sandbox_id,
"E2b-Sandbox-Port": str(ConnectionConfig.envd_port),
},
)
break
except SandboxException as e:
if "in progress" not in str(e):
raise
time.sleep(3)

# 备选:resume(已弃用,仅在需要钉节点恢复时用,e2b SDK 未覆盖,直接
# 调 REST:POST /sandboxes/{sandboxID}/resume -d '{"timeout":300,"nodeName":"node-7"}')

# 7. 设置超时(覆盖式,从当前时刻起重新计时)
sandbox.set_timeout(7200)

# 8. 续期保活(平台扩展接口,e2b SDK 未覆盖)
api.post(f"/sandboxes/{sandbox.sandbox_id}/refreshes", json={"duration": 300})

# 9. 创建快照(平台扩展接口,e2b SDK 未覆盖)
snap = api.post(f"/sandboxes/{sandbox.sandbox_id}/snapshots").json()
print("snapshot:", snap["snapshotId"])

# 10. 销毁沙箱
sandbox.kill()

# ---- 模板管理(e2b SDK 未覆盖以下几步,统一用 httpx 直连 REST) ----

# 11. 列表模板(可选)
templates = api.get("/templates", params={"keyword": "my-python-tpl"}).json()
print("total:", len(templates))

# 12. 注册模板(v3,走 alias 复用:alias 已存在时复用同一个 templateID,只产生新 buildID)
resp = api.post("/v3/templates", json={"alias": "my-python-tpl", "cpuCount": 2, "memoryMB": 1024}).json()
new_template_id, build_id = resp["templateID"], resp["buildID"]
print("registered:", new_template_id, build_id)

# 13. 上传 build context:先查该内容 hash 是否已在缓存里,命中则跳过上传
with open("build-context.tar", "rb") as f:
build_context = f.read()
file_hash = hashlib.sha256(build_context).hexdigest()
upload = api.get(f"/templates/{new_template_id}/files/{file_hash}").json()
# 防御性检查:这个查询本身也可能失败(比如后端 500,响应体是
# {"code":500,"message":"..."},没有 present 字段)——不能假设"没有
# present 就是已经存在,可以跳过上传",那样会把一次真正的查询失败悄悄
# 当成"缓存命中"放过,直接进入第 14 步触发构建,构建大概率会因为 layer
# 文件缺失而失败,且报错和这里完全对不上,排查成本更高。这里选择直接把
# 原始响应暴露出来,方便照着 upload 里的 code/message 去查后端,而不是
# 吞掉继续跑。
if "present" not in upload:
raise RuntimeError(f"failed to check build context upload cache: {upload}")
if not upload["present"]:
httpx.put(upload["url"], content=build_context)

# 14. 触发真正构建
api.post(
f"/v2/templates/{new_template_id}/builds/{build_id}",
json={
"fromImage": "registry.example.com/base-images/python:3.11",
"steps": [{"type": "RUN", "args": ["pip install numpy pandas"]}],
},
)

# 15. 轮询构建状态,直到 ready(building/waiting/ready/error 四种取值)
while True:
status = api.get(f"/templates/{new_template_id}/builds/{build_id}/status").json()
if status["status"] == "ready":
break
if status["status"] == "error":
raise RuntimeError("build failed")
time.sleep(3)
print("build ready")

# 16. build 详情(legacy,与 status 分开:一次性返回完整日志,不用于轮询)
build_detail = api.get(f"/templates/{new_template_id}/builds/{build_id}").json()

# 17. 模板详情(含构建历史)
detail = api.get(f"/templates/{new_template_id}").json()
print("builds:", len(detail.get("builds", [])))

# 18. 用新模板创建沙箱,验证构建产物可用
new_sandbox = Sandbox.create(template=new_template_id)
print("created:", new_sandbox.sandbox_id)
new_sandbox.kill()

# 19. 删除模板(清理;若有其它模板的 fromTemplate 还指向它会 409,需先处理下游模板)
api.delete(f"/templates/{new_template_id}")



上一篇:沙箱资源下一篇:阿里云 OSS 存储使用指南
该篇文档内容是否对您有帮助?
有帮助没帮助