yt-dlp and Instagram: “login required”, empty results and what to use instead
yt-dlp is a superb tool and Instagram is the site it is worst at, through no fault of its own: Instagram serves its pages behind a login wall to anything that does not look like a signed-in browser, and a server IP never does. This guide explains what the error means, why the usual fixes decay, and what the same link looks like through a downloader API — measured, not asserted.
Updated
What the error actually says
The message bundles three different situations into one line: the post is unavailable, your IP hit a limit, or Instagram wants a session. On a datacenter address it is nearly always the third — the request never reaches the post, it hits the login wall first. yt-dlp cannot tell them apart from the outside, which is why its advice is “try cookies”.
Terminal$ yt-dlp https://www.instagram.com/reel/ABC123/ ERROR: [Instagram] ABC123: Requested content is not available, rate-limit reached or login required. Use --cookies, --cookies-from-browser, --username and --password, or --netrc
Why cookies fix it only for a while
Exporting a browser session works the same day and stops working within days: Instagram rotates session tokens, ties them to the IP and device they came from, and flags accounts that fetch hundreds of posts from a server. The fix then becomes a rota of accounts and proxies to manage — which is a scraping operation, not a download command.
What we measured from a server IP
Before removing yt-dlp from our own pipeline we ran 13 public reels through it from a Hetzner address: 0 succeeded, and single calls ran up to 45 seconds before failing. Instagram's own embed endpoint did no better — 0 of 11. The links were fine; a home connection resolved them. It is the address, not the tool.
The same link through the API
One GET, no cookies on your side, no browser. The resolving happens on a chain of sources that do not depend on an Instagram session, and the response has the same shape for a reel, a post, a carousel or a story. The median response time is measured live and shown on the front page.
Terminalcurl -s "https://api.saveapi.org/v1/instagram" \ -H "Authorization: Bearer sk_live_xxxx" \ -G --data-urlencode "url=https://www.instagram.com/reel/ABC123/"
A drop-in replacement in Python
If your code called yt-dlp for the URL and then downloaded the file yourself, this is the same contract: pass the link, get back direct media URLs, fetch the bytes from the CDN. What you lose is yt-dlp's format selection; the response carries one rendition per Instagram media, so there is nothing to select.
Pythonimport httpx
def resolve(link: str) -> list[str]:
"""yt-dlp o'rniga: havoladan to'g'ridan-to'g'ri media URL'lar."""
r = httpx.get(
"https://api.saveapi.org/v1/download",
params={"url": link},
headers={"Authorization": "Bearer sk_live_xxxx"},
timeout=30,
)
data = r.json()
if not data["success"]:
raise RuntimeError(data["error"]["code"])
return [m["url"] for m in data["medias"]]Questions before you build
- Can yt-dlp still download Instagram at all?
- From a residential connection with fresh cookies, often yes. From a server, in our measurement, no — and it is the server case that matters for a bot or a backend.
- Does the API use my Instagram account?
- No. There is no login, no cookie and no account of yours involved; the request is just the link and your API key.
- What does a failed link cost?
- A link Instagram itself rejects — private or deleted — costs 0.1 credits and comes back with a stable code. Anything that fails on our side costs nothing, and the response says which it was.
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.