跳到正文
FreshApp Demo

开发者文档

生成 API 文档

用生成 API,在你自己的代码里调用本站的模型创建生成任务。每个请求写明模型和它的输入;返回的是一个生成任务,你跟进它直到结束,结果文件从本站下载。本页上的模型、价格、限额和错误码都是本站当前实际执行的。

通过 API 创建生成任务需要买过付费积分包或有有效的付费订阅,而且只扣付费积分。账号的任何密钥都可以查询、列出和下载。

快速开始

在 API 密钥页创建一把密钥,只放在你的服务器上,并设成 API_KEY。下面的示例用 seed-audio-1.0 创建一个生成任务,查询到它结束,再下载第一个文件。

每个新请求发送一个新的 Idempotency-Key。请求没有收到回复时,用同一个键和同样的请求体再发一次:你拿到的是第一次请求创建的任务,只扣一次费。

cURL

export API_ORIGIN='https://demo.freshapp.ai'
export API_KEY='sk_YOUR_API_KEY'

curl --fail-with-body "$API_ORIGIN/api/v1/generations" \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-1001' \
  -d '{"model":"seed-audio-1.0","input":{"prompt":"黎明时分悬崖上的灯塔,海面平静,光线柔和。"}}'

# Read it every 5 to 10 seconds until its status ends.
curl --fail-with-body "$API_ORIGIN/api/v1/generations/GENERATION_ID" \
  -H "Authorization: Bearer $API_KEY"

# A delivered output's url is a signed link: no key needed.
curl --fail-with-body -o result "OUTPUT_URL"

TypeScript 客户端

import { createClient } from './open-api-client';

const client = createClient({
  baseUrl: "https://demo.freshapp.ai",
  apiKey: process.env.API_KEY, // on your server only
});
const generation = await client.create(
  {
    "model": "seed-audio-1.0",
    "input": {
      "prompt": "黎明时分悬崖上的灯塔,海面平静,光线柔和。"
    }
  },
  { idempotencyKey: "order-1001" }
);
const done = await client.wait(generation.id);
for (const output of done.outputs)
  if (output.status === 'delivered') {
    const file = await client.download(output);
    // file.body is the output, as output.content_type says
  }

React hook

import { createClient } from './open-api-client';
import { useGeneration } from './open-api-react';

// In a browser on this site: no key, the visitor's own session.
const client = createClient();

export function Progress({ id }: { id: string }) {
  const { generation, error, done } = useGeneration(client, id);
  if (error) return <p>{String(error)}</p>;
  return <p>{generation?.status ?? '…'}{done ? ' ✓' : ''}</p>;
}

密钥和权限

每个请求都在 Authorization: Bearer 里带上 API 密钥页创建的密钥。密钥是 sk_ 加 43 个字母、数字、连字符或下划线,只在创建和轮换时显示一次。带了 Authorization 的请求只按这把密钥判断,不会改用旁边的登录状态。

密钥要么只读(查列表、查任务、下载),要么还能生成(创建和取消)。只读密钥尝试创建或取消时得到 403 insufficient_scope,什么都不会写入。

密钥可以带一个由地址和网段组成的 IP 白名单:从其他地址发来的请求得到 403 ip_not_allowed。给密钥更多权限、调高上限或放宽白名单需要最近重新登录过;收紧则不需要。

轮换密钥会立刻生成新值,旧值马上失效。作废密钥后它永久失效:还没提交给模型的任务会被取消并退回积分,已经在运行的任务照常完成。

只有有付费资格的账号才能通过 API 创建任务(否则是 403 not_eligible),而且 API 只扣付费积分(不够时是 402 insufficient_credits)。赠送积分和签到积分只留给网站使用。

生成 API 接口

所有路径都在本站上。请求体和响应体都是 JSON,结果文件除外。

https://demo.freshapp.ai

方法路径需要的权限作用
POST/api/v1/generationsgenerations:write创建生成任务。必须带 Idempotency-Key;返回 202 和这个任务。
GET/api/v1/generationsgenerations:read账号的生成任务,最新的在前,按页返回(limit、cursor、module)。文本任务只能按编号查询。
GET/api/v1/generations/{id}generations:read账号的一个生成任务,包括它的输出、积分和结束原因。
POST/api/v1/generations/{id}/cancelgenerations:write取消还没提交给模型的任务,并退回它的积分。
GET, HEAD/api/media/{id}generations:read (或签名链接)一个输出的文件,最多支持一个字节范围。签名链接不需要密钥,从任何地址都能打开。

模型和价格

本站已启用的模型,按 API 的写法列出:把编号填进 model,把字段填进 input。请求在创建时预留它最贵的可能结果,结束后按实际交付的部分收费。

模型编号名称类型价格单次最多输入字段
seed-audio-1.0Seed Audio 1.0音频每分钟 75 积分; 交付结果时至少 10 积分150 积分prompt (必填), voice, audio_references, image_url, output_format, sample_rate, speed, volume, pitch, multilingual

幂等键

创建生成任务必须带 Idempotency-Key 请求头,由 1 到 128 个字母、数字、点、冒号、下划线或连字符组成。建议用你自己系统里的编号,比如订单号,这样同一件事总是用同一个键。

同一个键加同样的请求,不管发多少次都返回同一个任务,只扣一次费。同一个键加不同的请求会得到 409 idempotency_conflict,不会创建任何任务。

超时或连接中断并不代表请求失败。请用同一个键和同样的请求体再发一次,并在收到回复之前一直保留这两样。5xx、429 和 408 也一样处理:按 Retry-After 等待,然后原样再发一次。

状态和轮询

用 GET /api/v1/generations/{id} 查询任务:开始时每 5 秒一次,之后最长每 10 秒一次,直到状态结束。遇到 429 时,Retry-After 会告诉你什么时候再查。

状态含义
queued已接受,等待提交给模型。这时还可以取消。
processing已提交给模型,或者正在保存文件和结算。
succeeded全部输出都已交付,积分已结算。
partially_succeeded部分输出已交付并收费,其余部分的积分已退回。
failed什么都没交付;error 说明原因,预留的积分已退回。
canceled提交之前被取消(由你取消,或因为密钥被作废);积分已退回。

billing.reserved_credits 是创建任务时预留的积分。结算后,charged_credits 是实际收取的积分,refunded_credits 是退回的积分;结算之前两者都是 null,held_credits 显示正在占用的积分。

等待人工核对的任务会在 review 里显示核对开始的时间,核对结束前积分一直占用;核对后任务继续,或者积分退回。

结果文件

每个已交付的输出都有一个本站上的 url。用密钥查询时,它是一个签名链接,一小时内不带密钥、从任何地址都能打开,可以直接交给下载工具。超过一小时后,再查一次任务就能拿到新的链接。

用密钥本身请求 GET /api/media/{id} 也能拿到文件,但受这把密钥的白名单限制。两种方式都支持 HEAD 和一个字节范围(Range: bytes=0-1023),并且不会跳转到别的网站。

输入里引用的文件(比如一段音频样本或一张图片)必须是这个账号在网站上上传的、本站自带的样本,或者来自本站允许的来源的链接;其他引用在预留积分之前就会被拒绝。

限额

下面每一项都是本站当前执行的限额,每次拒绝都会说明碰到了哪一项。

  • 一次请求最多预留 150 积分,即已启用模型中最贵一次请求的费用。可以给密钥设得更低,请求里的 max_credits 能为这一次再调低;超过时返回 400 request_limit_exceeded。
  • 每把密钥每个 UTC 日最多花 5,000 积分,可以自己设置,最高 1,000,000;超过后这把密钥得到 429 key_daily_limit_exceeded,直到下一个 UTC 日。
  • 一个账号的所有密钥加上它授权的 AI 客户端,每个 UTC 日合计最多花 10,000 积分,可以为账号另设上限;超过后返回 429 account_daily_limit_exceeded。
  • 每把密钥和每个账号每分钟最多创建和取消 20 次、查询和下载 240 次;超过后返回 429 rate_limited,并带 Retry-After。
  • 一个账号同时运行的生成任务最多 2 个;再创建会得到 429 too_many_active。
  • 请求体最多 32 KiB 的 JSON;更大的返回 413 request_too_large。

错误码

错误的格式是 {"error": {"code": …, "message": …}}。code 一定是下表中的一个,message 是写给开发者的英文说明。429 和 503 会在 Retry-After 里给出需要等待的秒数。失败或被取消的任务会在 error 字段里带上标为任务错误的码之一。

HTTP错误码含义怎么处理
401invalid_credentialAPI 密钥缺失、格式不对、不存在、已作废或已被替换。检查 API 密钥、它的权限和白名单。
401credential_revoked请求进行中密钥被作废或轮换;被作废密钥排队中的任务会被取消并退回积分。检查 API 密钥、它的权限和白名单。
403insufficient_scope这把密钥不能做这件事:错误里的 required_scope 写明了需要的权限。检查 API 密钥、它的权限和白名单。
403ip_not_allowed不能从这个地址使用这把密钥:它不在密钥的 IP 白名单里。检查 API 密钥、它的权限和白名单。
401unauthorized网站的登录已失效:请重新登录。修改请求后再发送。
403invalid_origin网站请求必须来自本站自身。修改请求后再发送。
403forbidden请求没有可信的客户端地址。联系本站客服。
400invalid_request请求格式不对:JSON、请求头(Idempotency-Key、Content-Type)或查询参数有误。修改请求后再发送。
400invalid_input输入不符合模型的字段,或者模型无法使用它。修改请求后再发送。
413request_too_large请求体太大。修改请求后再发送。
409idempotency_conflict这个 Idempotency-Key 已经用于另一个不同的请求。修改请求后再发送。
404model_unavailable模型不存在或当前不可用。修改请求后再发送。
404not_found这个账号下没有这个路径、任务或文件。修改请求后再发送。
405method_not_allowed这个路径不支持该方法;Allow 列出了支持的方法。修改请求后再发送。
409not_cancelable任务已经提交给模型,或者已经结束。修改请求后再发送。
409not_ready文件还没准备好。按 Retry-After 等待后再发送。
410expired文件已过保存期,已被删除。修改请求后再发送。
400request_limit_exceeded这个请求的费用会超过它的 max_credits,或超过密钥或本站的单次上限。修改请求后再发送。
403not_eligible通过 API 生成需要付费账号。购买积分包或套餐:API 只扣付费积分。
403email_unverified需要先验证账号的邮箱才能生成。修改请求后再发送。
402insufficient_credits积分不足:API 只扣付费积分。购买积分包或套餐:API 只扣付费积分。
403generation_restricted因多次内容违规,这个账号的生成暂停;ends_at 写明暂停到什么时候。联系本站客服。
403trial_paused这个账号有几次试用输出超出了试用限制,网站试用暂停;ends_at 写明暂停到什么时候。不用试用的请求照常进行。修改请求后再发送。
429key_daily_limit_exceeded已达到这把密钥的每日花费上限。按 Retry-After 等待后再发送。 Retry-After:到下一个 UTC 日。
429account_daily_limit_exceeded已达到账号通过 API 的每日花费上限。按 Retry-After 等待后再发送。 Retry-After:到下一个 UTC 日。
429rate_limited短时间内请求太多。按 Retry-After 等待后再发送。 Retry-After:限流窗口的剩余时间。
429too_many_active账号正在运行的任务太多。按 Retry-After 等待后再发送。 Retry-After:30 秒。
429too_many_under_review账号有一些任务在等待人工核对。按 Retry-After 等待后再发送。 Retry-After:3,600 秒。
503busy本站当前运行的任务太多。按 Retry-After 等待后再发送。 Retry-After:30 秒。
503paused生成暂时停止。按 Retry-After 等待后再发送。 Retry-After:60 秒。
503temporarily_unavailable请求暂时无法完成。用同一个 Idempotency-Key 原样再发一次。 Retry-After:60 秒。
503api_unavailable本站暂未开放生成。按 Retry-After 等待后再发送。 Retry-After:60 秒。
503retry请求进行中有内容发生了变化,比如密钥的设置。用同一个 Idempotency-Key 原样再发一次。 Retry-After:60 秒。
503unavailable文件暂时无法访问。按 Retry-After 等待后再发送。 Retry-After:60 秒。
500internal_error本站出了问题。用同一个 Idempotency-Key 原样再发一次。
400request_failed请求被拒绝,但没有对应的专门错误码。联系本站客服。
400captcha_required请完成网站的安全验证。修改请求后再发送。
403captcha_failed网站的安全验证没有通过。修改请求后再发送。
503captcha_unavailable网站的安全验证暂时不可用。按 Retry-After 等待后再发送。 Retry-After:60 秒。
400reference_unavailable引用的上传文件已不存在,或不属于这个账号。修改请求后再发送。
413reference_too_large引用的上传文件合计不能超过 10 MB。修改请求后再发送。
503reference_storage_unavailable引用文件的存储暂时不可用。按 Retry-After 等待后再发送。 Retry-After:60 秒。
409storage_quota_exceeded账号的存储空间已满。联系本站客服。
任务里canceled任务在提交前被取消;积分已退回。修改请求后再发送。
任务里submission_expired任务没能按时提交;积分已退回。用同一个 Idempotency-Key 原样再发一次。
任务里model_busy模型繁忙;积分已退回。按 Retry-After 等待后再发送。
任务里content_rejected内容安全检查拒绝了输入或输出。修改请求后再发送。
任务里result_expired结果没能按时保存。联系本站客服。
任务里generation_failed生成失败。联系本站客服。
任务里delivery_failed结果没能交付;积分已退回。联系本站客服。
任务里trial_limit_exceeded网站试用的输出超出了试用限制,没有交付;积分已退回。修改请求后再发送。

TypeScript 客户端和 React hook

TypeScript 客户端 open-api-client.ts 不需要任何其他包,可以在服务器、Worker 和浏览器里运行。在服务器上传入 baseUrl 和 apiKey;在本站的浏览器页面里两者都不传,请求会使用访客自己的登录状态。

create 会发送 Idempotency-Key(你给的,或者新生成的随机值),遇到连接中断、5xx、429 或 408 时用同一个请求重发,并按 Retry-After 等待。放弃时,错误里带着这个键,你可以稍后用它重发。传入 store(比如 localStorage)后,刷新页面也会沿用第一次的键。

wait 会一直查询任务直到结束:开始时间隔 5 秒,之后最长 10 秒,中止时立刻停下。download 下载一个输出的文件,只对本站自己的未签名链接带上密钥。

React hook open-api-react.ts 用客户端的 wait 跟进一个任务,组件卸载时停止。下载这两个文件,放在同一个目录下,并把扩展名改成 .ts。

文件下载