saveapi.org

yt-dlp says “Sign in to confirm you're not a bot” — what to do on a server

On a laptop yt-dlp downloads YouTube fine. Move the same command to a VPS and it answers “Sign in to confirm you're not a bot” — on nearly every video. Here is what the message means, what we measured from a datacenter address, and the API call that replaces it.

Updated

What the error actually says

YouTube's player endpoint refused to return stream data and asked for a signed-in session. yt-dlp relays that and suggests cookies, because a session is the only thing it can add on its side. The video is public; the request was rejected before it got anywhere near the video.

Terminal
$ yt-dlp https://www.youtube.com/watch?v=aqz-KE-bpKQ
ERROR: [youtube] aqz-KE-bpKQ: Sign in to confirm you're not a bot.
Use --cookies-from-browser or --cookies for the authentication.
See the yt-dlp FAQ for how to manually pass cookies.

It is the address, not the tool

We measured it before building our own pipeline: 16 randomly chosen public videos from a Hetzner address — 0 of 16 with yt-dlp's default clients (one famous video that works everywhere is an anomaly, not a signal). The same binary behind an exit address YouTube trusts: 22 of 22. Datacenter ranges are scored as bots as a whole; the fix is not a flag.

Why cookies are a treadmill

Exporting a browser session works today. Then YouTube ties the cookies to the IP and device they came from, rotates them, and flags the account when it sees hundreds of downloads from a server. You end up running a rota of throwaway accounts, PO-token generators and proxies — a scraping operation, not a download command.

Step one: what does the video offer

One GET with the link. The answer lists every rendition with its exact size in bytes, plus title, duration and thumbnail — the part of yt-dlp -F you actually used. No links yet: this call is 2 credits and is safe to cache.

Terminal
curl -s "https://api.saveapi.org/v1/youtube/info" \
  -H "Authorization: Bearer sk_live_xxxx" \
  -G --data-urlencode "url=https://www.youtube.com/watch?v=dQw4w9WgXcQ"

Step two: build the one you want

Pass the same link and a quality from the list. The response is a signed link that streams the MP4 (or M4A/MP3) from our side, with filename, file_size and an expiry. The stream comes through us on purpose: YouTube signs its media URLs to the requesting IP, so a raw link would answer 403 on your server.

Terminal
curl -s "https://api.saveapi.org/v1/youtube/create" \
  -H "Authorization: Bearer sk_live_xxxx" \
  -G --data-urlencode "url=https://www.youtube.com/watch?v=aqz-KE-bpKQ" \
  --data-urlencode "quality=720p"

A drop-in replacement in Python

If your code called yt-dlp for a file and then did something with it, this is the same contract: pass the link, get a URL to fetch. What you lose is format selectors like bestvideo+bestaudio; what you get is an exact size before you commit and no bot wall to babysit.

Python
import httpx

API = "https://api.saveapi.org/v1/youtube"
HEADERS = {"Authorization": "Bearer sk_live_xxxx"}

def youtube_link(url: str, quality: str = "720p") -> dict:
    """Instead of yt-dlp -f: list the renditions, then build one."""
    info = httpx.get(f"{API}/info", params={"url": url},
                     headers=HEADERS, timeout=30).json()
    if not info["success"]:
        raise RuntimeError(info["error"]["code"])
    available = {f["quality"] for f in info["formats"]}
    if quality not in available:
        quality = info["formats"][-1]["quality"]   # highest one this video has
    made = httpx.get(f"{API}/create", params={"url": url, "quality": quality},
                     headers=HEADERS, timeout=60).json()
    if not made["success"]:
        raise RuntimeError(made["error"]["code"])
    return made   # url, filename, file_size, expires

Questions before you build

Can yt-dlp still download YouTube from a home connection?
Usually yes. The wall is about the address class; a residential IP with a browser-like client passes most of the time. Servers are where it fails, and servers are where bots and backends run.
Does the API use my YouTube account or cookies?
No. The request is the link and your API key. There is no account of yours involved and nothing to rotate.
What does it cost per video?
12 credits for info and create together — with the $9 pack that is about a tenth of a cent per download. A link YouTube itself refuses costs 0.1 credit; a failure on our side costs nothing.

Platform references used here

Other guides

Run this guide with your own key

Key in under a minute. 1,000 free credits, no card, no expiry.

Get a key and try itFull API reference