GET /api/v2/anime/stream-file/
Stream Proxy
Streams a Media Sources URL back through AnimeDoc, with byte-range support, so players can seek.
Overview
Pass a url from Media Sources as video_url. The response is the video itself (video/mp4) with Accept-Ranges: bytes, so <video> elements and mobile players can seek. Forward the player's Range header if you proxy it. Needs an API key on a paid plan (Starter, Pro, Business or the 3-Day Pass). Each HTTP request counts, and players fetch in chunks, so one playback is usually several requests.
GET https://api.animedoc.cloud/api/v2/anime/stream-file/
X-API-Key: <your key>Parameters
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
| video_url | query | string | yes | — | A downloads[].url from Media Sources. |
| api_key | query | string | no | — | Your key, only where you can't set the X-API-Key header (avoid exposing it in a public page). |
| Range | header | string | no | — | Byte range, e.g. bytes=0-. Players send this automatically. |
Example request
curl -G "https://api.animedoc.cloud/api/v2/anime/stream-file/" \
--data-urlencode "video_url=$VIDEO_URL" \
-H "X-API-Key: $ANIMEDOC_API_KEY" \
-H "Range: bytes=0-1048575" -o first-megabyte.mp4// Server-side (Node 18+): pipe the video to your own client, forwarding Range.
const upstream = await fetch(
"https://api.animedoc.cloud/api/v2/anime/stream-file/?video_url=" + encodeURIComponent(videoUrl),
{ headers: { "X-API-Key": process.env.ANIMEDOC_API_KEY, Range: req.headers.range ?? "bytes=0-" } },
);
res.status(upstream.status);
for (const h of ["content-type", "content-length", "content-range", "accept-ranges"]) {
const v = upstream.headers.get(h);
if (v) res.setHeader(h, v);
}
Readable.fromWeb(upstream.body).pipe(res);import os
import requests
with requests.get(
"https://api.animedoc.cloud/api/v2/anime/stream-file/",
params={"video_url": video_url},
headers={"X-API-Key": os.environ["ANIMEDOC_API_KEY"], "Range": "bytes=0-"},
stream=True,
timeout=30,
) as res:
res.raise_for_status()
with open("episode.mp4", "wb") as f:
for chunk in res.iter_content(1024 * 1024):
f.write(chunk)Example response
HTTP/1.1 206 Partial Content
Content-Type: video/mp4
Accept-Ranges: bytes
Content-Range: bytes 0-1048575/145200000
Content-Length: 1048576
<binary video data>Error codes
| Status | Body | What it means |
|---|---|---|
| 400 | {"error": "Missing detailPath query parameter"} | A required parameter is missing or invalid. |
| 401 | {"detail": "An API key is required. Send it as the X-API-Key header. ..."} | No X-API-Key header, or the key doesn't exist (Invalid API key). Every catalog endpoint needs a key; get a free one at /docs/pricing. |
| 403 | {"detail": "Playback through the API needs a paid plan ..."} | Your plan doesn't include this endpoint. The stream proxy needs a paid plan. |
| 404 | {"error": "No playable source is available ..."} | The title or episode doesn't exist, or has no playable source right now. |
| 429 | {"error": "rate_limit_exceeded"} or {"error": "daily_quota_exceeded"} | Over your plan's requests per minute or per day. Wait Retry-After seconds, or upgrade. |
| 502 | {"error": "...", "details": "..."} | The content provider didn't answer in time. Safe to retry with backoff. |
When you go over a limit you get 429 Too Many Requests with a Retry-After header (seconds):
HTTP/1.1 429 Too Many Requests
Retry-After: 34
Content-Type: application/json
{
"error": "rate_limit_exceeded",
"detail": "Your free plan allows 10 requests per minute. Slow down and retry in 34s.",
"limit": 10,
"window": "minute",
"retry_after_seconds": 34,
"upgrade_url": "https://animedoc.cloud/pricing"
}Rate limits per plan
| Plan | Requests / day | Requests / minute | Stream proxy | Price |
|---|---|---|---|---|
| Free | 20 | 10 | No | $0 |
| Starter | 3,000 | 60 | Yes | $12/month or $60/6 months |
| Pro | 20,000 | 300 | Yes | $22/month or $110/6 months |
| Business | 500,000+ | Custom | Yes | From $100/month |
| 3-Day Pass | 2,000 | 60 | Yes | $1 for 3 days |
Every call made with your API key counts as one request. Daily quotas reset at 00:00 UTC. See rate limits for headers and retry advice, or pricing to raise your limits.