开发者接入 · v1
API 文档
API 专供有持续、批量任务需求的商家与合作者使用,不面向任务量很少的个人使用者。量大优惠更大,个人使用者请勿申请。
合作商家请联系管理员洽谈价格与接入方式;开通后,由管理员手动私信发送密钥。
接口地址https://shanjierenyi2.com/wp-json/site-api/v1
流程总览
GET /account读取当前可用功能、价格、余额和今日剩余额度(可选)。POST /tasks提交任务:请求头携带密钥和Idempotency-Key;成功返回 HTTP 202 和task_code,此时已扣款。GET /tasks/{task_code}每 5–10 秒轮询,直到status为succeeded、failed或cancelled。status为succeeded时下载result_url(10 分钟有效,过期后重新查询获取新地址)。failed/cancelled均已自动退款。
所有接口只接受服务器端调用;响应均为 JSON(下载地址除外),请求和响应均使用 UTF-8。同一账号内请串行提交创建请求:一个创建请求正在处理时,同账号的另一个创建请求会返回 409 account_busy,稍后按原编号重试即可。
密钥与鉴权
所有业务请求使用 HTTPS,并携带以下请求头。一个账号只有一个有效密钥;更换密钥请联系管理员,旧密钥在重置后失效。
Authorization: Bearer <管理员提供的密钥>
密钥格式为 sapi_ 加 64 位十六进制字符。请把密钥保存在服务器的环境变量中,供后端调用。不要放进网页 JavaScript、URL、公开代码或日志。账号停用、封禁后无法继续调用。
功能列表
API 只开放下表中的功能,每个 type 与站内同名页面使用同一套处理流程和默认价格。提交未开放或不存在的 type 会收到 400 unsupported_type(message 列出当前站点可用类型),不会扣款。
| 功能 | type | 必填素材 | prompt | 结果 |
|---|---|---|---|---|
| 文生图 | text_to_image | 无 | 必填 | 图片 |
| 人脸融合(文生图) | text_to_image_face | image | 必填 | 图片 |
| 单图编辑 | image_edit | image | 必填 | 图片 |
| 双图编辑 | multi_image_edit | image、image2 | 必填 | 图片 |
| 一键脱衣 | image_undress | image | 不需要 | 图片 |
| 换衣 | clothing_change | image | 不需要 | 图片 |
| 图片换脸 | image_face_swap | image:人物照片(提供人脸);image2:目标图片(被替换人脸) | 不需要 | 图片 |
| 视频换脸(自定义视频) | video_face_swap | image:人物照片(提供人脸);video:目标视频 | 不需要 | MP4 视频 |
| 视频换脸(站内模板) | video_face_swap_template | image:人物照片;template:站内模板 key(见 GET /account 的 options) | 不需要 | MP4 视频 |
| 文生视频 | text_to_video | 无 | 必填 | MP4 视频 |
| 人脸融合(文生视频) | text_to_video_face | image | 必填 | MP4 视频 |
| 图生视频(自定义提示词) | image_to_video | image(或 source_task_code:本人文生图作品) | 必填 | MP4 视频 |
| 图生视频模板 | image_to_video_template | image(或 source_task_code:本人文生图作品) | 不需要 | MP4 视频 |
| 图生长视频 | long_video | image(或 source_task_code:本人文生图作品) | 不需要 | MP4 视频 |
| 视频脱衣 | video_undress | video:已剪到 1–10 秒、不超过 20 MB 的视频 | 不需要 | MP4 视频 |
创建任务
POST /tasks · 有文件时使用 multipart/form-data;无文件任务可使用 application/json。所有参数放在请求体中,URL 上不能带查询参数。
请求头
| 请求头 | 说明 |
|---|---|
Authorization | 必填,Bearer <密钥>。 |
Idempotency-Key | 必填,8–128 位,仅允许字母、数字和 . _ : -,建议使用 UUID。每个新任务生成一个新编号;超时或网络错误后,使用相同编号、相同参数和相同文件重试,服务端会返回已创建的同一任务且不再扣款。同一编号搭配不同参数会返回 409 idempotency_conflict。 |
Content-Type | multipart/form-data 或 application/json。 |
请求体字段
| 字段 | 说明 |
|---|---|
type | 必填,见功能列表。 |
prompt | 文生图、人脸融合文生图、单图/双图编辑、文生视频、人脸融合文生视频、自定义图生视频必填,最多 6000 个 UTF-8 字节。脱衣、换衣、换脸、模板、长视频、视频脱衣请勿依赖此字段。提示词原样传给生成流程,与页面输入框等效。 |
negative_prompt | 可选,反向提示词,最多 3000 个 UTF-8 字节。 |
size | 仅文生视频、人脸融合文生视频、自定义图生视频、长视频:480x832(默认)或 704x1280。其他类型请勿传。 |
image / image2 | 文件字段,随创建请求上传。JPG、PNG、WebP;每张最多 18 MB,单边 32–8192 像素,最多 1200 万像素。服务端会重新编码为 JPEG 并剥离元数据。 |
video | 文件字段。视频换脸:MP4 或 WebM,最多 50 MB、250 秒、1080p(长边不超过 1920)、60 FPS,只能包含一个视频轨。视频脱衣:请先剪到 1–10 秒、不超过 20 MB,最短边不低于 360 像素;服务端不代剪。 |
source_task_code | 自定义图生视频、图生视频模板、长视频可用本人已完成的文生图作品作为源图,替代 image。传该作品的 19 位任务 code(字符串),不能与 image 同时使用。作品需在 30 天内且未删除。 |
style | 一键脱衣必填:xiaonai / zhongnai / danai。换衣必填:chiheidiao、rujiao、kunbang 等,完整列表见 GET /account 的 options。 |
hair / breast / outfit | 可选。脱衣的阴毛、换衣的身材/阴毛/服装、视频脱衣的奶子大小与阴毛;取值以 options 为准。视频脱衣未传时默认 breast=zhongnai、hair=shaomao。 |
template | 图生视频模板、站内视频换脸模板必填;长视频可与 segments 二选一。必须使用 GET /account 返回的 key,未知模板拒绝。 |
segments | 长视频自定义段:JSON 数组(multipart 时传 JSON 字符串),2–4 段,每段 {"template_key":"s01_1","duration":4}。只用页面别名(s01_1 / s02 等),不要传内部工作流名。与 template 不能同时传。 |
keep_shoes / keep_hands / keep_stockings / fullbody | 仅视频脱衣,可选,取值 0 或 1。默认保留鞋子、不保留手和丝袜、非全身。 |
整个请求最多 70 MB。不接受上表之外的字段和文件名(会返回 400 invalid_parameter)。文件数量、名称必须与功能列表一致,否则返回 400 invalid_uploads。请先调用 GET /account 读取 options 再提交模板或选项,不要猜测内部 workflow。
受理成功:HTTP 202
{
"task_code": "2026091209301234567",
"replayed": false,
"status_url": "https://shanjierenyi2.com/wp-json/site-api/v1/tasks/2026091209301234567"
}
task_code 与站内任务 code 一致,为 19 位字符串,请勿转成数字,以免丢失精度。replayed: true 表示返回了同一 Idempotency-Key 已创建的任务,没有再次扣款。幂等记录长期保留,不要复用旧编号发起新任务。
受理即扣款;扣款金额可在随后的查询响应 price 字段中看到。金币不足(402)或额度不足(429)时不会创建任务。
请求示例
示例中 $SITE_API_KEY 为管理员提供的密钥,$TASK_REQUEST_ID 为本次任务的唯一编号(请保存下来供重试使用)。
cURL:文生图(JSON)
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--header "Content-Type: application/json" \
--data '{"type":"text_to_image","prompt":"雨后山间的木屋,柔和晨光","negative_prompt":"模糊,低画质"}'
cURL:文生视频(JSON,指定尺寸)
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--header "Content-Type: application/json" \
--data '{"type":"text_to_video","prompt":"海边日出,镜头缓慢推进","size":"704x1280"}'
cURL:单图编辑(multipart 上传图片)
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--form "type=image_edit" \
--form "prompt=把背景换成黄昏的海边" \
--form "image=@input.jpg;type=image/jpeg"
cURL:图片换脸(两张图片)
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--form "type=image_face_swap" \
--form "image=@face.jpg;type=image/jpeg" \
--form "image2=@target.jpg;type=image/jpeg"
cURL:视频换脸(图片 + 视频)
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--form "type=video_face_swap" \
--form "image=@face.jpg;type=image/jpeg" \
--form "video=@target.mp4;type=video/mp4"
cURL:一键脱衣
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--form "type=image_undress" \
--form "style=zhongnai" \
--form "hair=moren" \
--form "image=@input.jpg;type=image/jpeg"
cURL:换衣
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--form "type=clothing_change" \
--form "style=chiheidiao" \
--form "breast=zhongnai" \
--form "image=@input.jpg;type=image/jpeg"
cURL:站内视频换脸模板
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--form "type=video_face_swap_template" \
--form "template=YOUR_TEMPLATE_KEY" \
--form "image=@face.jpg;type=image/jpeg"
cURL:视频脱衣(请先剪到 1–10 秒、不超过 20MB)
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--form "type=video_undress" \
--form "breast=zhongnai" \
--form "hair=shaomao" \
--form "keep_shoes=1" \
--form "video=@clip.mp4;type=video/mp4"
cURL:图生视频(使用本人文生图作品)
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--header "Content-Type: application/json" \
--data '{"type":"image_to_video","prompt":"云朵缓慢移动,镜头平稳推进","source_task_code":"2026091209301234567"}'
cURL:图生视频模板
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--header "Content-Type: application/json" \
--data '{"type":"image_to_video_template","template":"xianyilouru","source_task_code":"2026091209301234567"}'
cURL:长视频(预设或自定义段)
curl --request POST "https://shanjierenyi2.com/wp-json/site-api/v1/tasks" \
--header "Authorization: Bearer $SITE_API_KEY" \
--header "Idempotency-Key: $TASK_REQUEST_ID" \
--header "Content-Type: application/json" \
--data '{"type":"long_video","template":"classic_three","source_task_code":"2026091209301234567"}'
Python:创建、轮询、下载完整流程
import os
import time
import uuid
import requests
BASE = "https://shanjierenyi2.com/wp-json/site-api/v1"
HEADERS = {"Authorization": "Bearer " + os.environ["SITE_API_KEY"]}
def create_task(fields, files=None, request_id=None):
"""request_id 每个业务任务生成一次;重试时必须复用同一个。"""
request_id = request_id or str(uuid.uuid4())
headers = dict(HEADERS, **{"Idempotency-Key": request_id})
for attempt in range(5):
try:
if files:
r = requests.post(BASE + "/tasks", headers=headers, data=fields, files=files, timeout=120)
else:
r = requests.post(BASE + "/tasks", headers=headers, json=fields, timeout=60)
except requests.RequestException:
time.sleep(2 ** attempt)
continue
if r.status_code == 202:
return r.json()["task_code"]
if r.status_code in (409, 429, 503):
time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
continue
raise RuntimeError(r.status_code, r.json())
raise RuntimeError("创建任务重试次数用尽,请稍后用同一 request_id 重试")
def wait_result(task_code):
while True:
r = requests.get(BASE + "/tasks/" + task_code, headers=HEADERS, timeout=30)
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 60)))
continue
r.raise_for_status()
data = r.json()
if data["status"] == "succeeded":
return data
if data["status"] in ("failed", "cancelled"):
raise RuntimeError(data["message"])
time.sleep(8)
def download(data, path):
if not data.get("result_url"):
raise RuntimeError("结果已过期或已删除")
with requests.get(data["result_url"], stream=True, timeout=300) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(1024 * 1024):
f.write(chunk)
task_code = create_task({"type": "text_to_image", "prompt": "雨后山间的木屋,柔和晨光"})
result = wait_result(task_code)
download(result, task_code + ".jpg")
上传文件时把 files 传给 create_task,例如:create_task({"type": "image_edit", "prompt": "..."}, files={"image": ("input.jpg", open("input.jpg", "rb"), "image/jpeg")})。
查询任务与下载结果
GET /tasks/{task_code},携带 API 密钥。建议每 5–10 秒查询一次,收到 429 后按 Retry-After 退避。查询不收费。只能查询本账号通过 API 创建的任务。
{
"task_code": "2026091209301234567",
"status": "succeeded",
"price": 10,
"charged_amount": 10,
"refund_status": "none",
"message": "",
"result_url": "<短期有效下载地址>",
"result_expires_in": 600,
"result_retention_days": 30,
"created_at": "2026-09-12T01:30:12+00:00"
}
| status | 含义 | 处理 |
|---|---|---|
queued | 已受理、排队中。 | 继续轮询。 |
processing | 执行中,或失败后自动重试、退款核对中(此时 refund_status 可能为 pending)。 | 继续轮询。 |
succeeded | 成功。 | 下载 result_url。 |
failed | 最终失败,金币已按创建时价格退回(charged_amount 为 0,refund_status 为 refunded)。 | 终态;如需重做请用新的 Idempotency-Key 创建。 |
cancelled | 任务在排队阶段被账号本人在站内“我的作品”取消,金币已退回。 | 终态。 |
result_url 有效期 10 分钟(result_expires_in 秒),过期后重新查询即可获得新地址。下载结果地址时不要附带 API 密钥。结果为图片时通常是 PNG/JPG,视频为 MP4;请以下载响应的 Content-Type 或文件扩展名为准。
结果从受理时间起保留 30 天(result_retention_days),请及时下载;过期或已删除的结果 result_url 为 null。已结束任务的输入素材在 7 天后清理;仍在执行或重试的任务保留素材。
查询账户
GET /account,携带 API 密钥。返回余额、当前开放的功能与价格、网站时区与日期、每日消费上限、今日已消费和剩余额度。查询不收费。
{
"balance": 1000,
"currency": "金币",
"date": "YYYY-MM-DD",
"timezone": "<网站时区>",
"daily_limit": 500,
"today_spent": 100,
"daily_remaining": 400,
"types": ["text_to_image", "text_to_image_face", "image_edit", "multi_image_edit", "image_undress", "clothing_change", "image_face_swap", "video_face_swap", "video_face_swap_template", "text_to_video", "text_to_video_face", "image_to_video", "image_to_video_template", "long_video", "video_undress"],
"prices": {"text_to_image:default": 10, "image_undress:default": 8, "text_to_video:480x832": 15},
"pricing_notes": {"video_face_swap": "按视频时长阶梯计价……", "long_video": "按段计价……"},
"options": {"image_undress": {"style": ["xiaonai", "zhongnai", "danai"]}, "image_to_video_template": [{"template": "xianyilouru", "label": "掀衣露乳"}]}
}
prices 的键为 type:size,图片类功能的 size 固定为 default。按时长/段数/模板文件计价的类型只出现在 pricing_notes。options 给出当前站点可用的 style / template / 长视频预设与段别名。以上金额仅为示例,实际价格以此接口及下表为准。账户接口不返回个人联系方式等资料。
价格与退款
具体价格请商家与管理员协商,量大优惠更大。下表为当前基础报价,与站内页面同功能的价格一致;合作价格与计费方式请在开通前确认。
| 功能 | 尺寸 | 基础报价 |
|---|---|---|
| 文生图 | 默认 | 10 金币 / 次 |
| 人脸融合(文生图) | 默认 | 15 金币 / 次 |
| 单图编辑 | 默认 | 10 金币 / 次 |
| 双图编辑 | 默认 | 10 金币 / 次 |
| 一键脱衣 | 默认 | 8 金币 / 次 |
| 换衣 | 默认 | 8 金币 / 次 |
| 图片换脸 | 默认 | 8 金币 / 次 |
| 文生视频 | 480x832 | 15 金币 / 次 |
| 文生视频 | 704x1280 | 20 金币 / 次 |
| 人脸融合(文生视频) | 480x832 | 17 金币 / 次 |
| 人脸融合(文生视频) | 704x1280 | 22 金币 / 次 |
| 图生视频(自定义提示词) | 480x832 | 15 金币 / 次 |
| 图生视频(自定义提示词) | 704x1280 | 20 金币 / 次 |
| 图生视频模板 | 默认 | 27 金币 / 次 |
| 视频换脸(自定义视频) | 按时长 | 按视频时长阶梯计价:前 60 秒每秒 0.6 金币,60–180 秒每秒 0.5 金币,180–250 秒每秒 0.38 金币,向上取整,最低 15 金币。例:30 秒 18 金币,60 秒 36 金币,120 秒 66 金币,250 秒 123 金币。时长由服务器读取。 |
| 视频换脸(站内模板) | 按模板 | 按站内模板发布时写入的价格扣费;未知模板直接拒绝,不会回退到默认价 |
| 图生长视频 | 按段 | 按段计价:首段 27 金币,每多一段 +15;704x1280 每段再 +5;单段超过 4 秒后每多 1 秒 +1。段数 2–4,单段 3–10 秒。例:2 段各 4 秒 480x832 为 42 金币。 |
| 视频脱衣 | 按时长 | 按视频时长计价:每秒 5 金币,按 0.1 秒精度四舍五入,最短 1 秒、最长 10 秒。例:1 秒 5 金币,5.9 秒 30 金币,10 秒 50 金币。时长由服务器读取。 |
使用本站金币余额。任务受理时扣款,排队和执行中的任务计入当日 API 消费;查询任务与账户不收取金币。
最终失败或取消的任务按创建时的价格退回金币,并回退原扣款日的 API 消费额度。重试中的任务等待最终结果后结算。价格变动不影响已经受理的任务。
每日消费上限按网站时区自然日统计,只累计 API 消费;超出时创建请求返回 429 daily_limit_exceeded,次日自动恢复,需要调整请联系管理员。
请求限制
- 每个账号每分钟最多 60 次创建请求、500 次 API 请求;超出返回 429
rate_limited并附Retry-After。 - 同一账号同一时间只处理一个创建请求,并发创建返回 409
account_busy。 - 单张图片最多 18 MB;自定义视频换脸最多 50 MB;视频脱衣最多 20 MB 且须先剪到 1–10 秒;整个请求最多 70 MB。
- 每个
result_url只用于下载一次结果文件,请勿高频重复请求(本站直出的下载地址每分钟最多 20 次)。
安全策略与自动停用
密钥仅用于本文档列出的三个接口和文档中的字段。服务端会对每个密钥的异常请求进行识别,出现以下情况时密钥会被立即自动停用并通知管理员,后续请求返回 403 api_disabled;恢复需要联系管理员核实:
- 请求中出现服务端内部字段(例如价格、余额、额度、工作流、任务类型等),或试图指定他人身份。
- 参数、文件名或请求头中包含 SQL 注入、脚本注入、路径穿越等特征;上传可执行文件或脚本伪装成图片、视频。
- 反复查询或引用不属于本账号的任务编号,尝试用密钥访问本文档以外的接口或错误的请求方法。
- 短时间内持续产生大量参数错误、限流等异常请求。
正常接入不会触发以上规则:请只提交文档列出的字段,只查询本账号通过 API 创建的 task_code,联调时先用少量请求验证参数,再批量提交。遇到 400 错误时请先修正参数,不要用同一错误请求反复重试。任何安全测试请先与管理员沟通并使用管理员指定的测试账号。
错误处理
错误响应为 JSON,code 为稳定的机器可读错误码,message 为说明文字(可能调整,请勿用于程序判断)。
{"code":"invalid_api_key","message":"请提供有效 API 密钥","data":{"status":401}}
| HTTP | code | 含义与处理 |
|---|---|---|
| 400 | unsupported_type | type 不存在或本站未开放;message 中列出可用类型。 |
| 400 | invalid_parameter | 包含不支持的字段、URL 带查询参数或参数类型错误。 |
| 400 | invalid_prompt / invalid_size / invalid_source | 提示词、尺寸或 source_task_code 不符合要求。 |
| 400 | invalid_uploads / invalid_upload / invalid_image / invalid_video | 文件数量或字段名不对、上传失败、图片或视频格式/尺寸/时长不合规。 |
| 400 | upload_not_allowed | 当前站点禁止上传外部素材,请改用本人文生图 source_task_code。 |
| 400 | idempotency_required / invalid_body | 缺少合法的 Idempotency-Key,或请求体为空(常见于超过服务器上传限制)。 |
| 401 | invalid_api_key | 密钥缺失或错误;确认是否已被管理员重置。 |
| 402 | insufficient_balance | 金币不足,请充值后用同一 Idempotency-Key 重试。 |
| 403 | api_disabled / https_required / invalid_download | 授权已停用、账号不可用或因异常请求被自动停用(见安全策略,需联系管理员);未使用 HTTPS;下载链接无效或过期。 |
| 404 | task_not_found / source_not_found / result_unavailable | 任务、源作品或结果不存在、不属于当前账号,或已过期删除。 |
| 409 | account_busy | 同账号另一个请求正在处理,稍后用原 Idempotency-Key 重试。 |
| 409 | idempotency_conflict | 同一编号对应的参数或文件发生了变化;为新任务换一个编号。 |
| 409 | account_deleting | 账号正在注销,不能创建任务。 |
| 413 | request_too_large / file_too_large / image_too_large | 请求或文件过大,请压缩后重试。 |
| 415 | unsupported_media_type | Content-Type 不是 multipart/form-data 或 application/json。 |
| 429 | rate_limited | 请求过于频繁,按 Retry-After 退避。 |
| 429 | daily_limit_exceeded | 今日 API 消费额度不足,等待新的一天或联系管理员调整上限。 |
| 503 | service_unavailable / storage_unavailable / storage_error 等 | 服务暂时不可用。创建请求保留原 Idempotency-Key 重试,不会重复扣款;持续异常时联系管理员。 |
网络超时时无法确定请求是否已受理:请始终用同一 Idempotency-Key 重试创建,服务端会返回已存在的任务(replayed: true)或继续创建。