---
title: Vid Kraken API documentation
description: Every endpoint, request and response of the Vid Kraken YouTube download API.
url: https://vidkraken.com/docs
updated: '2026-10-24'
---

# Vid Kraken — YouTube Download API

For AI agents: docs index at https://vidkraken.com/docs/llms.txt; any page as Markdown by adding .md to its URL.

REST API to download videos from YouTube and to fetch direct download
links from YouTube, Instagram, TikTok, and Facebook.

## Getting Started

- Sign up at [vidkraken.com](https://vidkraken.com)

- Grab your API key from the [dashboard](https://vidkraken.com/dashboard)

- Pass it on every request as: `Authorization: Bearer YOUR_API_KEY`

## Usage & billing

You pay for bandwidth only. Every plan includes a monthly bandwidth
allowance; each download counts its actual file size against it,
with a 20 MB minimum per download. There are no per-download fees.
Downloads and metadata lookups are counted against generous
per-plan quotas that are sized so bandwidth always runs out first.
Failures are never charged.

| Endpoint | Counts against |
|---|---|
| `POST /download` | Bandwidth at actual file size (20 MB minimum) |
| `POST /info` | 1 info request |
| `POST /list-channel` | Account add-on — contact us to enable |
| `POST /client-download` | Account add-on — contact us to enable |

Polling endpoints (`GET /download/{jobId}`, `GET /info/{jobId}`,
`GET /client-download/{jobId}`) are free, as is `GET /me` for
checking your plan and remaining balances. Failed jobs, auth
failures (`401`) and limit errors (`403`, `429`) are not charged.

Each account may have at most 100 downloads queued or in progress at
once; `POST /download` returns `429` beyond that until some finish.
Every `POST /download` response carries the IETF `RateLimit-Policy`
and `RateLimit` headers for this cap (`"pending";r=37` means 37 more
downloads can be queued now), and the `429` adds `Retry-After`.

## Safe retries (Idempotency-Key)

`POST /download` and `POST /info` accept an optional `Idempotency-Key`
header (any unique string, e.g. a UUID). If a request times out or the
connection drops, retry it with the same key: you get the original
job back instead of starting, and paying for, a second one. Keys are
kept for 24 hours per account. Reusing a key with a different body
returns `422`; retrying while the first request is still running
returns `409` (wait `Retry-After` seconds). Failed requests aren't
stored, so retrying one with the same key runs it again. A replayed
response carries `Idempotent-Replayed: true`.

## Errors

Every error response is JSON with a human-readable `error` and a
machine-readable `code` (see `ApiError`); some add `details`. Branch
on `code`, not on the message:

| `code` | Status | What to do |
|---|---|---|
| `INVALID_REQUEST` | 400 | Fix the request; retrying it unchanged fails again |
| `UNAUTHORIZED` | 401 | Send a valid API key |
| `LIMIT_REACHED` | 403 | The plan's allowance is used up; upgrade or wait for the next cycle |
| `NOT_FOUND` | 404 | The job does not exist, has expired or is not yours |
| `VIDEO_NOT_FOUND` | 404 | No such video: private, deleted or a wrong id |
| `IDEMPOTENCY_CONFLICT` | 409 | A request with this `Idempotency-Key` is still running; retry after `Retry-After` seconds |
| `IDEMPOTENCY_KEY_REUSED` | 422 | This `Idempotency-Key` was used with a different body; use a new key |
| `TOO_MANY_PENDING` | 429 | Wait `Retry-After` seconds or until some downloads finish |
| `INTERNAL_ERROR` | 500 | Our fault; retry after a short backoff |
| `UNAVAILABLE` | 503 | Temporarily unavailable; retry after a short backoff |

A download that was accepted but then failed is not an HTTP error: its
status response says `FAILED` with an `errorCode` for the video (see
`DownloadErrorCode`).

## Versioning and deprecation

The API is versioned in the path (`/api/v2`). Within v2 we only make
backward-compatible changes: new endpoints, new optional request
fields, new response fields and new enum values, so handle unknown
`code`, `errorCode` and `status` values gracefully. Breaking changes
ship as a new version (`/api/v3`), and v2 keeps working alongside it.

Before an endpoint, field or version is removed, you get at least
**3 months' notice**: account owners are emailed, the change is
documented here, and affected responses carry a `Deprecation` header
(RFC 9745) and a `Sunset` header (RFC 8594) with the removal date,
plus a `Link` to migration notes. Nothing is deprecated today.

Base URL: `https://vidkraken.com/api/v2`

## Endpoints

### [Info](https://vidkraken.com/docs/endpoint/tag/info.md)

Fetch metadata for a YouTube video without queueing a download.

- [Submit a video-info job](https://vidkraken.com/docs/endpoint/submit-video-info.md): `POST /info`. Queue a job that fetches metadata for a YouTube video — title and duration.
- [Get video-info job status](https://vidkraken.com/docs/endpoint/get-video-info-status.md): `GET /info/{jobId}`. Poll the status of a previously-submitted video-info job.

### [Download](https://vidkraken.com/docs/endpoint/tag/download.md)

Server-side YouTube downloads — submit a job, poll for the CDN URL.

- [Submit a download](https://vidkraken.com/docs/endpoint/submit-download.md): `POST /download`. Queue a YouTube video for download.
- [Get download status](https://vidkraken.com/docs/endpoint/get-download-status.md): `GET /download/{jobId}`. Poll the status of a previously-submitted download job.

### [Channel](https://vidkraken.com/docs/endpoint/tag/channel.md)

List recent videos from a YouTube channel.

- [List channel videos](https://vidkraken.com/docs/endpoint/list-channel-videos.md): `POST /list-channel`. List recent videos for a YouTube channel.

### [Account](https://vidkraken.com/docs/endpoint/tag/account.md)

Your account details, current plan, and remaining balances.

- [Get account info & balances](https://vidkraken.com/docs/endpoint/get-account.md): `GET /me`. Returns the account behind the API key — name, email, current plan, and the live balance on every metered feature.

## Webhook: `download.finished`

Sent to the `webhookUrl` you passed on `POST /download` when that job
reaches a terminal state. Fires once per job — either `download.completed`
or `download.failed` — and the JSON body is exactly what
`GET /download/{jobId}` returns for the job at that moment.

**Acknowledge** with any `2xx` within 15 seconds. Anything else (a
non-2xx status, a redirect, a timeout, a connection error) is retried
with exponential backoff: 30s, 1m, 2m, 4m, 8m, 16m, 32m, 64m, 128m —
10 attempts over roughly 4 hours. Reply `410 Gone` to stop retries
early. Delivery is at-least-once; treat `jobId` as your idempotency key.

**Verify the signature.** Every request carries
`X-VidKraken-Signature: t=<unix seconds>,v1=<hex>` where `v1` is
`HMAC-SHA256(key = your API key, message = "<t>.<raw request body>")`.
Compute it over the raw bytes (before JSON parsing), compare in
constant time, and reject timestamps older than a few minutes to
defeat replays.

```js
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, signatureHeader, apiKey) {
  const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
  const expected = createHmac("sha256", apiKey).update(`${parts.t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
```

### Parameters

- `X-VidKraken-Event` (header, string, required)

  Which terminal state the job reached. Mirrors `status` in the body.

  One of: `download.completed`, `download.failed`.

- `X-VidKraken-Job-Id` (header, string, required)

  The `jobId` this delivery is about.

- `X-VidKraken-Attempt` (header, integer, required)

  Delivery attempt number, starting at 1.

- `X-VidKraken-Signature` (header, string, required)

  `t=<unix seconds>,v1=<hex HMAC-SHA256>` — see the description above.

  Example: `t=1756742400,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e7c4c1d1`.

### Request body

One of:

- `DownloadJobCompleted`
- `DownloadJobFailed`

#### Examples

download.completed:

```json
{
  "jobId": "69cfc45e8a3b4d2f9c1e0a7b",
  "status": "COMPLETED",
  "url": "https://youtu.be/dQw4w9WgXcQ",
  "format": "1080",
  "actualFormat": "1080",
  "language": null,
  "downloadUrl": "https://proxy.vidkraken.com/media/69cfc45e8a3b4d2f9c1e0a7b.mp4",
  "title": "Rick Astley - Never Gonna Give You Up",
  "duration": 213,
  "fileSize": 32240544
}
```

download.failed:

```json
{
  "jobId": "69cfc45e8a3b4d2f9c1e0a7b",
  "status": "FAILED",
  "errorCode": "AGE_RESTRICTED",
  "title": "Some Restricted Video",
  "duration": 412,
  "url": "https://youtu.be/example",
  "format": "1080",
  "language": null
}
```

### Responses

#### 410

Endpoint gone — we stop retrying without further attempts.

#### 2XX

Acknowledged — we stop retrying.

#### default

Treated as a failed attempt and retried with backoff.

## Webhook schemas

### `DownloadJobCompleted`

- `jobId` (string, required)

- `status` (string, required)

  One of: `COMPLETED`.

- `url` (string, required)

  The original YouTube URL submitted.

- `format` (`MediaFormat`, required)

  The format requested in the original POST.

- `actualFormat` (`MediaFormat`, required)

  Format actually delivered. May differ from the requested format if the video isn't available in that quality.

- `language` (string or null)

  Audio language requested in the original POST, or `null` if none was specified. The actual language used may differ if a fallback was applied.

- `downloadUrl` (string (uri), required)

  CDN URL to download the produced file from.

- `title` (string, required)

- `duration` (integer, required)

  Full video duration in seconds.

- `fileSize` (integer, required)

  Size of the produced file in bytes.

- `createdAt` (string (date-time))

  When the job was submitted (ISO 8601).

- `startedAt` (string or null (date-time))

  When processing started, or `null` if still queued.

- `completedAt` (string or null (date-time))

  When the job reached a terminal state, or `null`.

### `DownloadJobFailed`

- `jobId` (string, required)

- `status` (string, required)

  One of: `FAILED`.

- `errorCode` (`DownloadErrorCode`)

- `title` (string, required)

- `duration` (integer, required)

- `url` (string, required)

- `format` (`MediaFormat`, required)

- `language` (string or null)

  Audio language requested in the original POST, or `null` if none was specified.

- `createdAt` (string (date-time))

  When the job was submitted (ISO 8601).

- `startedAt` (string or null (date-time))

  When processing started, or `null` if still queued.

- `completedAt` (string or null (date-time))

  When the job reached a terminal state, or `null`.

### `MediaFormat`

Target media format. Numeric values are vertical resolutions in
pixels, delivered as MP4.

`audio` delivers the best audio stream YouTube serves, in whatever
container YouTube gives it — usually M4A (AAC) or WebM (Opus), at
roughly 130–160 kbps. Nothing is re-encoded, so it is the fastest
option and the highest quality available. Use this unless your
pipeline needs one specific container. Note that YouTube does not
serve higher audio bitrates for regular videos, so no option here
produces better audio than this one.

`audio-aac` prefers YouTube's AAC stream (M4A, ~128 kbps) over Opus —
the most widely compatible audio codec, and useful when a pipeline was
calibrated on AAC. Nothing is re-encoded. If YouTube offers no AAC
stream for a video, the best available audio is delivered instead and
`actualFormat` reports `audio` rather than `audio-aac`, so the
substitution is visible.

`audio-mp3` always delivers an MP3, encoded at 320 kbps CBR — the
highest rate the MP3 format defines — using LAME's best-quality
analysis. The source's sample rate and channel layout are preserved
rather than forced: 48 kHz audio stays 48 kHz with no resampling, and
a mono source stays mono.

Type: string.

One of: `1080`, `720`, `480`, `360`, `audio`, `audio-aac`, `audio-mp3`.

### `DownloadErrorCode`

Machine-readable failure reason. Except for `NETWORK_ERROR`, every
value below describes the video or our policy, not a fault on our
side — retrying will return the same result, so treat them as final.

Other values may appear for transient infra failures — retry after a
short backoff before treating them as fatal.

- `AGE_RESTRICTED` — sign-in required by YouTube for this video
- `MEMBERS_ONLY` — restricted to channel members
- `PAYMENT_REQUIRED` — purchase or rental required
- `PRIVATE_VIDEO` — the video is private
- `VIDEO_PREMIERE` — a scheduled premiere that has not aired yet; retry after the premiere starts
- `GEO_RESTRICTED` — not available in the server region
- `VIDEO_PROCESSING` — YouTube is still processing the video, usually a fresh upload or a live stream that ended recently; retry later
- `VIDEO_UNAVAILABLE` — unavailable for an unspecified reason
- `DRM_PROTECTED` — DRM protected
- `VIDEO_NOT_FOUND` — no such video, or it has been deleted
- `FORMAT_UNAVAILABLE` — the requested format/quality is not offered
- `LIVE_STREAM_IN_PROGRESS` — still live; retry once it has ended
- `COPYRIGHTED_MUSIC` — copyrighted music, not downloadable here
- `NO_DOWNLOAD_OPT_OUT` — the creator opted out via `#nodownload`
- `FILE_TOO_LARGE` — the file in this format is larger than we can deliver; try a lower-quality format or a shorter clip
- `NETWORK_ERROR` — transient network failure on our side; retry after a short backoff

Type: string.

One of: `AGE_RESTRICTED`, `MEMBERS_ONLY`, `PAYMENT_REQUIRED`, `PRIVATE_VIDEO`, `VIDEO_PREMIERE`, `GEO_RESTRICTED`, `VIDEO_PROCESSING`, `VIDEO_UNAVAILABLE`, `DRM_PROTECTED`, `VIDEO_NOT_FOUND`, `FORMAT_UNAVAILABLE`, `LIVE_STREAM_IN_PROGRESS`, `COPYRIGHTED_MUSIC`, `NO_DOWNLOAD_OPT_OUT`, `FILE_TOO_LARGE`, `NETWORK_ERROR`.

## SDKs and CLI

Official clients wrap these endpoints with typed errors, safe retries, polling and file saving, and include a `vidkraken` command-line tool:

- JavaScript / TypeScript: `npm install vidkraken` ([npm](https://www.npmjs.com/package/vidkraken))
- Python: `pip install vidkraken` ([PyPI](https://pypi.org/project/vidkraken/))
- CLI: `npx vidkraken download <url> -f 1080` or `vidkraken download <url>` after `pip install`
- Source: [github.com/vidkraken/vidkraken](https://github.com/vidkraken/vidkraken)

## More

- [Connect Claude and other agents (MCP)](https://vidkraken.com/docs/mcp.md)
- [OpenAPI spec (JSON)](https://vidkraken.com/openapi.json) and [YAML](https://vidkraken.com/openapi.yaml)
- [Docs index for agents](https://vidkraken.com/docs/llms.txt)
- [Every docs page in one file](https://vidkraken.com/llms-full.txt)
- [Pricing](https://vidkraken.com/pricing.md)
