G GeekTBVIDEO 申请 API Key

首页 / 文档

快速开始

接口都是普通的 HTTP GET,返回 JSON。播放走 HLS,不提供网页版播放器。

概述

Base URLhttps://api.geektb.com/v1
响应格式application/json; charset=utf-8
HTTP 方法仅 GET 与 HEAD,其它方法返回 405
API 版本2026-10-15(路径里的 v1 为大版本)
片库快照2026-11-08,每个响应的 meta.snapshot 里也有
可用区us-west-1(CDN 边缘按就近调度)

响应主体有两种形状。成功时是数据信封:

{
  "data": [],
  "meta": {
    "total": 0,
    "snapshot": "2026-11-08"
  }
}

失败时是错误信封,HTTP 状态码同时反映错误类别:

{
  "error": {
    "type": "invalid_request_error",
    "code": "unknown_endpoint",
    "message": "No endpoint matches this path."
  }
}

第一次请求

视频列表不需要密钥,直接请求即可:

curl -s https://api.geektb.com/v1/videos

返回里每个视频带 hls_master(master playlist)与 poster, 以及档位摘要的入口。要做清晰度列表请单独取单个视频。

认证

接口分两类:

把 token 放在 Authorization 头里,前缀 Bearer:

curl -s "https://api.geektb.com/v1/streams?video_id=v_8f3a1c" \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx"

缺少该头返回 401 missing_api_key,token 无效或已吊销返回 401 invalid_api_key,两种情况的响应都带 WWW-Authenticate。

如何拿到 token

目前采用人工开通:发邮件到 api@geektb.com,写明用途、预估日播放量与回调域名, 一般一个工作日内在同一线程回复。开通后 token 形如 sk_live_ 加 32 位随机串, 只在服务端保存哈希,遗失只能重新签发。

不要把 token 写进 App 或网页的前端代码。移动端与网页播放请由你自己的服务端中转签发。

播放链路

典型流程四步,全部是普通 HTTP:

  1. GET /v1/videos/{id} 拿元数据与档位,决定首帧用哪一档。
  2. GET /v1/streams?video_id={id} 签发播放会话,返回带签名的 manifest_url 与过期时间(默认 1800 秒)。
  3. 把 manifest_url 交给播放器(hls.js / AVPlayer / ExoPlayer 均可)。 master playlist 内含六档,播放器按带宽自选。
  4. 切片为 fMP4、6 秒一段,Cache-Control: immutable,可被任意中间缓存复用。

签名绑定会话与来源网段,换设备或换网络需要重新签发;重新签发不计入额外配额, 但受同一条每分钟限流约束。

所有切片默认 AES-128 加密,密钥随签名地址下发,不单独暴露。 不使用 DRM(Widevine / FairPlay);若你的内容需要硬件级保护,这一版不适用。

错误处理

所有错误都遵循同一个信封,error.type 用于程序分支, error.message 面向人阅读:

error.codeHTTP含义
unknown_endpoint404请求路径不匹配任何接口。检查路径拼写与版本前缀。
video_not_found404视频 ID 不存在或已下架。
method_not_allowed405接口只接受 GET / HEAD,响应带 Allow 头。
missing_api_key401播放与用量接口未携带 Authorization 头。
invalid_api_key401密钥无效、已吊销或不属于该环境。
too_many_requests429超过当前套餐的每分钟上限,响应带 Retry-After。
forbidden_host421Host 头与本站不匹配,请求被拒绝。

限流

计数窗口固定 60 秒,按密钥(无密钥时按来源 IP)分别计量。超限时返回 429, 并带 Retry-After 秒数:

{
  "error": {
    "type": "rate_limit_error",
    "code": "too_many_requests",
    "message": "Too many requests. See Retry-After."
  }
}

各套餐阈值可以从 /v1/rate-limit 读取,不要在客户端硬编码:

curl -s https://api.geektb.com/v1/rate-limit

缓存与条件请求

元数据是不可变快照(每次片库更新会换 meta.snapshot),因此响应带强校验器:

带上 If-None-Match 可以把未变更的请求压成 304,不消耗响应体:

curl -s https://api.geektb.com/v1/videos \
  -H 'If-None-Match: "6af5bd69-2e47"' \
  -o /dev/null -w '%{http_code}\n'   # 304

版本与弃用

路径里的 v1 是大版本,字段级的演进通过响应里的 meta 与 /v1/version 的 deprecations 数组公告。弃用条目包含 field、announced、remove_after 三个字段, remove_after 距今至少 180 天。

当前无弃用字段:

{
  "api_version": "2026-10-15",
  "service_version": "2.3.1",
  "base_url": "https://api.geektb.com/v1",
  "deprecations": [],
  "changelog": "https://api.geektb.com/changelog.html"
}

客户端示例

curl

curl -s https://api.geektb.com/v1/videos | jq '.data | length'

Python

import requests

r = requests.get("https://api.geektb.com/v1/videos", timeout=10)
r.raise_for_status()
for v in r.json()["data"]:
    if v["duration_sec"] > 900:
        print(v["id"], v["title"], v["duration_sec"])

Node.js(18+,内置 fetch)

const res = await fetch("https://api.geektb.com/v1/videos");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { data, meta } = await res.json();
console.log(meta.total, data.slice(0, 3).map(v => v.id));

获取支持

接口报错、视频缺失或需要提额,发邮件到 api@geektb.com,附上请求路径、 时间(UTC)与响应里的 X-Request-Id。每个响应都会带这个头,是我们查日志的唯一线索。

本站是只读接口服务,不提供网页版播放器或在线调试。调试请用 curl 或任意 HTTP 客户端。