YouTube Download Error Codes: Age-Restricted, Geo-Blocked or Deleted? (2026)

Why YouTube downloads fail with a vague 'Video unavailable', how to tell age-restricted, geo-blocked, private and deleted videos apart, and every machine-readable error code the Vid Kraken API returns, with the message to show your users.

Vid Kraken · Updated October 19, 2026

Your app lets users import YouTube videos. A download fails, and all you have is Video unavailable or a generic 500. So the user sees "Download failed" and tries again, and it fails again. They have no idea that the video is age-restricted, locked to Japan, or was deleted last week.

To show a real reason, you need a downloader that returns a stable, machine-readable error code for each failure, not a sentence you have to parse. This guide explains why that's harder than it sounds, then lists every code the Vid Kraken API returns, what causes it, whether to retry, and what to tell your user.

Short version: every failed Vid Kraken download ends with an errorCode such as AGE_RESTRICTED, GEO_RESTRICTED, PRIVATE_VIDEO or VIDEO_NOT_FOUND. Except for NETWORK_ERROR, every code describes the video itself, so don't retry it. Show the user the reason. Failed downloads are never charged.

Why "Video unavailable" tells you almost nothing

yt-dlp, and most hosted APIs built on it, report failures as free text. That text comes from whatever YouTube showed the request, and YouTube isn't consistent about it:

  • A region lock often looks like a deleted video. When a request comes from a country where the video is blocked, YouTube frequently answers with a plain Video unavailable, the same words it uses for removed videos. In one week of failures we reviewed in September 2026, 46 of 50 "Video unavailable" errors were actually region locks, not missing videos.
  • The same video fails differently from different IPs. A datacenter IP might get "Sign in to confirm you're not a bot", while a residential one gets the real reason.
  • The wording changes. YouTube rewords its messages, and yt-dlp prefixes them with its own text. Code that matches on strings breaks without warning.

So a reliable error code can't come from string matching alone. Vid Kraken checks YouTube's own metadata for every video before downloading: region restrictions, the age-restriction flag, live status, category and the creator's download opt-out. It combines those facts with what each download attempt reports. That's how a region lock comes back as GEO_RESTRICTED and not as a vague "unavailable".

What a failed download looks like

You submit a job with POST /download, then poll GET /download/{jobId} or receive a webhook. A failed job looks like this:

{
  "jobId": "a3f1c2d4-5e6f-4a1b-9c2d-3e4f5a6b7c8d",
  "status": "FAILED",
  "errorCode": "GEO_RESTRICTED",
  "title": "Some region-locked video",
  "duration": 412,
  "url": "https://youtu.be/VIDEO_ID",
  "format": "1080",
  "language": null,
  "createdAt": "2026-10-19T09:12:03.000Z",
  "startedAt": "2026-10-19T09:12:04.000Z",
  "completedAt": "2026-10-19T09:12:06.000Z"
}

Branch on errorCode. It's a fixed list of values, documented in the API reference.

Every error code, and what to tell your user

errorCodeWhat happenedRetry?Message you can show the user
AGE_RESTRICTEDYouTube requires a signed-in, age-verified viewer, and none of our routes could get past the age gateNo"This video is age-restricted on YouTube and can't be imported."
GEO_RESTRICTEDThe video is region-locked to countries we can't download fromNo"This video isn't available in the regions we can download from."
PRIVATE_VIDEOThe owner has made the video privateNo"This video is private. Ask the owner to make it public or unlisted."
VIDEO_NOT_FOUNDNo video with this ID, or it was deletedNo"We couldn't find this video. It may have been deleted. Check the link."
MEMBERS_ONLYOnly the channel's paying members can watch itNo"This video is for channel members only."
PAYMENT_REQUIREDIt's a paid rental or purchaseNo"This video requires a purchase on YouTube."
DRM_PROTECTEDThe streams are DRM-encryptedNo"This video is copy-protected and can't be downloaded."
COPYRIGHTED_MUSICYouTube files the video under its Music categoryNo"Music videos can't be imported."
NO_DOWNLOAD_OPT_OUTThe creator added #nodownload to the tags or descriptionNo"The creator has opted out of downloads for this video."
VIDEO_PREMIEREA scheduled premiere that hasn't aired yetAfter it airs"This video premieres soon. Try again once it's live."
LIVE_STREAM_IN_PROGRESSThe stream is still liveAfter it ends"This is a live stream. Try again after it ends."
VIDEO_PROCESSINGYouTube is still processing a fresh uploadLater"YouTube is still processing this video. Try again in a few minutes."
FORMAT_UNAVAILABLEThe requested quality isn't offered for this videoWith another format"This quality isn't available. Try a lower one."
FILE_TOO_LARGEThe file in this format is bigger than we can deliverWith a lower format or a trim"This video is too large at this quality. Try a lower one or a shorter clip."
VIDEO_UNAVAILABLEYouTube won't serve it and gives no specific reason. Also used for live streams that were scheduled but never airedNo"This video is unavailable on YouTube."
NETWORK_ERRORA transient network failure on our sideYes, after a short backoffRetry silently first. If it keeps failing: "Something went wrong. Please try again."

Two rules cover anything that isn't in the table:

  • errorCode is null or a value you don't recognise: the failure was on our side and wasn't classified. Retry after a short backoff before treating it as final.
  • Everything in the table except NETWORK_ERROR is final. Retrying returns the same result, so show the message.

Age-restricted vs geo-blocked vs deleted

These three get confused most often, and users react to them differently. Here's how each one is detected and what it means.

Age-restricted (AGE_RESTRICTED). YouTube flags the video as 18+ and asks the viewer to sign in and confirm their age. Vid Kraken doesn't give up at the first age gate. It sends the job through routes that can clear it, and you never supply cookies or a YouTube account. You only get AGE_RESTRICTED once none of them could. Tell users the video is age-restricted. Don't suggest that the link is wrong.

Geo-blocked (GEO_RESTRICTED). YouTube's metadata lists which countries a video plays in. Vid Kraken can download from exits in about 50 countries and picks one that the video is allowed in. You get GEO_RESTRICTED only when the video is locked to countries none of those exits cover, and that answer usually comes back within seconds instead of after minutes of retries. The video exists and plays for some people, so tell the user it's region-locked, not missing.

Deleted or never existed (VIDEO_NOT_FOUND). If YouTube has no video for the ID at submit time, POST /download returns 404 straight away and no job is created. If it disappears while the job runs, the job fails with VIDEO_NOT_FOUND. Ask the user to check the link.

Check before you download

To warn users before they wait on a download, call POST /info. Info requests come from a separate quota and use no bandwidth. When a video is region-locked, the result includes regionRestriction:

{
  "title": "Some region-locked video",
  "duration": 412,
  "url": "https://youtu.be/VIDEO_ID",
  "regionRestriction": { "allowed": ["TR"] }
}

allowed means the video plays only in those countries. blocked means it plays everywhere except those. The field is absent when the video plays everywhere. A video with a short allowed list is worth flagging in your UI. The download may still work, since Vid Kraken tries to reach an allowed country, but it's the most likely to come back GEO_RESTRICTED.

Errors before a job starts: HTTP status codes

Some problems are caught before a job is created. These come back as HTTP errors from POST /download, with a JSON body like { "error": "..." }. None of them are charged.

StatusWhenWhat to do
400Missing or malformed URL, more than one URL, invalid format, startTime or endTime outside the video, a live stream that's still running or never aired, an invalid webhookUrlFix the request, or show the message
401Missing or invalid API keyCheck the Authorization: Bearer header
403Your plan's bandwidth or request allowance is used upUpgrade, or wait for the next billing cycle
404The video doesn't exist, or it's private or deletedAsk the user to check the link
429You already have 100 downloads queued or runningWait for some to finish, then resubmit. The X-Pending-Downloads header shows your count
503The download couldn't be queued for a momentRetry after a minute

Handling it in code

Here's a complete handler for a finished job. It works for both polling responses and webhook bodies, because the two have the same shape.

const USER_MESSAGES = {
  AGE_RESTRICTED: "This video is age-restricted on YouTube and can't be imported.",
  GEO_RESTRICTED: "This video isn't available in the regions we can download from.",
  PRIVATE_VIDEO: "This video is private. Ask the owner to make it public or unlisted.",
  VIDEO_NOT_FOUND: "We couldn't find this video. It may have been deleted.",
  MEMBERS_ONLY: "This video is for channel members only.",
  PAYMENT_REQUIRED: "This video requires a purchase on YouTube.",
  DRM_PROTECTED: "This video is copy-protected and can't be downloaded.",
  COPYRIGHTED_MUSIC: "Music videos can't be imported.",
  NO_DOWNLOAD_OPT_OUT: "The creator has opted out of downloads for this video.",
  VIDEO_UNAVAILABLE: "This video is unavailable on YouTube.",
  VIDEO_PREMIERE: "This video premieres soon. Try again once it's live.",
  LIVE_STREAM_IN_PROGRESS: "This is a live stream. Try again after it ends.",
  VIDEO_PROCESSING: "YouTube is still processing this video. Try again in a few minutes.",
  FORMAT_UNAVAILABLE: "This quality isn't available. Try a lower one.",
  FILE_TOO_LARGE: "This video is too large at this quality. Try a lower one or a shorter clip.",
};

function handleFinishedJob(job, attempt = 1) {
  if (job.status === "COMPLETED") {
    return { ok: true, url: job.downloadUrl, size: job.fileSize };
  }

  const message = USER_MESSAGES[job.errorCode];
  if (message) {
    // A property of the video: final. Show the reason.
    return { ok: false, retry: false, code: job.errorCode, message };
  }

  // NETWORK_ERROR, null or an unknown code: transient, so retry with backoff.
  if (attempt < 3) {
    return { ok: false, retry: true, delaySeconds: 30 * attempt };
  }
  return { ok: false, retry: false, message: "Something went wrong. Please try again later." };
}

If you pass a webhookUrl when you submit, Vid Kraken POSTs this same body to you when the job finishes. It signs the request with X-VidKraken-Signature and retries for about 4 hours if your endpoint doesn't return a 2xx. See the docs for verifying the signature.

Migrating from yt-dlp: which message becomes which code

If you're replacing your own yt-dlp setup, these are the messages you've been parsing and the codes that replace them:

yt-dlp / YouTube message (typical wording)errorCode
"Sign in to confirm your age"AGE_RESTRICTED
"The uploader has not made this video available in your country", or a bare "Video unavailable" on a region-locked videoGEO_RESTRICTED
"Private video. Sign in if you've been granted access"PRIVATE_VIDEO
"Video unavailable. This video has been removed"VIDEO_NOT_FOUND
"Join this channel to get access to members-only content"MEMBERS_ONLY
"This video requires payment to watch"PAYMENT_REQUIRED
"Premieres in …"VIDEO_PREMIERE
"YouTube is still processing this video"VIDEO_PROCESSING
"Requested format is not available"FORMAT_UNAVAILABLE
"This video is DRM protected"DRM_PROTECTED
"Sign in to confirm you're not a bot", HTTP 403, timeoutsNot passed on to you. These are bot checks and blocked requests, and Vid Kraken retries them on other routes

The last row matters most. With your own yt-dlp, bot checks and 403s are most of your failures, and they look like errors about the video. With a managed API, they're the provider's problem and get retried, so the only errors left for you are real properties of the video.

What failures cost

Nothing. Vid Kraken bills only for the bandwidth of files it delivers, so failed jobs, polling and info lookups cost nothing. Failures caused by the video, meaning every code in the table except NETWORK_ERROR, also don't count against the success rate on our reliability page, because no downloader can fetch a private or deleted video. Everything else does count. Over the last 30 days that rate was 99.993% across more than 2.5 million downloads.

Try Vid Kraken

Paste a YouTube URL and get a direct download link.

Paste a link. See it work.

Paste a YouTube link, pick a format, and hit download.

Tell your users why a download failed.

Every failed Vid Kraken download comes back with a machine-readable errorCode. No string parsing, no "Download failed", and failures are never charged.

Start free

Frequently asked questions

Which YouTube download APIs return machine-readable error codes?+

Vid Kraken returns a stable errorCode on every recognised failure, from a fixed list of 16 values including AGE_RESTRICTED, GEO_RESTRICTED, PRIVATE_VIDEO, VIDEO_NOT_FOUND, MEMBERS_ONLY and LIVE_STREAM_IN_PROGRESS. yt-dlp and many hosted APIs built on it only return free-text messages, which you have to parse and which change when YouTube rewords them.

How can I tell an age-restricted YouTube video from a geo-blocked one?+

Use a downloader that checks YouTube's metadata as well as the error text. Vid Kraken reads the video's age-restriction flag and region restrictions before downloading, so an age-gated video fails with AGE_RESTRICTED and a region-locked one with GEO_RESTRICTED. You can also call POST /info first: region-locked videos include a regionRestriction field listing the allowed or blocked countries.

Why does yt-dlp say 'Video unavailable' for videos that exist?+

YouTube often answers a request from a country where the video is blocked with the same 'Video unavailable' message it uses for deleted videos. In a week of failures we reviewed in September 2026, 46 of 50 'Video unavailable' errors were region locks. Checking the video's region restrictions in YouTube's metadata is the reliable way to tell the two apart.

How do I tell a geo-restricted YouTube video from a deleted one?+

With Vid Kraken, a deleted or non-existent video returns HTTP 404 when you submit it, or fails with VIDEO_NOT_FOUND if it disappears while the job runs. A region-locked video fails with GEO_RESTRICTED. Before downloading, POST /info shows a regionRestriction field for region-locked videos.

Should I retry a failed YouTube download?+

Only for transient failures. With Vid Kraken, retry after a short backoff when errorCode is NETWORK_ERROR, null or a value you don't recognise. Every other code describes the video, such as private, age-restricted or deleted, and will fail the same way again. VIDEO_PREMIERE, LIVE_STREAM_IN_PROGRESS and VIDEO_PROCESSING are worth retrying later, once the video has aired, ended or finished processing.

Am I charged for failed YouTube downloads?+

Not with Vid Kraken. You pay only for the bandwidth of files that are delivered. Failed jobs, polling and metadata lookups are free.

Can Vid Kraken download age-restricted YouTube videos?+

Often, yes. Vid Kraken routes age-restricted videos through download paths that can clear YouTube's age gate, and you don't supply cookies or a YouTube account. If none of them can, the job fails with AGE_RESTRICTED and is not charged.