Vid Kraken Python SDK and CLI
The official Python client for Vid Kraken, a REST API that downloads YouTube videos and audio on its servers and returns a CDN link to the finished file. You pay for bandwidth only, and failed downloads are never charged.
One package gives you:
VidKrakenandAsyncVidKraken: typed sync and async clients built on httpx- a
vidkrakencommand-line tool for scripts, shells and AI agents
Use only with content you have the right to download.
Install
pip install vidkraken
Requires Python 3.9 or newer. The only runtime dependency is httpx.
Get an API key
Sign in at https://vidkraken.com/login and copy your key from the dashboard. Then either export it:
export VIDKRAKEN_API_KEY="vk_..."
or pass it to the client with VidKraken(api_key="vk_...").
Quickstart (sync)
from vidkraken import VidKraken
with VidKraken() as vk: # reads VIDKRAKEN_API_KEY
done = vk.download_and_wait(
"https://youtu.be/dQw4w9WgXcQ",
format="720", # "1080", "720", "480", "360", "audio", "audio-aac", "audio-mp3"
on_progress=lambda s: print(s.status, s.progress and s.progress.percent),
)
print(done.download_url, done.actual_format, done.file_size)
path = vk.save_file(done, "downloads/") # named after the video title
print("saved to", path)
Step by step, if you'd rather poll yourself or use a webhook:
job = vk.download("https://youtu.be/dQw4w9WgXcQ", format="audio-mp3", start_time=30, end_time=90)
print(job.job_id, job.title, job.rate_limit.remaining)
status = vk.get_download(job.job_id) # IN_QUEUE / IN_PROGRESS / COMPLETED / FAILED
status = vk.wait_for_download(job.job_id, poll_interval=3.0, timeout=600)
Pass webhook_url="https://example.com/hooks/vidkraken" to download() and Vid
Kraken POSTs the final status to that URL. The body is the same as get_download()
returns, so DownloadStatus.from_dict(payload) parses it.
Quickstart (async)
AsyncVidKraken has the same methods. Each one is a coroutine.
import asyncio
from vidkraken import AsyncVidKraken
async def main() -> None:
async with AsyncVidKraken() as vk:
urls = ["https://youtu.be/dQw4w9WgXcQ", "https://youtu.be/9bZkp7q19f0"]
results = await asyncio.gather(*(vk.download_and_wait(u, format="audio") for u in urls))
for done in results:
await vk.save_file(done, "downloads/")
asyncio.run(main())
Video info
info = vk.info_and_wait("https://youtu.be/dQw4w9WgXcQ", include_audio_tracks=True)
print(info.title, info.duration)
for track in info.audio or []:
print(track.language, track.type) # e.g. "en original", "es dubbed"
info(), get_info() and wait_for_info() are the separate steps behind info_and_wait().
Account and channel listing
me = vk.me()
print(me.plan.id, me.balances["bandwidth_mb"].remaining)
listing = vk.list_channel("https://www.youtube.com/@RickAstleyYT", max_videos=25) # paid add-on
for entry in listing.entries:
print(entry["id"], entry["title"])
Saving files
save_file(source, path=None) streams a finished download to disk in chunks.
sourceis a completedDownloadStatusor a rawdownloadUrlstring.pathis a file path, a directory, orNonefor the current directory. When you give a directory, the file is named<title>.<ext>:.mp4for video,.mp3foraudio-mp3,.m4aforaudio-aac, and the CDN's own extension foraudio.- The file is written to
<name>.partand renamed once it is complete. - Your API key is never sent to the file host.
Responses
Responses are frozen dataclasses with snake_case attributes, for example
status.download_url and status.progress.eta_seconds. The original JSON is kept
on .raw, and obj["camelCaseKey"] / obj.get(...) read from it, so fields
added to the API later are still reachable.
Errors
Every error inherits from vidkraken.VidKrakenError, which has .status, .code,
.message, .details and .retry_after. Branch on .code, not on the message.
| Exception | When | code |
|---|---|---|
BadRequestError |
400 | INVALID_REQUEST |
AuthenticationError |
401 | UNAUTHORIZED |
PermissionDeniedError |
403 | LIMIT_REACHED |
NotFoundError |
404 | NOT_FOUND, VIDEO_NOT_FOUND |
RateLimitError |
429 | TOO_MANY_PENDING, RATE_LIMITED |
ServerError / ServiceUnavailableError |
5xx / 503 | INTERNAL_ERROR / UNAVAILABLE |
APIError |
any other HTTP error (base class of the above) | as sent |
APIConnectionError / APITimeoutError |
no HTTP response | CONNECTION_ERROR / TIMEOUT |
JobFailedError |
the job ended in FAILED / failed |
.error_code, e.g. AGE_RESTRICTED |
WaitTimeoutError |
wait_for_*(timeout=...) ran out of time |
WAIT_TIMEOUT |
MissingAPIKeyError |
no key given and VIDKRAKEN_API_KEY is unset |
MISSING_API_KEY |
If the API sends a code this SDK doesn't know, the exception class is picked from the HTTP status.
from vidkraken import JobFailedError, RateLimitError, VidKrakenError
try:
done = vk.download_and_wait(url, format="1080")
except JobFailedError as e:
print(e.job_id, e.error_code, e.message)
if e.retryable: # only NETWORK_ERROR is worth resubmitting
...
except RateLimitError as e:
print("retry in", e.retry_after, "seconds")
except VidKrakenError as e:
print(e.status, e.code, e.message)
Retries
The API has no idempotency key. Retrying a POST /download after a timeout
or a 5xx could therefore start, and bill, a second download. The client only
retries when that can't happen:
| Request | Retried on |
|---|---|
Any GET (status polls, /me, file download) |
network errors, timeouts, 429, 500, 502, 503, 504 |
POST /download, /info, /list-channel |
429 and 503 UNAVAILABLE only, because in both cases nothing was queued |
It never retries a POST on 500, 502, 504, a timeout or a network error. In those cases you get the error and can check before resubmitting.
- Retries use exponential backoff with jitter (0.5s, 1s, 2s and so on, capped at 8s).
- A
Retry-Afterheader is honored if it is 60s or less. If it is longer, the error is raised so you can decide what to do. max_retriesdefaults to 2. Set it to 0 to turn retries off:
vk = VidKraken(max_retries=5, timeout=30.0)
vk = VidKraken(max_retries=0)
Rate limits
Each account can have at most 100 downloads queued or in progress at once.
Every download() result carries the headroom parsed from the IETF RateLimit
headers:
job = vk.download(url)
job.rate_limit.limit # 100: how many may be pending at once
job.rate_limit.remaining # how many more you can queue right now
Past the cap, download() raises RateLimitError with code TOO_MANY_PENDING
and .retry_after set. The error also has .rate_limit.
Bring your own httpx client
import httpx
vk = VidKraken(http_client=httpx.Client(proxy="http://proxy:8080")) # proxy= needs httpx>=0.26
The client must be an httpx.Client for VidKraken or an httpx.AsyncClient
for AsyncVidKraken. A client you pass in is not closed by close().
CLI
vidkraken download https://youtu.be/dQw4w9WgXcQ -f 1080 # waits, saves, prints the path
vidkraken download https://youtu.be/dQw4w9WgXcQ -f audio-mp3 -o music/
vidkraken download https://youtu.be/dQw4w9WgXcQ --start 30 --end 90 -o clip.mp4 -f 720
vidkraken download https://youtu.be/dQw4w9WgXcQ --no-wait # prints the job id
vidkraken download https://youtu.be/dQw4w9WgXcQ --webhook https://example.com/hook --no-wait
vidkraken status <job_id>
vidkraken info https://youtu.be/dQw4w9WgXcQ --audio-tracks
vidkraken me
vidkraken --version
python -m vidkraken --help
| Option | Applies to | Effect |
|---|---|---|
-f/--format |
download |
one of 1080, 720, 480, 360, audio (default), audio-aac, audio-mp3 |
--start S / --end S |
download |
trim the clip, in seconds |
--language LANG |
download |
preferred audio track |
-o/--output PATH |
download |
output file or directory |
--no-wait |
download |
print the job id and exit |
--webhook URL |
download |
URL to POST the final status to |
--timeout S, --poll-interval S |
download |
how long to wait and how often to poll |
-q/--quiet |
download |
no progress output |
--json |
every command | print one JSON document on stdout |
--api-key KEY |
every command | overrides VIDKRAKEN_API_KEY |
The CLI is designed to be scripted and driven by agents:
- stdout carries only the result: a path, a job id, a plain report, or one
JSON document with
--json. On failure with--json, stdout gets{"error", "code", ...}. - stderr carries progress and errors such as
vidkraken: error: TOO_MANY_PENDING: .... - It never prompts. Color appears only when stderr is a TTY and
NO_COLORis unset. - Exit codes:
0: success1: API or job error.statusalso exits 1 for aFAILEDjob.2: usage error, including a missing API key130: interrupted
Links
- Documentation: https://vidkraken.com/docs
- LLM-friendly reference: https://vidkraken.com/llms.txt
- OpenAPI spec: https://vidkraken.com/openapi.yaml
- Support: hello@vidkraken.com
License
MIT © Vid Kraken
Metadata
Release files for vidkraken 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| vidkraken-0.1.1.tar.gz | 33.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vidkraken-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 66.6 kB
Release files / vidkraken-0.1.1.tar.gz
| Download URL | vidkraken-0.1.1.tar.gz |
|---|---|
| Size | 33.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c4ef00723c97082276a5277401e4197f2be1d50ede545cdde20b944c18d14669
|
|
BLAKE2b-256 checksum How to use checksums |
8950c7c0998153cb780f813a61cd42068745cd417890182eb3d2053f0c973e82
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / vidkraken-0.1.1-py3-none-any.whl
| Download URL | vidkraken-0.1.1-py3-none-any.whl |
|---|---|
| Size | 33.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c5d86388b827bd06b8e6315909e960c4f6031f94d46c0048e7ccc7edf8cd1c24
|
|
BLAKE2b-256 checksum How to use checksums |
f75da8bbc6230242602cd9559e988776b230c2457f3bd08c6f868fda3cbdc9ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for vidkraken 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for vidkraken-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.