videofetch (Python SDK)
Official VideoFetch SDK — video ingestion API: give us a video URL, we deliver MP4/MP3 to your storage.
pip install videofetch-sdk
Usage
import videofetch
client = videofetch.VideoFetch(api_key="vf_live_sk_...")
# L1: create a job (returns immediately, status "queued")
job = client.downloads.create(
url="https://www.youtube.com/watch?v=...",
format="1080p", # 144p..2160p | mp3
trim=videofetch.TrimSpec(start=120, end=420), # optional clip window
# destination={"type": "r2", "id": "..."}, # optional direct-to-bucket
)
# L2: wait for the terminal state (polls with backoff)
result = job.wait(timeout=120)
print(result.status) # "completed"
print(result.download_url) # presigned link, valid 7 days (url destination)
print(result.storage_key) # "user://<bucket>/<key>" (bucket destination)
print(result.size_bytes, result.cost_usd)
# L3: one-shot
result = client.downloads.create_and_wait(url=..., format="1080p")
Async (FastAPI / Next.js backends):
from videofetch.asyncio import AsyncVideoFetch
async with AsyncVideoFetch(api_key="...") as client:
job = await client.downloads.create(url=..., format="1080p")
result = await job.wait(timeout=120)
Free metadata lookup:
info = client.info.lookup("https://www.youtube.com/watch?v=...")
print(info.title, info.duration, info.formats) # available quality tiers
Webhooks (FastAPI):
from fastapi import Request, HTTPException
from videofetch.webhooks import construct_event, SignatureVerificationError
@app.post("/webhooks/videofetch")
async def on_event(request: Request):
body = await request.body()
try:
event = construct_event(body, request.headers.get("X-VideoFetch-Signature"), WEBHOOK_SECRET)
except SignatureVerificationError:
raise HTTPException(status_code=400, detail="bad signature")
if event["event"] == "download.completed":
... # fetch the result via client.downloads.retrieve(event["id"])
return {"ok": True}
Configuration
api_key— required. Create keys in the Dashboard (vf_live_sk_...). Falls back to theVIDEOFETCH_API_KEYenv var.base_url— defaults to the hosted API; override for local/single-server dev (http://localhost:8301). Env:VIDEOFETCH_BASE_URL.timeout(s),max_retries(default 2, automatic on 429/5xx).
Errors
All SDK errors derive from videofetch.VideoFetchError:
| error | meaning |
|---|---|
AuthenticationError |
bad/expired key (401) |
QuotaExceededError |
monthly quota exhausted (402) |
ValidationError |
bad url/format/trim/destination (400/422) |
NotFoundError |
job/connection not found (404) |
RateLimitError |
slow down (429) |
JobFailedError |
job reached failed — never charged |
Serverless warning
Do not call job.wait() in a Vercel/Cloudflare/Lambda function — it burns
billed execution time. Create the job, persist its id, then respond to the
download.completed webhook and retrieve(id) the final result.
Development
pip install -e ".[dev]"
pytest
License
MIT
Usage, alerts and credits
u = client.usage.get()
u.plan # "free" | "developer" | "growth" | "scale"
u.quota_gb # account plan quota for this month (GB)
u.used_pct # % of the monthly quota consumed
u.remaining_gb # remaining GB before overage
u.active_jobs # jobs currently queued + processing on your account
u.concurrency_limit # max in-flight jobs per account
u.alert_level # ok | warning | critical | exceeded
u.payg_balance_cents # pay-as-you-go credit balance
a = client.usage.alerts()
a.state.level # ok | warning | critical | exceeded
a.state.crossed # e.g. [80, 95] — thresholds already crossed
a.state.next_threshold_pct
a.state.action # "topup" | "upgrade" | None → drive your own UI
a.fired # alerts already delivered this month (kind/level/pct/created_at)
a.thresholds # [80, 95, 100]
a.topup_amounts # [10, 25, 50, 100]
Alerts are evaluated after every successful download and delivered to your webhook
endpoints as quota.warning (80%/95%) and quota.exceeded (100%); a low credit
balance emits balance.low. Each threshold fires at most once per calendar month,
so your handler will not be spammed.
Managing webhook endpoints with your API key
wh = client.webhooks.create(
"https://api.yourapp.com/videofetch/hook",
events=["download.completed", "quota.exceeded", "balance.low"],
)
wh.secret # whsec_... — FULL value, shown only on create. Store it now.
client.webhooks.list() # secrets are masked here
client.webhooks.test(wh.id) # send a webhook.test ping, check delivery
client.webhooks.delete(wh.id)
Available events: download.queued, download.processing, download.completed,
download.failed, quota.warning, quota.exceeded, balance.low.
Verification mirrors the server exactly — HMAC-SHA256 over the raw request body:
from videofetch import construct_event # or verify_webhook_signature(raw, header, secret)
event = construct_event(request.body, request.headers["X-VideoFetch-Signature"], wh.secret)
Concurrency limits and 429s
Your account may run a limited number of jobs at once (queued + processing). When the
cap is reached the API answers 429 with a typed RateLimitError instead of silently
queueing forever:
from videofetch import RateLimitError
try:
client.downloads.create(url=url, format="1080p")
except RateLimitError as e:
e.code # concurrency_limit_exceeded | queue_limit_exceeded | platform_at_capacity
e.limit, e.active, e.scope
e.retry_after # seconds, from the Retry-After header
GET /v1/usage returns active_jobs / concurrency_limit so you can schedule work
before hitting the cap. A suspended account returns 403 with
code=account_suspended (surface it to your own operators rather than retrying).
Release files for videofetch-sdk 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| videofetch_sdk-0.2.0.tar.gz | 21.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| videofetch_sdk-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.8 kB
Release files / videofetch_sdk-0.2.0.tar.gz
| Download URL | videofetch_sdk-0.2.0.tar.gz |
|---|---|
| Size | 21.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
53637d25e3f478fb9d5020407d016e2edecc5a77a957a26b16f1ea76048e9210
|
|
BLAKE2b-256 checksum How to use checksums |
e0a7254686949080d53bda4958690d21a562ac87c17cd34709b85205c77d34fe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 21, 2026.
Transparency logRelease files / videofetch_sdk-0.2.0-py3-none-any.whl
| Download URL | videofetch_sdk-0.2.0-py3-none-any.whl |
|---|---|
| Size | 17.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2f7685993faa4020b7f70e154d38d6d0d6b10abe1911ea6ae82f4ce66f2ec526
|
|
BLAKE2b-256 checksum How to use checksums |
51d444b46c1362af9df47c4b5962ca8c5a73c67886398b29ce88724a915b1b1b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Sep 21, 2026.
Transparency log