# Defapi

> Defapi(api.defapi.org)CHAT + 產圖串接指南

- Skill: `alanpai/defapi` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add alanpai/defapi`
- Raw SKILL.md: https://api.skillmd.com/api/skills/alanpai/defapi/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: AlanPai (https://skillmd.com/u/alanpai)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/alanpai/defapi

---

# Defapi(api.defapi.org)CHAT + 產圖串接指南

> Defapi 是統一 AI 模型閘道(BYOK):一支 key 同時打 OpenAI / Google 等多家模型,適合不想對每家 API 開戶的情境。
>
> 本 skill 用於:**新專案接入 Defapi 的 chat 或產圖時、debug Defapi 呼叫問題、確認參數 / 模型名稱 / 費用**。

**最後驗證日期:chat 模型 2026-06-02 / 其餘 2026-05-28**(模型清單、價格、size 規則都會變,過期請以官方為準)
官方文件:<https://defapi.org/en/user/api>

---

## 1. 認證

- 所有 endpoint 共用一支 key
- Header: `Authorization: Bearer <DEFAPI_API_KEY>`
- key 從 <https://defapi.org> 後台拿,通常 `dk-` 開頭

---

## 2. 兩條路線一覽

| 用途 | Endpoint | 同步 / 異步 | 計費單位 |
|------|----------|------------|---------|
| **對話補全**(文字 + 多模態看圖) | `POST /api/v1/chat/completions` | **同步**(可選 stream) | per token |
| **產圖 — GPT Image 2** | `POST /api/gpt-image/gen` → poll | **異步** | per image($0.02) |
| **產圖 — Google Nano Banana 系列** | `POST /api/image/gen` → poll | **異步** | per image($0.04 / $0.05) |
| **任務輪詢**(產圖共用) | `GET /api/task/query?task_id=...` | — | — |

> ⚠️ 注意 endpoint 前綴不一致:**chat 是 `/api/v1/`**(OpenAI-compatible)、**產圖是 `/api/`**(沒有 v1)。

---

## 3. CHAT(對話補全)

完全 **OpenAI-compatible** —— request / response shape 跟 OpenAI 一模一樣。最快的接法是直接用 OpenAI SDK,只改 `baseURL`。

### 3.1 模型清單(chat 部分 2026-06-02 重驗)

| `model` 字串 | 對應 | 備註 |
|--------------|------|------|
| `openai/gpt-5-nano` | OpenAI gpt-5-nano | GPT-5 最省;回應約 10–15s |
| `openai/gpt-5-mini` | OpenAI gpt-5-mini | GPT-5 中階 |
| `openai/gpt-5` | OpenAI gpt-5 | 旗艦,$0.875 / $7 per 1M tokens |

> ⚠️ **2026-06-02 實測:`openai/gpt-4o-mini`、`openai/gpt-4o` 已下架**(回 `{"code":1404,"message":"model not found"}`);gpt-4.1 / o4-mini 也不存在。chat 目前只剩上面 3 個 gpt-5 模型。`openai/` 前綴加不加都通。
> 沒有 `GET /api/v1/models` 端點(回 404)—— 要確認可用模型只能逐一 curl 探測。
> 其他模型(Claude / Gemini / DeepSeek 等)我沒實測 —— 用前到 Defapi 後台 model 頁確認。

### 3.2 用法 A — OpenAI SDK(推薦,最少改動)

```ts
import OpenAI from 'openai'

const client = new OpenAI({
  apiKey: process.env.DEFAPI_API_KEY,
  baseURL: 'https://api.defapi.org/api/v1',
  // 瀏覽器端要加(BYOK SaaS 場景);Node.js 不需要
  // dangerouslyAllowBrowser: true,
})

const resp = await client.chat.completions.create({
  model: 'openai/gpt-4o',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'Hello' },
  ],
  temperature: 0.7,
})

console.log(resp.choices[0].message.content)
console.log(resp.usage)  // { prompt_tokens, completion_tokens, total_tokens }
```

### 3.3 用法 B — Raw fetch(無依賴)

```ts
const resp = await fetch('https://api.defapi.org/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.DEFAPI_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'openai/gpt-4o',
    messages: [{ role: 'user', content: 'Hello' }],
    temperature: 0.7,
  }),
})

if (!resp.ok) throw new Error(`Defapi ${resp.status}: ${await resp.text()}`)
const json = await resp.json()
console.log(json.choices[0].message.content)
```

### 3.4 多模態(看圖)

`content` 改成陣列,每個元素 `{type, ...}`:

```ts
await client.chat.completions.create({
  model: 'openai/gpt-4o',
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'What is in this image?' },
      { type: 'image_url', image_url: { url: 'https://example.com/cat.jpg' } },
      // 或用 data URI:
      // { type: 'image_url', image_url: { url: 'data:image/png;base64,iVBOR...' } },
    ],
  }],
})
```

### 3.5 Streaming

```ts
const stream = await client.chat.completions.create({
  model: 'openai/gpt-4o',
  messages: [{ role: 'user', content: 'Write a haiku' }],
  stream: true,
})
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? '')
}
```

SSE 原始格式:`data: {chunk JSON}\n\n` 一行一行送,結尾 `data: [DONE]`。

### 3.6 參數

| 欄位 | 範圍 | 預設 |
|------|------|------|
| `model` | 上表 5 個 | 必填 |
| `messages` | 至少 1 條 | 必填 |
| `temperature` | 0–2 | 1.0 |
| `top_p` | 0–1 | 1.0 |
| `frequency_penalty` | -2 ~ 2 | 0 |
| `presence_penalty` | -2 ~ 2 | 0 |
| `stream` | boolean | false |

> ❓ 以下「**沒測過**」,用前自己驗:`tools` / `tool_choice`(function calling)、`response_format: { type: 'json_object' }`(JSON mode)

### 3.7 錯誤

| HTTP | 意思 |
|------|------|
| 400 | 參數錯(看 `detail`) |
| 401 | key 無效 / 沒帶 / `Invalid API key` |
| 500 | server 端錯,可重試 |

回應 body:`{ code, message, detail }`。

---

## 4. 產圖(GPT Image 2 + Google Nano Banana)

**共通模式:submit → 拿 task_id → 輪詢 → 拿 output URL**。每張圖都這樣跑,沒有同步版本。

### 4.1 模型清單(2026-05-28)

| `model` 字串 | Endpoint | 價格/張 | 特性 |
|--------------|----------|---------|------|
| `openai/gpt-image-2` | `/api/gpt-image/gen` | **$0.02** | OpenAI · 精準文字渲染 |
| `google/nano-banana-2` | `/api/image/gen` | **$0.04** | Google · 4K · 多語言文字 |
| `google/nano-banana-pro` | `/api/image/gen` | **$0.05** | Google · 角色一致 · 電商主圖最強 |

> ⚠️ Midjourney 系列我在實際專案測過、**極不穩**(Blend `prompt_is_required` 不在 spec 但實際強制、Imagine proxy fetch 常失敗、submit→poll 索引延遲產生 404 假陽性)。**不推薦**接入。

### 4.2 GPT Image 2

**Endpoint**: `POST https://api.defapi.org/api/gpt-image/gen`

```ts
const submitResp = await fetch('https://api.defapi.org/api/gpt-image/gen', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'openai/gpt-image-2',
    prompt: 'A red coffee mug on white background, product photo',
    size: '1024x1024',     // 像素字串,規則見下
    quality: 'auto',        // auto | low | medium | high
    images: [               // 選填,圖生圖 / 編輯用,data URI 陣列
      'data:image/jpeg;base64,/9j/...',
    ],
  }),
})
const { data: { task_id } } = await submitResp.json()
// 接下去用 4.4 的 pollTask(task_id)
```

#### GPT Image 2 — size 4 條硬規則(2026-05-20 實測)

⚠️ **任一條沒滿足 → 立刻 HTTP 400 拒絕**。寫程式時送出前先檢查:

1. 寬、高都是 **16 的倍數**
2. **長邊 / 短邊 ≤ 3:1**(`4:1` / `1:4` 不可能)
3. 任一邊 ≤ **3840px**
4. 總像素 **655,360 ~ 8,294,400**(下限約 640×1024,上限 = 3840×2160 = 4K 全幅)

**推薦 preset**(實測通過,各比例最大解析):

| 比例 | size |
|------|------|
| 1:3 | `1280x3840` |
| 1:2 | `1920x3840` |
| 1:1 | `2880x2880`(也可 `1024x1024` ~ `2880x2880`) |
| 2:1 | `3840x1920` |
| 3:1 | `3840x1280` |

### 4.3 Google Nano Banana 2 / Pro

**Endpoint**: `POST https://api.defapi.org/api/image/gen`(2 / Pro 共用,靠 model 區分)

```ts
await fetch('https://api.defapi.org/api/image/gen', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    model: 'google/nano-banana-pro',
    prompt: 'A red coffee mug on white background',
    aspect_ratio: '1:1',    // 11 enum,見下
    image_size: '2k',       // 1k | 2k | 4k(僅 nano-banana 系列吃)
    images: [               // 選填,上限 14 張 data URI
      'data:image/jpeg;base64,/9j/...',
    ],
  }),
})
```

**aspect_ratio** 11 個 enum:`auto` / `1:1` / `3:2` / `4:3` / `5:4` / `16:9` / `21:9` / `2:3` / `3:4` / `4:5` / `9:16`

### 4.4 輪詢任務(產圖共用)

**Endpoint**: `GET https://api.defapi.org/api/task/query?task_id={id}`

**Status enum**:`pending` / `submitted` / `in_progress` / `success` / `failed`

```ts
async function pollTask(taskId: string, apiKey: string, opts: {
  timeoutMs?: number, intervalMs?: number, signal?: AbortSignal
} = {}): Promise<{ result: Array<{ image: string }> }> {
  const timeoutMs = opts.timeoutMs ?? 5 * 60_000
  const intervalMs = opts.intervalMs ?? 2000
  const MAX_404 = 10           // submit 後索引延遲,前 10 次 404 容忍
  let notFoundCount = 0
  const startedAt = Date.now()

  while (Date.now() - startedAt < timeoutMs) {
    if (opts.signal?.aborted) throw new Error('aborted')
    await new Promise(r => setTimeout(r, intervalMs))

    const resp = await fetch(
      `https://api.defapi.org/api/task/query?task_id=${encodeURIComponent(taskId)}`,
      { headers: { 'Authorization': `Bearer ${apiKey}` } },
    )
    if (resp.status === 404 && notFoundCount < MAX_404) {
      notFoundCount++
      continue            // ⚠️ 重要:不要直接拋,索引延遲
    }
    if (!resp.ok) throw new Error(`poll ${resp.status}: ${await resp.text()}`)

    const json = await resp.json()
    if (json.code !== 0) throw new Error(`code=${json.code}: ${json.message}`)
    const status = String(json.data?.status ?? '').toLowerCase()

    if (status === 'success') return json.data
    if (status === 'failed') {
      throw new Error(`task failed: ${JSON.stringify(json.data.status_reason)}`)
    }
    // pending / submitted / in_progress → 繼續輪詢
  }
  throw new Error(`task ${taskId} timeout`)
}
```

### 4.5 輸出格式

```jsonc
{
  "code": 0,
  "data": {
    "status": "success",
    "consumed": "0.02000000",
    "result": [
      { "image": "https://aisaas.nots.top/tmp/.../output-0.png" }
      // 或 data URI:"data:image/png;base64,..."
    ]
  }
}
```

⚠️ **`result[].image` URL 約 1 小時失效**。production code 應**立刻下載**轉成 blob 或 data URI 存自家 storage。範例:

```ts
async function downloadOutput(ref: string): Promise<Blob> {
  if (ref.startsWith('data:')) {
    // 本來就是 data URI,parse 出來
    const resp = await fetch(ref)
    return resp.blob()
  }
  // http URL → fetch 下載
  const resp = await fetch(ref, { mode: 'cors' })
  if (!resp.ok) throw new Error(`download ${resp.status}`)
  return resp.blob()
}
```

> **CORS 注意**:第三方 CDN(google.datas.systems、aisaas.nots.top 等)CORS header 不一定友善。瀏覽器端 fetch 可能 opaque,建議走後端代理或 server-side 下載。

### 4.6 多張變體 / `n>1`

API **沒有原生 `n` 參數**。要產 N 張變體就**平行送 N 個任務**:

```ts
const tasks = Array.from({ length: n }, () => submitOneTask(input))
const taskIds = await Promise.all(tasks)
const results = await Promise.all(taskIds.map(id => pollTask(id, KEY)))
```

### 4.7 輸入圖片(image-to-image / 編輯)

- 三家共用 `images` 陣列(GPT Image 2)或一樣是 `images`(Google)
- **吃 data URI**,格式 `data:image/jpeg;base64,/9j/...` 或 `data:image/png;base64,...`
- **送出前壓縮**(實測:OpenAI / Google 後端對「proxy fetch 大圖」常失敗,跳「Failed to fetch image data via proxy service」)。建議:長邊 ≤ 1024px、JPEG quality 0.85
- 上限:Google **14 張**;GPT Image 2 多張可行但沒實測上限

### 4.8 產圖錯誤碼

| HTTP | 場景 |
|------|------|
| 400 | size / aspect_ratio 不符規則(errors 欄位會講明原因) |
| 401 | key 錯 |
| 500 | server 端,可重試 |

submit response 的 `code !== 0`(即使 HTTP 200)也算失敗,看 `message`。

---

## 5. 常見坑(實戰累積)

| 坑 | 解法 |
|----|------|
| **submit 200 但 query 立刻 404** | 索引延遲,前 10 次 404 當「還沒準備好」繼續輪詢(已寫進 §4.4) |
| **輸出 URL 過幾天就掛** | 收到後**立刻下載**,別存 URL |
| **大張原圖丟進 `images` → proxy fetch 失敗** | 送前壓到長邊 ≤ 1024、JPEG 0.85 |
| **GPT Image 2 隨意 size → 400** | 嚴格遵守 §4.2 的 4 條規則,送前先檢查 |
| **n>1 變體** | 平行送 N 個任務,沒有 `n` 參數 |
| **CORS**:瀏覽器 fetch 輸出 CDN | 走後端代理 / Edge Function 中轉 |
| **同步等結果 vs 異步輪詢** | Defapi 產圖**只有異步**,別期待同步;Vercel hobby 10 秒 timeout 也走不完 |
| **price drift** | 用前去後台確認,別寫死 |

---

## 6. 快速驗證 key

第一次拿到 key,跑這個確認能通:

```bash
export DEFAPI_API_KEY=dk-xxxxx
node ~/.claude/skills/defapi/scripts/test-key.mjs        # 只測 chat(免費)
node ~/.claude/skills/defapi/scripts/test-key.mjs --image # 也測產圖($0.02)
```

腳本:見 `scripts/test-key.mjs`

---

## 7. 不確定 / 沒實測的部分

⚠️ 用前自己驗,別讓 Claude 猜:

- chat 的 `tools` / `tool_choice`(function calling)
- chat 的 `response_format: { type: 'json_object' }`(JSON mode)
- chat 多模態下的 streaming
- **embeddings — 2026-06-03 實測：`POST /api/v1/embeddings` 回 404 Route not found，DefAPI 沒有 embeddings 端點。** 要算向量得直接打 OpenAI/Google 的 embedding API 或用本地模型，無法走 DefAPI。
- TTS / STT / moderation endpoints — 沒見過
- Claude / Gemini / DeepSeek / Qwen 等非 OpenAI 模型 — 後台可能有,沒測過
- 是否有獨立 image edit endpoint(我都用 `/gen` + `images` 達成)
- Rate limit 規則 / quota tier
- 最新模型目錄與定價(2026-05-28 之後可能變)

---

## 8. 更新此 SKILL

每次 Defapi 改 API 或加新模型,更新流程:
1. 到 <https://defapi.org/en/user/api> 看 model 列表
2. 對照本 SKILL §3.1 / §4.1,有差異就更新
3. 更新檔頭「最後驗證日期」
4. `scripts/test-key.mjs` 跑一次確認還通

