跳到主要内容

实例

一个实例,就是一份租下的报价在跑一个模板。这些端点能做实例页面能做的一切。

部署实例​

curl -s -X POST "$SUPERHEAT_API/v1/instances" \
-H "Authorization: Bearer $SUPERHEAT_KEY" \
-H "Content-Type: application/json" \
-d '{
"offer_id": "5d2f9c31-8b64-4c0e-9a77-2e0f1b6a4c11",
"template_id": "b1f4a6d2-90c7-5e33-8a2b-1d7c4e0f9a56",
"disk_gb": 60,
"label": "sft-run-14",
"ssh_key_id": "7a3c1e08-42bd-4f19-9d63-5b8e0c2a1f40",
"env_overrides": [
{"key": "HF_TOKEN", "value": "hf_...", "secret": true}
]
}'
字段类型必填说明
offer_iduuid是必须仍然是 available,否则调用返回 409 OFFER_UNAVAILABLE
template_iduuid是系统模板、公开模板,或你的组织拥有的模板
disk_gb整数是介于 10 和 20000 之间,且不大于主机的 machine.disk_gb
label字符串,最多 64 个字符否显示在控制台列表里。默认为 null。
ssh_key_iduuid否用已登录会话或 personal API 密钥时:你自己的一把 SSH 密钥。用 team API 密钥时:当前任何一位组织成员的密钥 —— 绝不能是已离开成员的,包括已离开的密钥创建者本人。不传的话,实例上没有授权密钥,你就 SSH 不进去。
env_overrides{key, value, secret} 数组否按 key 匹配,叠加到模板的 env 之上。值为 null 的条目会被丢弃。最多 100 条;key 最长 256 个字符,值最长 4,096 个字符。

环境变量覆盖项会叠加在模板的 env 之上,但平台自己的覆盖层压过这两者。你无法遮蔽 JUPYTER_TOKEN、GPU_COUNT 或实例访问令牌(SUPERHEAT_ACCESS_TOKEN,以及任何携带它的模板变量)。

响应是 201 加上实例对象。计费从工作负载真正进入 running 时开始,而不是这次调用的那一刻。

部署一跑起来就在花钱

部署和启动时都会检查余额——要够整个组织跑一小时:这个实例的 GPU 加磁盘,加上所有运行中、启动中或正在创建的实例的 GPU 加磁盘,以及所有已停止实例的磁盘——但不做预留。实例运行期间 GPU 时间按秒计费,停止期间磁盘按 GB 每小时计费。余额降到 ¥0 时,运行中的实例会被自动停止;在 ¥0 或以下持续 24 小时、并且到 −¥35 或更低时,已停止和出错的实例会被销毁。见计费如何运作。

部署被拒绝时​

检查按下表的顺序进行,返回第一个不满足的那一项。被拒绝的部署什么都不会留下,也不产生任何费用:

状态码代码原因
409OFFER_UNAVAILABLE报价不存在、已下架,或那台机器上这种形态已经租完
409MACHINE_IN_MAINTENANCE机器排期停机的时间太近,来不及启动。带 next_maintenance
422DISK_TOO_LARGEdisk_gb 超过机器的磁盘总量。带 max_disk_gb
409MACHINE_DISK_EXHAUSTED机器剩下可以许诺的磁盘不够。带 available_disk_gb 和 requested_disk_gb
404TEMPLATE_NOT_FOUND模板不存在、你看不到,或已停用
422TEMPLATE_COMING_SOON模板被标记为即将推出,镜像还没有发布
422OFFER_NOT_VM_CAPABLEvm 模板配上了不支持虚拟机的机器
422TEMPLATE_CUDA_TOO_NEW模板固定了较旧的 CUDA 栈(cuda_max),而机器的 CUDA 版本更高
422TEMPLATE_ARCH_UNSUPPORTED模板声明了 x86_64 以外的架构;Superheat 的机器都是 x86_64
422TEMPLATE_REQUIRES_OWNER别的组织的模板,从带凭据的私有镜像仓库拉取;只有拥有它的组织能部署
409OFFER_NOT_PINNABLE这台机器没有在机群控制平面登记,部署无法固定到它上面。列表不会显示这种报价,一般是拿着旧的 offer_id
404SSH_KEY_NOT_FOUNDssh_key_id 不是这个凭据可以附加的密钥(见上面的 ssh_key_id),或者不存在
402INSUFFICIENT_BALANCE余额不够组织加上这个实例后跑一小时:这个实例报价的 GPU 费率加上 disk_gb 的存储费,再加上组织其他实例的每小时费用。带 balance_micro 和 required_micro
409INSTANCE_LIMIT_REACHED组织里尚未销毁的实例已有 25 个——stopped 和 error 的也算。带 limit
409OFFER_UNAVAILABLE就在你的部署预留的那一刻,最后一个空闲单元被另一次部署拿走了。什么都不会留下

每个代码该怎么处理,见错误。

实例字段​

{
"id": "e08b5f27-1c4a-4d90-b3e6-72a9d1f45c83",
"label": "sft-run-14",
"status": "creating",
"disk_gb": 60,
"price_per_hour_micro": 2290000,
"storage_price_per_gb_hour_micro": 250,
"ssh_host": "203.0.113.14",
"ssh_port": 41207,
"ssh_user": "root",
"jupyter_url": null,
"open_url": null,
"access_token": null,
"error_code": null,
"error_message": null,
"allowed_actions": ["destroy"],
"gpus_released": false,
"gpu_release": null,
"gpu_release_requested_at": null,
"hold_price_per_hour_micro": null,
"stop_hold_price_per_hour_micro": null,
"stop_releases_gpus": false,
"suspended_hold_price_per_hour_micro": null,
"created_at": "2026-07-24T11:12:03.771Z",
"started_at": null,
"status_changed_at": "2026-07-24T11:12:03.771Z",
"destroyed_at": null,
"spend_micro": 0,
"deployed_by": "you@example.com",
"deployed_via_api_key": {
"id": "0f5a1c93-6d21-4e7c-a0b8-3c9f2e1d4a77",
"name": "ci-pipeline",
"kind": "personal",
"prefix": "shk_9f3a21c4",
"revoked": false
},
"offer": { "id": "5d2f9c31-8b64-4c0e-9a77-2e0f1b6a4c11" },
"template": { "id": "b1f4a6d2-90c7-5e33-8a2b-1d7c4e0f9a56" }
}

offer 和 template 就是报价和模板里记录的对象;这里只做了缩略展示。嵌在实例里的模板,readme 和 onstart 为 null——需要时请调用 GET /v1/templates/{id}。

字段类型含义
status字符串creating、starting、running、stopping、stopped、destroying、destroyed、error 之一。见实例状态。
disk_gb整数你申请的磁盘,停止期间按 GB 每小时计费
price_per_hour_micro整数租用时从报价快照下来的费率,对应整个切片
storage_price_per_gb_hour_micro整数租用时快照下来的磁盘费率
ssh_host, ssh_port, ssh_user字符串、整数、字符串连接目标。ssh_host 是主机的公网 IP 地址,SSH 总是直连,从不经过中继。端口从主机发布的范围里分配,绝不会是 22。
jupyter_url字符串或 nullJupyterLab 的入口地址(已带令牌),等 jupyter 模板上报之后才有。主机无法从外部直接访问时,它是 relay.superheat.cloud 下的中继地址——仅在 Superheat CN 启用 superheat.cloud 域名之后。见按钮打开的地址。
open_url字符串或 null模板打开按钮指向的地址,上报之后才有。同样可能是中继地址,条件相同。打开按钮所在的端口由访问代理守护时,地址里带着 ?token=
access_token字符串或 null实例访问令牌:JupyterLab 的令牌、模板应用端口要求的 Bearer 密钥,也是模板预建账号的密码。模板用不到它时为 null,实例销毁后也为 null。见实例访问令牌
error_code, error_message字符串或 null实例为什么处于 error——例如 provision_failed、workload_completed 或 workload_gone——或者对 running、stopped 的实例来说,为什么一次停止或启动没有落地(stop_timed_out、start_timed_out、gpus_unavailable)。其他时候为 null。控制台的实例页面会用你的控制台语言解释这个代码;它认识的每个代码都列在错误码里
allowed_actions数组当前状态下 API 接受 start、stop、destroy 中的哪些
gpus_released布尔已停止实例的 GPU 是否已被释放回机器(余额用完时,在机器支持的地方:被自动停止的实例,或者你之前自己停止的实例)。为 true 时磁盘仍在,启动要看原来那几块 GPU 是否可用
gpu_release, gpu_release_requested_at字符串或 null、时间或 null平台决定释放这个实例的 GPU 时(余额用完),gpu_release_requested_at 记下决定的时间,gpu_release 说明进展:pending(机器还没确认)、released,或 unsupported(机器不能释放,GPU 继续为你保留,余额恢复为正后两者回到 null)。没有决定释放时两者都为 null
hold_price_per_hour_micro, stop_hold_price_per_hour_micro, suspended_hold_price_per_hour_micro, stop_releases_gpus整数或 null、布尔在 Superheat CN 上为 null 和 false:停止期间只收存储费,你自己停止时 GPU 总是为你保留
created_at, started_at, status_changed_at, destroyed_at时间戳started_at 在工作负载第一次运行时被写入
spend_micro整数这个实例迄今已结算的费用。它随每一次结算变动,并且和账本记录在同一个事务里写入,所以它始终等于这个实例在交易记录列表上所有扣费之和
deployed_by字符串部署它的团队成员:邮箱;只用手机号登录、没有邮箱的账户显示为带国际区号的手机号,例如 +8613800138000。用 team API 密钥部署的实例,这里显示的是密钥本身:它的名称和前缀,例如 CI deploys (shk_1a2b3c4d…)
deployed_via_api_key对象或 null部署这个实例所用的 API 密钥——id、name、kind(team 或 personal)、prefix 和 revoked——控制台部署的实例,以及在开始记录这一项之前部署的实例,为 null

列出实例​

curl -s "$SUPERHEAT_API/v1/instances" \
-H "Authorization: Bearer $SUPERHEAT_KEY"

返回当前组织的 {"items": [...]},最新的在前。除非你传 include_destroyed=true,否则已销毁的实例不在其中。

获取单个实例​

curl -s "$SUPERHEAT_API/v1/instances/e08b5f27-1c4a-4d90-b3e6-72a9d1f45c83" \
-H "Authorization: Bearer $SUPERHEAT_KEY"

未知的 id,以及属于别的组织的实例,都返回 404 INSTANCE_NOT_FOUND。

停止、启动和销毁​

请求效果允许的起始状态
POST /v1/instances/{id}/stop关掉容器,保留磁盘,并占住 GPU 槽位。状态先变为 stopping,再变为 stopped。running
POST /v1/instances/{id}/start在同一块磁盘上把已停止的实例拉回来。状态先变为 starting,再变为 running。stopped
DELETE /v1/instances/{id}拆掉实例并释放切片。状态先变为 destroying,再变为 destroyed。对已经处于 destroying 的实例,它会重新发送拆除并返回 200。除 destroyed 以外的任何状态
curl -s -X POST "$SUPERHEAT_API/v1/instances/$INSTANCE_ID/stop" \
-H "Authorization: Bearer $SUPERHEAT_KEY"

这三个都会返回处于新的过渡状态的完整实例对象——状态转换是异步完成的,所以要一直轮询到状态稳定下来。

在不允许的状态下调用其中任何一个,都会返回 409 INVALID_STATE,消息里会点明当前状态,例如 Cannot start an instance while it is running。实例的 allowed_actions 列出当前合法的操作。启动时还会重新检查余额——要够组织在这个实例运行后跑一小时——可能返回 402 INSUFFICIENT_BALANCE。

主机接不住启动时,启动也会失败。无论哪种情况,实例都会回到 stopped,磁盘都还在:

状态码代码何时
409HOST_UNREACHABLE实例所在的机器当前未连接。稍后再试
409START_REFUSED主机拒绝了恢复,或者已不再持有这个工作负载——消息会说明是哪一种。后一种情况下销毁实例并重新部署
503START_UNAVAILABLE暂时无法完成恢复。稍后重试
409CAPACITY_UNAVAILABLE实例的 GPU 在余额用完时被释放了,而原来那几块现在正被别人使用,或者在那台机器上已停止服务。稍后再试;一直不成功的话,唯一的出路是销毁它(磁盘会一起删除)后重新部署。见 GPU 被释放之后

停止和销毁会先记录下来,再发给主机,平台会一直重发,直到主机完成。主机 30 分钟后仍未确认的停止会把实例放回 running、重新按 GPU 计费,并继续重试停止;此时 error_code 为 stop_timed_out。

同一次停止、启动或销毁的每一次重发都带着同一个身份标识,而你发起的每个新请求都会得到一个新的。所以在你已经重新启动实例之后才重发的那次停止,会被识别为过时的,不会再把它停一次;平台在启动已经生效之后才重试的那次启动,也不会再被当作拒绝来回复。你自己的调用不需要做任何额外的事:如果想让拆除再发一次,就再发一次 DELETE,它会被当作一个新请求。

销毁会删掉磁盘

DELETE 是终局的:容器、磁盘以及上面的一切都没了,报价重新回到市场上。想让数据等着你,就改用停止。见停止与销毁的区别。

日志​

curl -s "$SUPERHEAT_API/v1/instances/$INSTANCE_ID/logs?tail=50" \
-H "Authorization: Bearer $SUPERHEAT_KEY"
{
"lines": [
"[2026-07-24 11:12:07] superheat-agent: pulling image superheat-cn-registry.cn-shanghai.cr.aliyuncs.com/superheat/pytorch",
"[2026-07-24 11:12:11] superheat-agent: image ready, creating container",
"[2026-07-24 11:12:13] nvidia-smi: detected 1 GPU(s), driver 570.86",
"[2026-07-24 11:12:14] sshd: listening on 0.0.0.0:41207",
"[2026-07-24 11:12:15] superheat-agent: instance is ready"
]
}
参数取值范围默认值
tail1 到 1000200

超出该范围的值会被以 422 拒绝。在工作负载第一次跑起来之前 lines 都是空的,所以 creating 状态的实例什么都不返回。没有流式端点;请轮询这一个——控制台每三秒刷新一次。见日志。

从部署到连接,端到端​

这是从一个空 shell 到一个 SSH 会话的完整路径。它假定你有 jq、已经在控制台里添加了 SSH 密钥,并且余额够这个实例跑一小时(GPU 加磁盘),再加上组织里其他实例一小时的费用。

1. 设好你的凭据。SSH 密钥属于你的账户而不是组织,所以列出它们需要会话令牌;其余一切都靠 API 密钥跑。

export SUPERHEAT_API="https://console-api.superheat.cloud"
export SUPERHEAT_KEY="shk_your_key_here"
AUTH=(-H "Authorization: Bearer $SUPERHEAT_KEY")

2. 找出 SSH 密钥的 id,用已登录浏览器里的会话令牌:

curl -s "$SUPERHEAT_API/v1/ssh-keys" \
-H "Authorization: Bearer $SESSION_TOKEN" \
| jq -r '.items[] | "\(.id) \(.name) \(.fingerprint)"'
7a3c1e08-42bd-4f19-9d63-5b8e0c2a1f40 laptop SHA256:Yx1r0oW2fS7Tq8kJ3mN4pB6vC9dE0gH2iK5lM8nP1qR
SSH_KEY_ID="7a3c1e08-42bd-4f19-9d63-5b8e0c2a1f40"

3. 挑最便宜的单卡 H100 切片:

OFFER_ID=$(curl -s "$SUPERHEAT_API/v1/offers?gpu_model=H100%20SXM&num_gpus=1&sort=price_asc" \
"${AUTH[@]}" | jq -r '.items[0].id')

4. 挑一个 SSH 模板:

TEMPLATE_ID=$(curl -s "$SUPERHEAT_API/v1/templates?tab=recommended&mode=ssh" \
"${AUTH[@]}" | jq -r '.items[0].id')

5. 部署:

INSTANCE_ID=$(curl -s -X POST "$SUPERHEAT_API/v1/instances" \
"${AUTH[@]}" -H "Content-Type: application/json" \
-d "{\"offer_id\":\"$OFFER_ID\",\"template_id\":\"$TEMPLATE_ID\",\"disk_gb\":60,\"label\":\"api-demo\",\"ssh_key_id\":\"$SSH_KEY_ID\"}" \
| jq -r '.id')

6. 等它变成 running:

while :; do
STATUS=$(curl -s "$SUPERHEAT_API/v1/instances/$INSTANCE_ID" "${AUTH[@]}" | jq -r '.status')
echo "$STATUS"
case "$STATUS" in
running) break ;;
error|destroyed) exit 1 ;;
esac
sleep 3
done

7. 直接从实例本身拼出连接命令,而不是自己去还原它——端口是按实例分配的:

curl -s "$SUPERHEAT_API/v1/instances/$INSTANCE_ID" "${AUTH[@]}" \
| jq -r '"ssh -p \(.ssh_port) \(.ssh_user)@\(.ssh_host)"'
ssh -p 41207 root@203.0.113.14

8. 连上去,看看 GPU:

ssh -p 41207 root@203.0.113.14 nvidia-smi

9. 中途歇一会儿就停止,彻底干完就销毁:

curl -s -X POST "$SUPERHEAT_API/v1/instances/$INSTANCE_ID/stop" "${AUTH[@]}" | jq -r '.status'
curl -s -X DELETE "$SUPERHEAT_API/v1/instances/$INSTANCE_ID" "${AUTH[@]}" | jq -r '.status'