YouTube 代理下载

POST/openapi/v1/youtube/proxy-download — 视频每 10 分钟消耗 4 积分。

传入 YouTube 链接,返回与单个帖子提取相同的结构,并为 1080p 及以下的每个档位附上代理链接。在云服务器、VPS、Serverless 函数上直接下载代理链接即可,无需维护住宅代理、cookies 或 PO token。

为什么服务器下载 YouTube 会返回 403 Forbidden

/extract/post 拿到 googlevideo.com 直链后,从服务器下载却报 HTTP Error 403: Forbidden。我们的实测结果:

  • 机房 IP 被拒绝。 同一条直链,从家庭宽带下载正常(206),从亚洲、欧洲多个地区的机房服务器下载返回 403。直链并不绑定提取时的 IP,YouTube 看的是下载时的 IP。
  • 部分链接只允许美国 IP 下载。gcr=us 参数的链接,美国以外的 IP 下载一律返回 403

所以只在提取环节想办法(换工具、配 cookies 或 PO token)解决不了问题:只要下载发生在机房 IP 上,照样返回 403

工作原理

  1. 你把 YouTube 链接发给 POST /openapi/v1/youtube/proxy-download
  2. 我们提取视频,返回常规的提取结果,并为 1080p 及以下的每个档位加上代理链接。
  3. 你下载代理链接。我们的下载网络从 YouTube 取回文件并流式转发给你,YouTube 看不到你服务器的 IP。

响应里的直链(video_urlaudio_urlresource_url)全部保留:1080p 以上的档位只有直链;如果你的服务器能正常下载直链(比如在家庭宽带上),也可以继续用直链。

请求与响应

curl -X POST https://api.snapany.com/openapi/v1/youtube/proxy-download \
  -H "Authorization: Bearer sk_snapany_xxx" \
  -H "Accept-Language: zh" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'

支持 youtube.com 链接(普通视频、Shorts、直播回放,以及 m.music. 子域)和 youtu.be 链接。其他链接——包括 youtube-nocookie.com 嵌入链接和跳转到 YouTube 的短链(如 t.co)——返回 HTTP 400、错误码 unsupported_site,不扣费。

响应示例(有删减,ID 与链接均为占位符):

{
  "site": "youtube",
  "title": "Scenic tour of the Karst mountains",
  "medias": [
    {
      "media_type": "video",
      "resource_url": "https://rr1---sn-example.googlevideo.com/videoplayback?itag=18&expire=1700000000&sig=EXAMPLE",
      "resource_proxy_url": "https://proxy.example.com/proxy?payload=EXAMPLE_360P_RESOURCE",
      "preview_url": "https://i.ytimg.com/vi/EXAMPLE0001/maxresdefault.jpg",
      "duration": 754,
      "variants": [
        {
          "quality": 2160,
          "quality_label": "4K",
          "fps": 30,
          "video_url": "https://rr1---sn-example.googlevideo.com/videoplayback?itag=313&expire=1700000000&sig=EXAMPLE",
          "video_ext": "webm",
          "video_codec": "vp9",
          "video_filesize": 687194112,
          "audio_url": "https://rr1---sn-example.googlevideo.com/videoplayback?itag=251&expire=1700000000&sig=EXAMPLE",
          "audio_ext": "weba",
          "audio_codec": "opus",
          "audio_filesize": 11354112
        },
        {
          "quality": 1080,
          "quality_label": "1080p",
          "fps": 30,
          "video_url": "https://rr1---sn-example.googlevideo.com/videoplayback?itag=137&expire=1700000000&sig=EXAMPLE",
          "video_proxy_url": "https://proxy.example.com/proxy?payload=EXAMPLE_1080P_VIDEO",
          "video_ext": "mp4",
          "video_codec": "h264",
          "video_filesize": 181403648,
          "audio_url": "https://rr1---sn-example.googlevideo.com/videoplayback?itag=140&expire=1700000000&sig=EXAMPLE",
          "audio_proxy_url": "https://proxy.example.com/proxy?payload=EXAMPLE_1080P_AUDIO",
          "audio_ext": "m4a",
          "audio_codec": "aac",
          "audio_filesize": 12202891
        },
        {
          "quality": 360,
          "quality_label": "360p",
          "fps": 30,
          "video_url": "https://rr1---sn-example.googlevideo.com/videoplayback?itag=18&expire=1700000000&sig=EXAMPLE",
          "video_proxy_url": "https://proxy.example.com/proxy?payload=EXAMPLE_360P_VIDEO",
          "video_ext": "mp4",
          "video_codec": "h264",
          "video_filesize": 50847744
        }
      ]
    },
    {
      "media_type": "audio",
      "resource_url": "https://rr1---sn-example.googlevideo.com/videoplayback?itag=140&expire=1700000000&sig=EXAMPLE",
      "resource_proxy_url": "https://proxy.example.com/proxy?payload=EXAMPLE_AUDIO_RESOURCE",
      "duration": 754,
      "variants": [
        {
          "audio_url": "https://rr1---sn-example.googlevideo.com/videoplayback?itag=140&expire=1700000000&sig=EXAMPLE",
          "audio_proxy_url": "https://proxy.example.com/proxy?payload=EXAMPLE_AUDIO_EN",
          "audio_ext": "m4a",
          "audio_codec": "aac",
          "audio_filesize": 12202891,
          "language_tag": "en",
          "language_name": "English"
        }
      ]
    }
  ],
  "id": "EXAMPLE0001",
  "post_url": "https://www.youtube.com/watch?v=EXAMPLE0001",
  "created_at": "2025-06-02T12:00:00.000Z",
  "author": { "username": "@travelchannel", "display_name": "Travel Channel" }
}

在单个帖子提取的响应之外多出的字段:

字段位置何时出现
video_proxy_url档位(variant)该档位在 1080p 及以下
audio_proxy_url档位(variant)1080p 及以下且音频分离的视频档位;音频媒体的每个语言档位
resource_proxy_url视频 / 音频媒体resource_url 对应的文件在 1080p 及以下

超过 3 GB 的文件不带代理链接。响应里没有 preview_proxy_url,也没有过期时间字段——有效期规则见下文。

完整 schema 见 API 参考

video_proxy_url 下载

选有 video_proxy_url 的最高档位。如果它同时有 audio_proxy_url,说明音视频是分离的流:分别下载后用 ffmpeg 合并;只有 video_proxy_url 时它就是完整文件。代理链接不需要 API key,也不需要额外的请求头。

1080p 及以下的档位通常是 MP4(H.264)视频加 M4A(AAC)音频,-c copy 流复制即可合成标准 MP4,无需重编码。只想要一个现成文件时,用视频媒体的 resource_proxy_url(通常清晰度较低、自带音轨);只要音频时,用音频媒体的 resource_proxy_url

curl(配合 jq):

# 1. 获取代理链接
curl -s -X POST https://api.snapany.com/openapi/v1/youtube/proxy-download \
  -H "Authorization: Bearer sk_snapany_xxx" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}' > post.json

# 2. 选有代理链接的最高档位(1080p 及以下)
jq '[.medias[] | select(.media_type == "video") | .variants[]? | select(.video_proxy_url)] | max_by(.quality)' post.json > variant.json

# 3. 分别下载音视频流:-f 遇到 HTTP 错误直接失败,-C - 支持断点续传
#    (档位没有 audio_proxy_url 时视频文件已经完整,跳过音频那一行和第 4 步)
curl -fL -C - -o video.mp4 "$(jq -r .video_proxy_url variant.json)"
curl -fL -C - -o audio.m4a "$(jq -r .audio_proxy_url variant.json)"

# 4. 合并:流复制,不重编码
ffmpeg -i video.mp4 -i audio.m4a -c copy merged.mp4

Python

import subprocess
import requests

response = requests.post(
    "https://api.snapany.com/openapi/v1/youtube/proxy-download",
    headers={"Authorization": "Bearer sk_snapany_xxx"},
    json={"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"},
)
response.raise_for_status()
post = response.json()

# 有代理链接的最高档位(代理链接只在 1080p 及以下的档位出现)
video = next(media for media in post["medias"] if media["media_type"] == "video")
variant = max((v for v in video["variants"] if v.get("video_proxy_url")), key=lambda v: v["quality"])


def download(url, path):
    # 不需要 API key 或请求头:代理链接本身就是访问凭证
    with requests.get(url, stream=True, timeout=60) as r:
        r.raise_for_status()  # 如 404 payload_expired
        with open(path, "wb") as f:
            for chunk in r.iter_content(1 << 20):
                f.write(chunk)


download(variant["video_proxy_url"], "video.mp4")
if variant.get("audio_proxy_url"):
    download(variant["audio_proxy_url"], "audio.m4a")
    subprocess.run(["ffmpeg", "-i", "video.mp4", "-i", "audio.m4a", "-c", "copy", "merged.mp4"], check=True)

Node.js(18+):

import { spawnSync } from 'node:child_process'
import { createWriteStream } from 'node:fs'
import { Readable } from 'node:stream'
import { pipeline } from 'node:stream/promises'

const response = await fetch('https://api.snapany.com/openapi/v1/youtube/proxy-download', {
  method: 'POST',
  headers: { Authorization: 'Bearer sk_snapany_xxx', 'Content-Type': 'application/json' },
  body: JSON.stringify({ url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' }),
})
if (!response.ok) throw new Error(`${response.status} ${await response.text()}`)
const post = await response.json()

// 有代理链接的最高档位(代理链接只在 1080p 及以下的档位出现)
const video = post.medias.find(media => media.media_type === 'video')
const variant = video.variants.filter(v => v.video_proxy_url).sort((a, b) => b.quality - a.quality)[0]

async function download(url, path) {
  const res = await fetch(url)
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`) // 如 404 payload_expired
  await pipeline(Readable.fromWeb(res.body), createWriteStream(path))
}

await download(variant.video_proxy_url, 'video.mp4')
if (variant.audio_proxy_url) {
  await download(variant.audio_proxy_url, 'audio.m4a')
  spawnSync('ffmpeg', ['-i', 'video.mp4', '-i', 'audio.m4a', '-c', 'copy', 'merged.mp4'], { stdio: 'inherit' })
}

大文件建议用 Range 请求下载(curl 的 -C -,或自己带 Range: bytes=… 请求头),中断后可以续传,不必从头再来。

有效期、限制与计费

有效期:代理链接自响应起至少 30 分钟、最长 1 小时有效。拿到响应就开始下载,按 30 分钟规划。

过期后代理链接返回 HTTP 404

{ "code": "payload_expired", "message": "link has expired" }

重新调用接口拿新链接即可,重试同一条链接没有用。

有效期内代理链接返回 HTTP 403,说明 YouTube 拒绝了这次请求:

{ "code": "upstream_error", "message": "upstream refused the request", "retryable": true, "upstream_status": 403 }

这种情况很少见,重试一次通常就能成功。

限制

  • 代理链接最高 1080p。1080p 以上的档位(1440p、4K、8K)保留在响应里,但只有直链,见下方常见问题。
  • 超过 3 GB 的文件不带代理链接。
  • 仅支持 YouTube 链接。无法提取的链接——播放列表、频道、正在直播的视频、会员专享或已删除的视频等——返回 HTTP 400(见错误码),不扣费。

计费:返回响应后按响应内容扣费。

响应内容扣费
有视频代理链接(档位的 video_proxy_url,或视频媒体的 resource_proxy_url每 10 分钟视频 4 积分(不足 10 分钟按 10 分钟计)
只有音频代理链接4 积分
没有代理链接1 积分

示例:10 分钟以内的视频 4 积分,10–20 分钟 8 积分,1 小时 24 积分。时长取自 duration 字段,缺失时按 4 积分计。上面的响应示例(duration: 754,约 12.5 分钟)消耗 8 积分。失败的调用(不支持的链接、提取失败)不扣费。

常见问题

需要自己维护住宅代理吗?

用本接口就不需要。住宅代理常见价格是每 GB $3–4,一个 20 分钟的 1080p 视频(约 270–470 MB)光代理流量就要约 $0.81–1.88,还不算维护 cookies 和 PO token 的工作量。通过本接口下载同一个视频消耗 8 积分,按积分套餐折算约 $0.0053–0.0088。

遇到「Sign in to confirm you're not a bot」怎么办?

这个报错出现在提取阶段:YouTube 把请求判定为自动化访问时会要求登录,服务器 IP 上很常见。用本接口时提取在我们这边完成,你的服务器不需要 cookies、登录账号或 PO token。

能在 AWS Lambda、EC2、VPS 上用吗?

可以。你的服务器只和 SnapAny API 及我们的下载网络通信,不直连 YouTube,所以跑在 AWS Lambda、EC2、其他云还是 VPS 上都一样。注意运行环境自身的限制:Lambda 有最长运行时间和 /tmp 容量上限,长视频建议直接流式写入对象存储(如 S3),或用 Range 分段下载。

能通过代理下载 4K 吗?

不能,代理链接最高 1080p。1440p、4K、8K 档位保留在响应里,但只有直链。YouTube 这些清晰度只提供 VP9 或 AV1 编码,文件体积是 1080p 的 3–7 倍,通常还需要转码。这些直链从家庭宽带下载一般没有问题。如果你需要通过代理下载更高清晰度,请联系我们。