# 速率限制与并发控制

Tripo API 通过速率限制和并发限制来保障服务稳定性和公平使用。

## 速率限制

速率限制约束你在一个时间窗口内能发起的 API 请求数量。

### 限制规则

- 限流按 **API Key** 粒度计算
- 不同端点的限制不同：生成类端点（`/v3/generation/*`）限制较低，查询类端点（`/v3/tasks/*`）限制较高
- 超出限制时返回 HTTP `429 Too Many Requests`，错误码 `1007`

### 响应头

每个 API 响应都包含限流相关的响应头：

| 响应头 | 说明 |
| :-: | :-: |
| `X-RateLimit-Limit` | 当前时间窗口内允许的最大请求数 |
| `X-RateLimit-Remaining` | 当前时间窗口内剩余可用请求数 |
| `X-RateLimit-Reset` | 限流窗口重置的 Unix 时间戳（秒） |

### 触发限流时的响应

```json
{
  "code": 1007,
  "message": "Rate limit exceeded, you've generated too many requests in a short amount of time",
  "suggestion": "Please wait for a while and try again"
}
```

---

## 并发限制

并发限制约束你的账户下能**同时运行**的任务数量。这与速率限制不同——速率限制管控请求频率，并发限制管控同时执行的任务数。

### 运作机制

- 并发按**账户**维度计算（不是按单个 API Key）
- 按**任务类目**独立限制——每个类目拥有独立的并发池
- 当某个类目的并发占满时，该类目下新建任务会返回 HTTP `429`，错误码 `2000`
- 其他类目**不受影响**——并发池满的类目不会阻塞其他类目的任务创建

### 默认限制

所有用户的默认并发数为每个类目 **10** 个，部分类目有不同上限（见下表）。

| 类目 | 包含的任务类型 | 默认并发数 |
| :-: | :-: | :-: |
| 3D 生成 — H 系列 | 文本生成模型 (H)、图片生成模型 (H)、多视图生成模型 (H) | 10 |
| 3D 生成 — P 系列 | 文本生成模型 (P)、图片生成模型 (P)、多视图生成模型 (P) | 5 |
| 图像生成 | 文本生成图像、图生图、图片生成多视图、编辑多视图 | 1 |
| 动画 | 自动绑骨、绑骨前检查、动画重定向 | 10 |
| 模型后处理 | 贴图、格式转换、精修 | 5 |
| 网格操作 | 语义分割、网格补全、重拓扑 | 10 |

> **说明：** 同一类目内的所有任务类型共享并发池。例如，如果你正在运行 10 个 H 系列的文本生成模型任务，就无法再启动 H 系列的图片生成模型任务，需等待其中一个完成后才能创建新任务。但与此同时，你仍可以正常创建 P 系列或图像生成类任务。

### 并发超限时的响应

```json
{
  "code": 2000,
  "message": "You have exceeded the limit of generation",
  "suggestion": "Try again later. You can also check `Retry-After` header."
}
```

响应中包含 `Retry-After` 头，指示建议等待的秒数。

### 提升并发额度

如需更高的并发限制，请通过支持渠道联系我们。我们可以按类目为你单独配置并发上限。

---

## 处理 429 响应

### Python

```python
import time
import requests

def request_with_backoff(method, url, headers, json=None, max_retries=5):
    for attempt in range(max_retries):
        response = requests.request(method, url, headers=headers, json=json)

        if response.status_code != 429:
            return response

        retry_after = response.headers.get("Retry-After")
        reset_at = response.headers.get("X-RateLimit-Reset")

        if retry_after:
            wait = int(retry_after)
        elif reset_at:
            wait = max(int(reset_at) - int(time.time()), 1)
        else:
            wait = 2 ** attempt

        print(f"触发 429，等待 {wait} 秒（第 {attempt + 1} 次重试）...")
        time.sleep(wait)

    raise Exception("重试次数已用尽")
```

### JavaScript

```javascript
async function requestWithBackoff(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) {
      return response;
    }

    const retryAfter = response.headers.get("Retry-After");
    const resetAt = response.headers.get("X-RateLimit-Reset");
    const wait = retryAfter
      ? Number(retryAfter)
      : resetAt
        ? Math.max(Number(resetAt) - Math.floor(Date.now() / 1000), 1)
        : 2 ** attempt;

    console.log(`触发 429，等待 ${wait} 秒（第 ${attempt + 1} 次重试）...`);
    await new Promise(resolve => setTimeout(resolve, wait * 1000));
  }

  throw new Error("重试次数已用尽");
}
```

## 最佳实践

- **实现指数退避重试**：首次等待 1 秒，每次翻倍，最大等待 32 秒
- **优先使用 `Retry-After` 和 `X-RateLimit-Reset` 响应头**：精确等待到限制窗口重置
- **合理利用类目并发池**：图像生成和 3D 生成拥有独立的并发池，可以并行调度不同类目的任务
- **使用批量接口**：用 `POST /v3/tasks/list` 替代多次 `GET /v3/tasks/{task_id}`
- **合理轮询间隔**：等待任务完成时，以 1–2 秒的间隔轮询，避免对查询端点造成压力
