Creative Lab 键帽 API
通过两个阶段,将一张照片变成机械键盘的全彩定制键帽。原型任务根据照片生成成品键帽的图像。构建任务将该图像转换为带纹理的 3D 键帽模型。它将原型任务的 ID 作为 input_task_id,并将图像的候选 ID 作为 candidate_id。
POST /openapi/creative-lab/keycap/v1/prototypePOST /openapi/creative-lab/keycap/v1/build
两个 POST 端点都需要付费计划。免费计划账户发出的请求将返回 402。
创建 Keycap prototype 任务
根据一张照片生成一张成品键帽的图像。任务的 image_urls 中保存该图像,candidate_ids 中保存其 ID,二者各有一条记录。如果你不喜欢这个设计,可以再创建一个 prototype 任务。Meshy 会对每个 prototype 任务单独收费。
要构建 3D 键帽,请将此任务的 ID 作为 input_task_id 传给 build endpoint,并将 candidate_ids 中的那条记录作为 candidate_id 传入。有关该任务各字段的说明,请参阅Keycap prototype 任务对象。
Request
# Stage 1: generate a finished-keycap design render
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype \
-X POST \
-H "Authorization: Bearer ${YOUR_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"image_url": "<your publicly accessible image url or base64-encoded data URI>"
}'
Response
{
"result": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef"
}
参数
- Name
- image_url
- Type
- string
- 必选
- Description
要转换为键帽设计的照片,可以是公开可访问的 URL,或是一个 data URI(
data:image/jpeg;base64,<your base64-encoded image data>)。图片必须是 JPG、JPEG、PNG 或 WebP 文件,且不超过 20 MB(20,000,000 字节)。每条边至少为 32 像素,图片总像素数最多为 178,956,970。超出这些限制的图片会返回
400。Meshy 会根据图像数据来检测格式,而不是依据 URL 中的文件扩展名。没有扩展名的 URL 也可以正常工作,并且 Meshy 会跟随 HTTP 重定向。Meshy 会应用 EXIF 方向信息,因此旋转过的手机照片会按照其实际显示的方向呈现。
data URI 必须使用 base64 编码,并带有图像类型,例如
data:image/png;base64,...,且解码后的图像必须是 JPEG、PNG 或 WebP 格式。20 MB 的限制适用于解码后的图像,因此 base64 文本本身可能会大约再大三分之一。
- Name
- name
- Type
- string
- Description
任务的名称,最多 100 个字符。该名称会在任务的
name字段中返回。超出长度限制会返回400。
- Name
- remove_background
- Type
- boolean
- 默认值 false
- Description
是否从 prototype 图像中移除背景。设为
true会返回一张透明的 RGBA PNG,这样你就可以将键帽放置在任意背景之上。如果 Meshy 无法移除背景,它会返回带有原背景的图像,任务仍然会成功。无论如何,build 阶段用于生成 3D 效果的头部设计都会保留其背景。build 阶段会根据
image_urls中的图像来绘制键帽底座,如果你发送了true,则不带其背景。build 阶段会将该图像以process_image_urls.composite的形式返回。
返回值
result 字段保存新创建的 prototype 任务的 ID。获取该任务或对其进行流式获取,直到 status 变为 SUCCEEDED。然后将该 ID 作为 input_task_id 传给 build endpoint,并将 candidate_ids 中的那条记录作为 candidate_id 传入。
失败情形
- Name
400 - Bad Request- Description
请求无法执行。例如:
- 缺少
image_url。 - 图像不是 JPG、JPEG、PNG 或 WebP 文件。
- 图像过小、超过 20 MB,或超过最大像素数限制。
- Meshy 无法下载该图像,例如因为该 URL 返回
404或超时。 - data URI 不是有效的 base64 编码。
- 该图像被标记为 NSFW(不适宜内容)。
错误信息会说明具体发现的问题。修复请求后再重新发送。
- 缺少
- Name
401 - Unauthorized- Description
API key 缺失或无效。请检查
Authorization请求头。
- Name
402 - Payment Required- Description
以下情况之一适用:
- 账户的积分不足。
- 该 API key、workspace 或成员已达到其积分上限。
- 该账户处于免费套餐。
请检查你的积分和套餐,然后重试。
- Name
403 - Forbidden- Description
你的团队已开启IP 过滤功能,且 Meshy 将该图像标记为涉及第三方知识产权。请发送你拥有使用权利的图像。
- Name
429 - Too Many Requests- Description
已达到以下限制之一:
- 你发送的请求数超过了速率限制所允许的数量。请等待几秒后重试。请参阅速率限制。
- 排队或正在运行的任务过多。请等待部分任务完成后再重试。
- 在没有剩余积分的无限制套餐下,你已达到可在无积分情况下运行的任务数量上限。请等待某个任务完成,或增加积分。
- Name
500 - Internal Server Error- Description
Meshy 遇到了服务器错误,例如由于内容审核(moderation)或知识产权检查未能运行,或 Meshy 无法存储该图像。Meshy 不会创建该任务,并会退还其已扣除的任何积分。请重试该请求。
创建 Keycap build 任务
根据一个成功的 prototype 任务及其候选项之一,生成带贴图的 3D keycap。相关字段请参阅 Keycap build 任务对象。
一个 build 任务会执行每一个步骤。它先根据设计生成一个 3D 模型并为其上色,然后以经过校准的默认姿态将模型嵌入并裁切到 keycap 底座上,因此你无需手动调整。最后,它会组装 keycap 并导出文件。一次 build 通常需要 3 到 7 分钟。
Request
# Stage 2: build the chosen candidate into a 3D keycap
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/build \
-X POST \
-H "Authorization: Bearer ${YOUR_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"input_task_id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
"candidate_id": "0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b",
"options": {
"base_model": "cherry-mx-1x1-r1",
"head_size_mm": 23,
"vertical_offset_mm": 0
}
}'
Response
{
"result": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af"
}
参数
- Name
- input_task_id
- Type
- string
- 必选
- Description
要构建的 prototype 任务的 ID。它必须是你的账户通过
POST /openapi/creative-lab/keycap/v1/prototype创建的任务,且状态为SUCCEEDED。如果 ID 不是你通过 API 创建的 Keycap prototype 任务之一,将返回
404,这也包括在 web app 中创建的 prototype。
- Name
- candidate_id
- Type
- string
- 必选
- Description
要构建的候选项,来自 prototype 任务的
candidate_ids数组。如果值不在该数组中,将返回400。
- Name
- name
- Type
- string
- Description
任务的名称,最多 100 个字符。它会在任务的
name字段中返回。名称过长将返回400。
options
可选的 geometry 设置。每个字段都有经过校准的默认值,因此只需发送你想更改的字段。
- Name
- base_model
- Type
- string
- 默认值 cherry-mx-1x1-r1
- Description
用于构建的 keycap 底座。目前唯一可用的值是
cherry-mx-1x1-r1,即标准的 Cherry MX profile 1u keycap。不支持自定义尺寸,其他任何值都会返回400。
- Name
- head_size_mm
- Type
- number
- 默认值 23
- Description
头部沿其最长维度的尺寸,单位为毫米,范围为
10到40。Meshy 会缩放头部,使其最长维度与该值匹配。超出范围的值将返回400。当值大于约
32.9时,Meshy 可能会缩小尺寸,以使头部符合底座的保护性占用面积限制。此时头部生成的结果可能会比你要求的更小。任务对象不会返回 Meshy 实际应用的尺寸。要检查该尺寸,请测量模型中keycap-headmesh 的包围盒。
- Name
- vertical_offset_mm
- Type
- number
- 默认值 0
- Description
在 Meshy 将头部嵌入底座之前对其施加的垂直偏移量,单位为毫米,范围为
-5到5。超出范围的值将返回400。
返回值
result 字段保存新 build 任务的 ID。获取任务或对其进行流式传输,直到 status 为 SUCCEEDED。然后从 model_urls.glb 下载 GLB 文件,并从 model_urls.obj_zip 下载 OBJ 压缩包。
Meshy 导出的 GLB 文件和 OBJ 压缩包均按真实世界比例以毫米为单位。两者均采用 Y 轴朝上的坐标系,keycap 正面朝向 +Z 方向。
失败模式
- Name
400 - Bad Request- Description
请求无法运行。例如:
input_task_id缺失或不是有效的任务 ID。- prototype 任务的状态不是
SUCCEEDED。如果仍在运行,请等待后重试。如果已失败或被取消,请创建一个新的 prototype 任务。 - prototype 任务成功了,但没有候选项。请创建一个新的 prototype 任务。
candidate_id缺失,或不在 prototype 任务的candidate_ids中。请从该数组中发送一个值。options中的某个字段超出范围,或base_model不是可用的值。
错误消息会说明发现的具体问题。
- Name
401 - Unauthorized- Description
API key 缺失或无效。请检查
Authorization请求头。
- Name
402 - Payment Required- Description
以下情况之一适用:
- 账户没有足够的积分。
- 该 API key、workspace 或成员已达到其积分限额。
- 账户处于免费计划。
请检查你的积分和计划,然后重试。
- Name
404 - Not Found- Description
input_task_id与你的账户通过 API 创建的任何 Keycap prototype 任务都不匹配。在 web app 中创建的 prototype 同样会返回404。请发送来自 prototype 创建响应中的resultID。
- Name
429 - Too Many Requests- Description
达到以下限制之一:
- 你发送的请求数超过了速率限制所允许的数量。请等待几秒后重试。参见速率限制。
- 你排队或正在运行的任务过多。请等待部分任务完成后重试。
- 在没有积分的无限制计划下,你达到了在没有积分时可运行的任务数量上限。请等待某个任务完成,或添加积分。
- Name
500 - Internal Server Error- Description
由于服务器错误,Meshy 无法创建任务。Meshy 会退还其已扣除的所有积分。请重试该请求。
获取 Keycap 任务
返回一个 prototype 或 build 任务。路径必须与任务所处的阶段匹配。通过 /prototype/:id 请求 build 任务会返回 404,通过 /build/:id 请求 prototype 任务同样会返回 404。
字段说明请参阅 Keycap prototype 任务对象 和 Keycap build 任务对象。
Request
# Prototype
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef \
-H "Authorization: Bearer ${YOUR_API_KEY}"
# Build
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/build/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af \
-H "Authorization: Bearer ${YOUR_API_KEY}"
Prototype Response
{
"id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
"type": "creative-lab-keycap-prototype",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142456000,
"started_at": 1753142460000,
"finished_at": 1753142516000,
"expires_at": 1753401716000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 12,
"image_urls": [
"https://assets.meshy.ai/***/design-1.png?Expires=***"
],
"candidate_ids": [
"0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b"
]
}
Build Response
{
"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
"type": "creative-lab-keycap-build",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142600000,
"started_at": 1753142610000,
"finished_at": 1753143050000,
"expires_at": 1753402250000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 50,
"model_urls": {
"glb": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model.glb?Expires=***",
"obj_zip": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model-obj.zip?Expires=***"
},
"process_image_urls": {
"head_design": "https://assets.meshy.ai/***/head-design.png?Expires=***",
"composite": "https://assets.meshy.ai/***/composite.png?Expires=***",
"base_canvas": "https://assets.meshy.ai/***/base-canvas.png?Expires=***"
}
}
参数
- Name
- id
- Type
- path
- Description
创建响应中返回的任务 ID。
返回值
prototype 任务对象 或 build 任务对象,具体取决于路径中指定的阶段。
失败情况
- Name
400 - Bad Request- Description
id不是一个有效的任务 ID。请发送创建响应中返回的resultID。
- Name
403 - Forbidden- Description
你的团队已开启 IP Filtering,并且 Meshy 检测到该 prototype 任务的图片涉及第三方知识产权问题。Build 任务以及 Enterprise 套餐团队不会遇到此错误。请使用你拥有使用权的图片来创建 prototype 任务。
- Name
404 - Not Found- Description
你的账户下没有该 ID 对应的 Keycap 任务,或者该任务所处的阶段与路径不匹配。请检查 ID,并使用
/prototype或/build来匹配对应的任务。
- Name
500 - Internal Server Error- Description
Meshy 无法执行知识产权检查。请重试该请求。
删除 Keycap 任务
取消一个 Keycap 任务。取消 PENDING 任务会退还其积分。IN_PROGRESS 任务被取消时不会退款,因为工作进程已经开始处理它。已处于最终状态(SUCCEEDED、FAILED 或 CANCELED)的任务无法取消。
路径必须与任务所处的阶段匹配。例如,用 build 任务的 ID 对 /prototype/:id 执行 DELETE 会返回 404。
Request
curl --request DELETE \
--url https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype/019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef \
-H "Authorization: Bearer ${YOUR_API_KEY}"
Response
// Returns 204 No Content on success (empty body).
路径参数
- Name
- id
- Type
- path
- Description
要取消的任务的 ID。
返回值
204 No Content,响应体为空。
失败情况
- Name
400 - Bad Request- Description
以下情况之一适用:
id不是有效的任务 ID。请发送创建响应中的resultID。- 该任务已处于最终状态(
SUCCEEDED、FAILED或CANCELED),因此无法取消。
- Name
404 - Not Found- Description
你的账户下不存在该 ID 对应的 Keycap 任务,或任务所处的阶段与路径不匹配。请检查 ID,并使用
/prototype或/build来匹配该任务。
- Name
500 - Internal Server Error- Description
Meshy 在取消任务时遇到了服务器错误,因此该任务可能已取消,也可能未取消。请先获取任务以检查其
status,然后再重试。
流式获取 Keycap 任务
以服务器发送事件(SSE)的形式,流式获取 Keycap 任务的更新。路径必须与任务所处的阶段匹配。如果不匹配,例如在 /prototype/:id/stream 中使用 build 任务的 ID,流会发送一个 status_code: 404 的 error 事件,然后关闭连接。
Request
curl -N https://api.meshy.ai/openapi/creative-lab/keycap/v1/build/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/stream \
-H "Authorization: Bearer ${YOUR_API_KEY}"
Response Stream
// Error event example (wrong stage or task not found)
event: error
data: {
"status_code": 404,
"message": "Task not found"
}
// Message event examples illustrate task progress.
// Every frame is the full task object; fields not yet populated are null / empty.
// The following PENDING frame is shortened to the fields that change.
event: message
data: {
"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
"progress": 0,
"status": "PENDING"
}
event: message
data: {
"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
"type": "creative-lab-keycap-build",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142600000,
"started_at": 1753142610000,
"finished_at": 1753143050000,
"expires_at": 1753402250000,
"task_error": null,
"consumed_credits": 50,
"model_urls": {
"glb": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model.glb?Expires=***",
"obj_zip": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model-obj.zip?Expires=***"
},
"process_image_urls": {
"head_design": "https://assets.meshy.ai/***/head-design.png?Expires=***"
}
}
参数
- Name
- id
- Type
- path
- Description
创建响应中返回的任务 ID。
返回值
一个由原型或构建任务对象组成的流。每个事件都包含完整的任务对象,格式与获取 Keycap 任务返回的一致。当任务处于 PENDING 或 IN_PROGRESS 状态时,其输出字段仍为空(null、[] 或 {}),且 finished_at 为 null。
如果 IP 过滤 功能拦截了任务的结果,流会在该处发送一个 error 事件,其中的 status_code 与 get 请求所返回的一致。
获取 Keycap 任务列表
返回某一阶段的 Keycap 任务列表,每次返回一页。路径决定阶段:/prototype 仅返回 prototype 任务,/build 仅返回 build 任务。
Request
# List prototype tasks
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/prototype?page_size=10 \
-H "Authorization: Bearer ${YOUR_API_KEY}"
# List build tasks
curl https://api.meshy.ai/openapi/creative-lab/keycap/v1/build?page_size=10 \
-H "Authorization: Bearer ${YOUR_API_KEY}"
Response (List Prototype Tasks)
[
{
"id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
"type": "creative-lab-keycap-prototype",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142456000,
"started_at": 1753142460000,
"finished_at": 1753142516000,
"expires_at": 1753401716000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 12,
"image_urls": [
"https://assets.meshy.ai/***/design-1.png?Expires=***"
],
"candidate_ids": [
"0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b"
]
}
]
路径参数
- Name
- stage
- Type
- path
- 必选
- Description
要列出的阶段,
prototype或build。
查询参数
- Name
- page_num
- Type
- integer
- 默认值 1
- Description
要返回的页码,范围从
1到255。0返回第一页。超出0到255的值将返回400。
- Name
- page_size
- Type
- integer
- 默认值 10
- Description
每页返回的任务数量,最多
100。101到255之间的值将返回 100 个任务,0使用默认值。超出0到255的值将返回400。
- Name
- sort_by
- Type
- string
- 默认值 -created_at
- Description
排序方式。
可用值:
-created_at:最新的排在最前+created_at:最旧的排在最前
返回值
一个数组,包含与路径中阶段相匹配的prototype 任务对象或build 任务对象。
Keycap prototype 任务对象
一个 prototype 任务会根据照片生成一张成品键帽的图像。要将该设计转为 3D 键帽,请将该任务的 ID 作为 input_task_id 传给 build endpoint,并将其 candidate_ids 中的对应条目作为 candidate_id 传入。
Properties
Example Keycap Prototype Task Object
{
"id": "019c9a4e-2b31-7f6a-8c44-5d2a87b3c1ef",
"type": "creative-lab-keycap-prototype",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142456000,
"started_at": 1753142460000,
"finished_at": 1753142516000,
"expires_at": 1753401716000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 12,
"image_urls": [
"https://assets.meshy.ai/***/design-1.png?Expires=***"
],
"candidate_ids": [
"0198c1b2-33f5-7d21-a4b7-8e9d0c1f2a3b"
]
}
- Name
- id
- Type
- string
- Description
任务 ID。请勿依赖其具体格式。
- Name
- type
- Type
- string
- Description
任务类型,恒为
creative-lab-keycap-prototype。
- Name
- name
- Type
- string
- Description
你在创建任务时发送的
name;如果没有发送,则为""。
- Name
- status
- Type
- string
- Description
任务的状态。
可用取值:
PENDING:在队列中等待IN_PROGRESS:正在生成结果SUCCEEDED:最终状态,已生成结果FAILED:最终状态,失败原因见task_errorCANCELED:最终状态,没有结果
- Name
- progress
- Type
- integer
- Description
已完成工作的百分比,范围从
0到100。任务开始前为0,成功后为100。
- Name
- created_at
- Type
- timestamp
- Description
任务创建的时间,单位为 Unix 毫秒。
- Name
- started_at
- Type
- timestamp
- Description
某个 worker 开始处理该任务的时间,单位为 Unix 毫秒。在此之前为
null。
- Name
- finished_at
- Type
- timestamp
- Description
任务到达最终状态的时间,单位为 Unix 毫秒。在此之前为
null。
- Name
- expires_at
- Type
- timestamp
- Description
任务结果过期的时间,单位为 Unix 毫秒。该时间为
finished_at之后 3 天;在任务完成之前,为created_at之后 3 天。对于可无限期保留 API 结果的 Enterprise 账户,该时间约为 100 年之后。另见资产保留期。
- Name
- preceding_tasks
- Type
- integer
- Description
任务处于
PENDING状态时在队列中的位置。1表示该任务将是下一个开始的任务。任务离开队列后为0,在列表结果中也始终为0。
- Name
- task_error
- Type
- object
- Description
任务失败的原因。除非
status为FAILED,否则为null。另见 Errors。
- Name
- consumed_credits
- Type
- integer
- Description
该任务在扣除任何退款后实际收取的积分数量。返回错误的请求不会被扣费。失败的任务为
0,因为 Meshy 会对其进行退款。取消任务仅在其处于PENDING状态时才会退款。如果 Meshy 无法读取扣费信息,get 或 stream 响应会省略该字段,而 list 请求会返回500。在这种情况下,请重试该请求。
- Name
- image_urls
- Type
- array of strings
- Description
成品键帽图像的下载 URL。任务成功后,该数组包含一个 URL;在此之前为空数组。
image_urls[i]展示的是candidate_ids[i]所对应的候选结果。这些 URL 仅用于展示。build endpoint 接收的是
candidate_ids中的某个条目,而不是 URL。这些是签名 URL,获取时无需携带Authorization请求头。它们在expires_at之前始终有效,并且再次获取该任务会返回相同的 URL。
- Name
- candidate_ids
- Type
- array of strings
- Description
候选结果的 ID,顺序与
image_urls相同。要构建某个候选结果,请在 build 请求中将其 ID 作为candidate_id发送。请勿依赖这些 ID 的具体格式。
键帽构建任务对象
构建任务会根据一个成功的原型任务及其候选项之一,生成带 texture 的 3D 键帽。它会生成 3D 模型、为其上色、将其嵌入并裁切到键帽底座上,然后组装并导出键帽。
属性
Example Keycap Build Task Object
{
"id": "019c9a52-7d18-7e2b-9f01-6e3b98c4d2af",
"type": "creative-lab-keycap-build",
"name": "",
"status": "SUCCEEDED",
"progress": 100,
"created_at": 1753142600000,
"started_at": 1753142610000,
"finished_at": 1753143050000,
"expires_at": 1753402250000,
"preceding_tasks": 0,
"task_error": null,
"consumed_credits": 50,
"model_urls": {
"glb": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model.glb?Expires=***",
"obj_zip": "https://assets.meshy.ai/***/tasks/019c9a52-7d18-7e2b-9f01-6e3b98c4d2af/output/model-obj.zip?Expires=***"
},
"process_image_urls": {
"head_design": "https://assets.meshy.ai/***/head-design.png?Expires=***",
"composite": "https://assets.meshy.ai/***/composite.png?Expires=***",
"base_canvas": "https://assets.meshy.ai/***/base-canvas.png?Expires=***"
}
}
- Name
- id
- Type
- string
- Description
任务 ID。请勿依赖其格式。
- Name
- type
- Type
- string
- Description
任务类型,始终为
creative-lab-keycap-build。
- Name
- name
- Type
- string
- Description
你创建任务时发送的
name,如果未发送则为""。
- Name
- status
- Type
- string
- Description
任务的状态。
可用值:
PENDING:在队列中等待IN_PROGRESS:正在生成结果SUCCEEDED:最终状态,附带结果FAILED:最终状态,原因见task_errorCANCELED:最终状态,无结果
- Name
- progress
- Type
- integer
- Description
已完成工作的百分比,从
0到100。任务开始前为0,成功后为100。
- Name
- created_at
- Type
- timestamp
- Description
任务创建的时间,单位为 Unix 毫秒。
- Name
- started_at
- Type
- timestamp
- Description
工作进程开始处理任务的时间,单位为 Unix 毫秒。在此之前为
null。
- Name
- finished_at
- Type
- timestamp
- Description
任务达到最终状态的时间,单位为 Unix 毫秒。在此之前为
null。
- Name
- expires_at
- Type
- timestamp
- Description
任务结果过期的时间,单位为 Unix 毫秒。该时间为
finished_at之后 3 天,或在任务完成前为created_at之后 3 天。对于会无限期保留 API 结果的企业账户,该时间约为 100 年后。参见资产保留期。
- Name
- preceding_tasks
- Type
- integer
- Description
任务处于
PENDING状态时在队列中的位置。1表示该任务将是下一个开始执行的任务。任务离开队列后为0,在列表结果中始终为0。
- Name
- task_error
- Type
- object
- Description
任务失败的原因。除非
status为FAILED,否则为null。参见Errors。
- Name
- consumed_credits
- Type
- integer
- Description
该任务在退款后实际扣除的积分数量。返回错误的请求不会被扣费。失败的任务为
0,因为 Meshy 会退还费用。仅当任务处于PENDING状态时取消才会退款。如果 Meshy 无法读取扣费信息,get 或 stream 响应会省略该字段,而 list 请求会返回500。遇到这种情况,请重试该请求。
- Name
- model_urls
- Type
- object
- Description
3D 模型的下载 URL。Meshy 导出的 GLB 文件和 OBJ 包按真实世界比例(以毫米为单位)、Y 轴朝上,键帽正面朝向 +Z 方向。
该模型包含名为
keycap-head和keycap-base的 mesh。当 Meshy 回退为底座使用图案填充时,模型还会包含一个用于轴芯凹腔的keycap-base-interiormesh。请勿假设模型一定只有两个 mesh。这些是签名 URL,因此获取时无需携带
Authorization请求头。它们在expires_at之前保持有效,在此之前再次获取该任务会返回相同的 URL。过期的 URL 无法续期,因此请在到期前下载并保存文件。- Name
glb- Type
- string
- Description
带 texture 的 GLB 文件
model.glb的下载 URL。
- Name
obj_zip- Type
- string
- Description
OBJ 包的下载 URL,这是一个包含
model.obj、model.mtl以及model.mtl所使用的 PNG texture 的 ZIP 文件。如果底座为纯色,唯一的 texture 是keycap-head.png。如果底座带有图案,该 ZIP 文件还会包含keycap-base.png。
- Name
- process_image_urls
- Type
- object
- Description
构建过程各步骤生成图片的下载 URL,按类型进行键值索引。与
model_urls一样,它们也是签名 URL,因此获取时无需携带Authorization请求头。它们在expires_at之前保持有效,再次获取该任务会返回相同的 URL。它可能包含以下键:
head_design:所选候选项的设计图像,构建任务据此生成 3D 内容。始终存在。composite:所选候选项完成后的键帽图像,底座正是根据它绘制而成。可能缺失。base_canvas:绘制完成的键帽底座。可能缺失。
Meshy 可能会新增键,因此遇到不认识的键请直接忽略。
端到端示例
该示例运行整个流程:
- 根据照片创建一个 prototype 任务。
- 获取任务,直到其
status为SUCCEEDED。 - 从
candidate_ids中选取一个候选项。 - 使用该候选项创建一个 build 任务。
- 获取 build 任务,直到其
status为SUCCEEDED。 - 从
model_urls下载 GLB 文件和 OBJ 压缩包。
该示例选择了第一个候选项。在您的应用中,可以向用户展示 image_urls 条目,让他们自行选择。所选图片在 image_urls 中的索引,就是其 ID 在 candidate_ids 中的索引。
Complete flow
#!/usr/bin/env bash
set -euo pipefail
# Requires curl and jq. Point IMAGE_PATH at a local photo, or IMAGE_URL at a public one:
# export MESHY_API_KEY=msy_...
# export IMAGE_PATH=./portrait.jpg # or: export IMAGE_URL=https://...
: "${MESHY_API_KEY:?export MESHY_API_KEY first}"
if [[ -z "${IMAGE_PATH:-}" && -z "${IMAGE_URL:-}" ]]; then
echo "export IMAGE_PATH (local file) or IMAGE_URL (public url) first" >&2
exit 1
fi
BASE="https://api.meshy.ai/openapi/creative-lab/keycap/v1"
AUTH="Authorization: Bearer $MESHY_API_KEY"
# api METHOD URL [curl args...] -> prints the response body, non-zero on failure.
# Note we do not use -f/--fail: it discards the body, and the body is the only
# place the reason appears.
api() {
local method=$1 url=$2 out http_code body
shift 2
out=$(curl --silent --show-error --max-time 60 --write-out $'\n%{http_code}' \
-X "$method" "$url" -H "$AUTH" "$@") || return 1
http_code=${out##*$'\n'}
body=${out%$'\n'*}
if ((http_code >= 400)); then
echo "HTTP $http_code for $url: $body" >&2
return 1
fi
printf '%s' "$body"
}
# Each task gets its own 40-minute budget.
poll() {
local kind=$1 id=$2 delay=5 task_status deadline
deadline=$(($(date +%s) + 2400))
while :; do
if (($(date +%s) >= deadline)); then
echo "gave up waiting for $kind $id" >&2
return 1
fi
task_status=$(api GET "$BASE/$kind/$id" | jq -r '.status')
echo "$kind: $task_status"
case "$task_status" in
SUCCEEDED) return 0 ;;
FAILED | CANCELED) return 1 ;;
esac
sleep "$delay"
delay=$((delay * 2 > 30 ? 30 : delay * 2))
done
}
# Build the request body in a file. A base64 data URI must never go on the
# command line or into an exported variable - a photo of any real size will
# exceed the OS argument limit.
BODY=$(mktemp)
trap 'rm -f "$BODY"' EXIT
if [[ -n "${IMAGE_PATH:-}" ]]; then
# Declare the real type: the API accepts JPEG, PNG and WebP.
case "$(printf '%s' "${IMAGE_PATH##*.}" | tr 'A-Z' 'a-z')" in
png) MIME=image/png ;;
webp) MIME=image/webp ;;
*) MIME=image/jpeg ;;
esac
{
printf '{"image_url":"data:%s;base64,' "$MIME"
base64 <"$IMAGE_PATH" | tr -d '\n'
printf '"}'
} >"$BODY"
else
printf '{"image_url":"%s"}' "$IMAGE_URL" >"$BODY"
fi
# 1. Create the prototype task
PROTO_ID=$(api POST "$BASE/prototype" \
-H 'Content-Type: application/json' --data-binary @"$BODY" | jq -r '.result')
# 2. Wait for the design render
poll prototype "$PROTO_ID"
# 3. Pick a candidate (first one here; show image_urls to a user in production)
CANDIDATE_ID=$(api GET "$BASE/prototype/$PROTO_ID" | jq -r '.candidate_ids[0]')
# 4. Create the build task
jq -n --arg p "$PROTO_ID" --arg c "$CANDIDATE_ID" \
'{input_task_id: $p, candidate_id: $c}' >"$BODY"
BUILD_ID=$(api POST "$BASE/build" \
-H 'Content-Type: application/json' --data-binary @"$BODY" | jq -r '.result')
# 5. Wait for the model (a build usually takes 3-7 minutes)
poll build "$BUILD_ID"
# 6. Download the artifacts. These are signed URLs: no Authorization header,
# and they stay valid for 3 days after the task finishes.
TASK=$(api GET "$BASE/build/$BUILD_ID")
curl --silent --show-error --fail --max-time 900 \
-o keycap.glb "$(jq -r '.model_urls.glb' <<<"$TASK")"
curl --silent --show-error --fail --max-time 900 \
-o keycap-obj.zip "$(jq -r '.model_urls.obj_zip' <<<"$TASK")"
echo "Done: keycap.glb + keycap-obj.zip"