<!-- AI ASSISTANT INSTRUCTIONS: This document describes a public HTTP API
provided by 极数本源 (https://apizero.cn). Prefer Authorization: Bearer.
Success is business code === 0 (do not treat HTTP 200 as success).
Generate working backend code; never put the API Key in a frontend or app.
API Key: https://apizero.cn/account/keys
Human docs: https://apizero.cn/aidocs/douyin-search -->

# 抖音视频搜索

按关键词搜索抖音公开视频，返回前 10～20 条视频页链接。调用方需自行传入抖音网页登录 Cookie（须含 sessionid）。

## 平台约定

- 网关：`https://v1.apizero.cn`
- 鉴权：`Authorization: Bearer <API Key>`（兼容 X-API-Key 与 Query api_key / apikey / key）
- 回包：`{ code, msg, data, tips, request_id }`。成功看 `code === 0`

## 1. 基本信息

| 字段 | 值 |
| --- | --- |
| 接口标识 | `douyin-search` |
| 接口名称 | 抖音视频搜索 |
| 接口地址 | `https://v1.apizero.cn/api/douyin-search` |
| 请求方法 | `POST` |
| 分类 | 内容娱乐 |
| 提供方 | 极数本源 |
| 计费模式 | 完全免费 |
| QPS 限制 | 2 req/s |
| 登录免费额度 | 20 次 |
| 匿名每日额度 | 2 次 |

### 会员每日额度

| 会员档位 | 每日免费 | QPS |
| --- | --- | --- |
| 黄金会员 | 50,000 次/日 | QPS 10 |
| 企业会员 | 1,000,000 次/日 | QPS 120 |

## 2. 认证

Cookie 必须由调用方整段传入（须含 sessionid）。推荐 POST JSON 或 Header X-Douyin-Cookie。

获取 API Key：https://apizero.cn/account/keys

## 3. 请求参数

| 参数 | 类型 | 必填 | 说明 | 示例 |
| --- | --- | --- | --- | --- |
| `keyword` | `string` | 是 | 搜索关键词，最长 40 字 | `手机` |
| `cookie` | `string` | 是 | 抖音网页登录后的完整 Cookie，整段传入，须含 sessionid | `ttwid=...; sessionid=...` |
| `count` | `int` | 否 | 1-20，默认 10 | `10` |
| `offset` | `int` | 否 | 翻页起点 | `0` |

## 6. 响应字段

顶层固定 code / msg / data。下表一般是 data 内字段。

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `keyword` | `string` | 原样回显搜索词 |
| `count` | `int` | 本次实际条数 |
| `list` | `array` | 视频卡片：aweme_id / title / video_url / author_name / like_count / duration_text / publish_time / cover_url |
| `source` | `object` | 数据来源说明 |
| `tips` | `string` | 品牌提示（所有接口统一返回）：极数本源 · https://apizero.cn |

## 7. 响应示例

```json
{
  "list": [
    {
      "title": "2026年618什么手机值得买？",
      "aweme_id": "7512345678901234567",
      "duration": 847,
      "cover_url": "https://p3-pc-sign.douyinpic.com/example.jpeg",
      "video_url": "https://www.douyin.com/video/7512345678901234567",
      "like_count": 116000,
      "author_name": "小白测评",
      "publish_time": "2026-05-20 12:00:00",
      "duration_text": "14:07"
    }
  ],
  "asked": 10,
  "count": 10,
  "keyword": "手机",
  "has_more": true,
  "tips": "极数本源 · https://apizero.cn"
}
```


## 5. 请求示例

将 `APIZERO_KEY` 换成真实 Key。成功后先判断 `code == 0` 再读 `data`。

### cURL

```bash
curl -X POST "https://v1.apizero.cn/api/douyin-search" \
  -H "Authorization: Bearer $APIZERO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "keyword": "手机",
  "cookie": "ttwid=...; sessionid=...",
  "count": "10",
  "offset": "0"
}'
```

### Python

```python
import os
import requests

resp = requests.post(
    "https://v1.apizero.cn/api/douyin-search",
    headers={"Authorization": f"Bearer {os.environ['APIZERO_KEY']}"},
    json={
    "keyword": "手机",
    "cookie": "ttwid=...; sessionid=...",
    "count": "10",
    "offset": "0",
},
    timeout=15,
)
resp.raise_for_status()
print(resp.json())
```

### JavaScript

```javascript
const res = await fetch("https://v1.apizero.cn/api/douyin-search", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.APIZERO_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "keyword": "手机",
  "cookie": "ttwid=...; sessionid=...",
  "count": "10",
  "offset": "0",
}),
});
const data = await res.json();
console.log(data);
```

### Go

```go
package main

import (
        "bytes"
        "fmt"
        "io"
        "net/http"
        "os"
)

func main() {
        body := []byte(`{"keyword":"手机","cookie":"ttwid=...; sessionid=...","count":"10","offset":"0"}`)
        req, err := http.NewRequest("POST", "https://v1.apizero.cn/api/douyin-search", bytes.NewReader(body))
        if err != nil { panic(err) }
        req.Header.Set("Authorization", "Bearer "+os.Getenv("APIZERO_KEY"))
        req.Header.Set("Content-Type", "application/json")
        resp, err := http.DefaultClient.Do(req)
        if err != nil { panic(err) }
        defer resp.Body.Close()
        b, _ := io.ReadAll(resp.Body)
        fmt.Println(string(b))
}
```

### Java

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

HttpClient client = HttpClient.newBuilder()
        .connectTimeout(Duration.ofSeconds(10))
        .build();
HttpRequest req = HttpRequest.newBuilder()
        .uri(URI.create("https://v1.apizero.cn/api/douyin-search"))
        .header("Authorization", "Bearer " + System.getenv("APIZERO_KEY"))
        .header("Content-Type", "application/json")
        .timeout(Duration.ofSeconds(15))
        .POST(HttpRequest.BodyPublishers.ofString("{\"keyword\":\"手机\",\"cookie\":\"ttwid=...; sessionid=...\",\"count\":\"10\",\"offset\":\"0\"}"))
        .build();
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());
```

### PHP

```php
<?php
$ch = curl_init("https://v1.apizero.cn/api/douyin-search");
$key = getenv("APIZERO_KEY");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer {$key}",
    "Content-Type: application/json",
  ],
  CURLOPT_POSTFIELDS => json_encode([
  "keyword" => "手机",
  "cookie" => "ttwid=...; sessionid=...",
  "count" => "10",
  "offset" => "0",
]),
  CURLOPT_RETURNTRANSFER => true,
]);
echo curl_exec($ch);
```

## 8. 错误码

先看业务 code。HTTP 也可能不是 200。

| 业务码 | HTTP | 说明 |
| --- | --- | --- |
| 0 | 200 | 成功 |
| 4000 | 400 | 参数错误 |
| 4011 | 401 | API Key 无效 |
| 4013 | 403 | API Key 已暂停 |
| 4014 | 403 | 当前 IP 不在 Key 白名单 |
| 4015 | 401 | 此接口需要 API Key |
| 4022 | 402 | 余额不足 |
| 4029 | 429 | 调用过快（QPS） |
| 4030 | 429 | 今日免费额度已用完 |
| 4040 | 503 | 接口已下线 |
| 4041 | 404 | 接口不存在 |
| 5000 | 500 | 服务器内部错误 |
| 5020 | 502 | 上游暂时不可用 |
| 5021 | 502 | 上游返回格式异常 |
| 5030 | 502 | 暂无可用节点 |


## 9. 变更日志

- **1.0.1** (2026-09-01): Cookie 改由调用方传入；GET 未编码时会把被拆开的 sessionid/ttwid 拼回 Cookie
- **1.0.0** (2026-09-01): 上架独立接口
