YTDL.my public HTTP API
Base URL: your deployment’s public origin (same as NEXT_PUBLIC_APP_URL; e.g. https://ytdl.my).
Version: see the running app’s OpenAPI at /docs if enabled, or public/openapi.yaml in the repo.
This document describes this gateway (account, billing, quotas, download proxy). It does not document private worker hostnames or keys.
Authentication (user API)
| Method | Header |
|---|---|
| API key | X-API-Key: <key> |
| Bearer | Authorization: Bearer <key> |
Issue keys from the authenticated dashboard. Endpoints that require a key return 401 when missing or invalid.
GET /api/healthz, GET /healthz, and GET /api/v1/d (signed downloads) do not use API keys.
Adding cookie sets to the pool (operators)
Downloads may use a shared server-side pool of Netscape-format cookie exports. Operators append rows via HTTP; this is not the same auth as dashboard API keys (X-API-Key). Ingest is gated by a separate secret configured on the server.
POST /api/internal/youtube-cookies
| Item | Value |
|---|---|
| URL | {BASE_URL}/api/internal/youtube-cookies |
| Header | Authorization: Bearer <YT_COOKIE_INGEST_SECRET> |
| Header | Content-Type: application/json |
| Body | { "cookies": "<full Netscape cookie file as one string>" } |
The secret YT_COOKIE_INGEST_SECRET must be set in the gateway environment (not exposed to clients). If it is unset, this endpoint returns 503.
201 — { "ok": true, "id": "<row id>" }
400 — body invalid or empty / too large
401 — Bearer missing or wrong
503 — ingest disabled (secret not configured)
Example: send a Netscape file with curl and jq
Use jq to read the file and build valid JSON (handles escaping and newlines). Replace the base URL and secret with your values:
export BASE_URL="http://localhost:3000"
export YT_COOKIE_INGEST_SECRET="your-ingest-secret-from-server-env"
jq -Rs '{cookies: .}' path/to/www.youtube.com_cookies.txt | \
curl -sS -X POST "${BASE_URL}/api/internal/youtube-cookies" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${YT_COOKIE_INGEST_SECRET}" \
-d @-
-Rsonjqreads raw input as a single string and wraps it in{ "cookies": "..." }.-d @-sends that JSON on stdin tocurl.
One line (same idea):
jq -Rs '{cookies: .}' www.youtube.com_cookies.txt | curl -sS -X POST 'http://localhost:3000/api/internal/youtube-cookies' -H 'Content-Type: application/json' -H "Authorization: Bearer $YT_COOKIE_INGEST_SECRET" -d @-
You can also paste the file contents into a JSON body manually, but you must escape quotes and newlines; using jq avoids that.
Endpoints (summary)
| Method | Path | Auth | Role |
|---|---|---|---|
| GET | /api/healthz | no | Health |
| POST | /api/internal/youtube-cookies | Bearer ingest secret | Append one Netscape cookie export to the pool |
| POST | /api/v1/jobs | yes | Create a download job |
| GET | /api/v1/jobs/{job_id} | yes | Poll job status (mirrors upstream) |
| GET | /api/v1/d?... | no (signed) | Stream finished file |
POST /api/v1/jobs may block for a long time while the upstream worker runs (Route Handler timeout is capped; see deployment limits). Responses mirror the private worker (job_id, status, signed download_url rewritten to /api/v1/d on this host).
Signed downloads
Signed URLs use query parameters job_id, e (expiry unix time), s (hex HMAC). The gateway’s /api/v1/d handler proxies bytes from the worker without exposing the worker’s URL.