文档中心内部

图像接口概览

发一句提示词,等一两分钟,拿到一张图。

调用一次是什么样子?

一次 HTTP 请求换一张图。没有轮询、webhook、任务 id。

✍️发提示词POST 一个 JSON
→
⏳等 50–110 秒连接挂着
这是正常的
→
🖼️图在响应里直接取

同步接口:返回时图已经生成完。

先知道这三件事

50–110秒
出一张图要等这么久,2K / 4K 更慢。这是正常的,不是卡住了。网关、代理、Serverless 超时都放宽到分钟级,别让用户干等。
1 张
一次请求出一张。n 参数不存在,要 4 张就发 4 次,单独计费。
∞
Node 内置的 fetch 没有默认超时。不传 signal: AbortSignal.timeout(...),上游一卡住就永久挂着:不报错、不返回、连接不释放,直到并发占满、服务僵掉。每个 fetch 都要传,包括下载图那次。

两个端点

地址只有一个 https://api.tutujin.com/v1,模型固定 gpt-image-2。OpenAI 兼容 Images API,现成 SDK 换 base URL 和 key 即可。

POST https://api.tutujin.com/v1/images/generations
POST https://api.tutujin.com/v1/images/edits
用途端点请求体
文生图(纯提示词)/v1/images/generations application/json
图生图(带参考图 / 改图)/v1/images/edits multipart/form-data
对外口径

对外统一称「土土金图像接口 / Tutujin Image API」。代码、注释、日志、报错、commit 里不要出现其它服务商域名或品牌名。

鉴权

一个 header,没别的握手。

Authorization: Bearer <TUTUJIN_API_KEY>

Key 形如 sk-xxxxxxxx,找管理员要。放环境变量,不要硬编码。

config.js
export const TUTUJIN = {
  baseUrl: 'https://api.tutujin.com/v1',
  apiKey: process.env.TUTUJIN_API_KEY,   // 绝不硬编码,绝不进 git
  model: 'gpt-image-2',
  timeoutMs: 300_000,                    // 5 分钟。正常 50–110s,2K/4K 更久
};
动手前先确认 .env 真的被 git 忽略了

Key 写进 .env,跑 git check-ignore -v .env 确认:有输出才算忽略。Key 一旦推上去就得整体轮换,所有在用它的服务都要跟着改。

五分钟跑通第一张图

先用一条命令分清「代码写错」还是「链路不通」。Node 20+ 整行复制:

最小可运行验证 · Node 20+
TUTUJIN_API_KEY=你的key node -e "
fetch('https://api.tutujin.com/v1/images/generations', {
  method:'POST',
  headers:{Authorization:'Bearer '+process.env.TUTUJIN_API_KEY,'Content-Type':'application/json'},
  body:JSON.stringify({model:'gpt-image-2',prompt:'a red apple',size:'1024x1024',quality:'high',response_format:'url'}),
  signal:AbortSignal.timeout(300000)
}).then(async r=>console.log(r.status, (await r.text()).slice(0,500)))
"

它会安静地跑一两分钟 —— 那是在出图,不是卡住 —— 然后打印状态码和一段 JSON。

打印出来的东西说明
200 + 一段带 data 的 JSON链路全通,可以开始写正式代码
401 / 403key 不对、没权限,或者环境变量没读到
402余额不足,找管理员
命令直接抛 TimeoutError300 秒都没返回,见错误处理与避坑

一个能直接用的最小示例

比「能跑」多做三件事:设超时、非 2xx 带出响应正文、两种返回形式都兼容。缺一件出问题都难查。

text-to-image.mjs · Node 20+
const BASE = 'https://api.tutujin.com/v1';
const KEY = process.env.TUTUJIN_API_KEY;

export async function textToImage({ prompt, size = '1024x1024' }) {
  const resp = await fetch(`${BASE}/images/generations`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'gpt-image-2',
      prompt,                  // 建议英文;中文也能理解,英文更稳
      size,                    // 必须是 "宽x高" 像素串,而且永远要传
      quality: 'high',
      response_format: 'url',  // 期望返回 URL,但也可能给 b64_json
    }),
    signal: AbortSignal.timeout(300_000),   // ← 少了这行会永久挂住
  });

  // 非 2xx 一定要把响应正文带出来,否则后面无从判断是审核拦截还是参数错
  if (!resp.ok) {
    const text = await resp.text();
    throw new Error(`图像接口返回 ${resp.status}: ${text.slice(0, 500)}`);
  }

  const json = await resp.json();
  const d = json?.data?.[0];
  if (!d) throw new Error('响应里没有 data');

  // 两种返回形式都要无条件兼容,别赌只会返回其中一种
  if (typeof d.b64_json === 'string' && d.b64_json) {
    return Buffer.from(d.b64_json, 'base64');
  }
  if (typeof d.url === 'string' && /^https?:\/\//.test(d.url)) {
    // 这个 URL 是临时的,拿到当下就下载转存
    const r = await fetch(d.url, { signal: AbortSignal.timeout(60_000) });
    if (!r.ok) throw new Error(`下载生成图失败: ${r.status}`);
    return Buffer.from(await r.arrayBuffer());
  }
  throw new Error('data[0] 里既没有 url 也没有 b64_json');
}

尺寸只认像素串

✕ 这么传 size
"size": "16:9"

不报错,悄悄给你一张 2048×2048 的方图。省略也一样:图生图会塌成 1024×1024。

✓ 只认宽×高
"size": "1536x864"

像素串,如 "1536x864",而且每次都要传。

返回的两种形式

📥收到 200取 data[0]
取不到就报错
→
?两种可能url 或 b64_json
都要接住
→
💾立刻转存存到自己的存储

别赌它只返回其中一种。

返回的 url 存活以小时计

域名也不固定。拿到当下就下载转存,别入库当长期地址、别直接发给终端用户、也别把域名写死进代码。

接下来看哪一页

你要做的事去这里
完整参数、图生图传参考图、尺寸限制 接口参考
哪些错该重试、五个"不报错"的坑 错误处理与避坑
正式接入前先扫一遍避坑页

最难缠的不是报错,是不报错:多参考图可能只有第一张生效,照常 200、照常计费。错误处理与避坑列全了。

土土金内部文档 · 本页最后更新 2026-09-09

内容有误或步骤跑不通,请把页面地址和截图发给内部技术支持。