# X Video Downloader — Full API Reference Base URL: https://xvideodownloader.org ## Authentication Two methods: 1. **Anonymous**: No auth header. Limited to 2 downloads/day, 720p, 25MB. 2. **Bearer token**: `Authorization: Bearer `. Obtain via CLI device auth flow. Plans: - Free (registered): same limits as anonymous — 2 downloads/day, 720p, 25MB. - SVIP (one-time lifetime purchase): no daily limit, no quality/size cap, custom filenames and format. ## Endpoints ### POST /api/download Create a download task. Request body (JSON): - `url` (string, required): X/Twitter video URL (x.com or twitter.com) - `filename` (string, optional): Custom output filename (SVIP only) - `format` (string, optional): Custom ffmpeg format (SVIP only) - `playlistIndex` (number, optional): Video index for multi-video tweets (0-based) Response 200: ```json { "ok": true, "cached": false, "message": "download queued", "download": { "id": "uuid" }, "video": { "id": "uuid", "status": "queued", "requestId": "uuid" }, "remaining": 4 } ``` Response 429 (rate limited): ```json { "ok": false, "error": "RATE_LIMIT", "plan": "free", "limit": 2, "remaining": 0 } ``` Response 400: ```json { "ok": false, "error": "EMPTY_URL" | "INVALID_URL_FORMAT" | "UNSUPPORTED_HOST" } ``` ### GET /api/downloads/{id} Get download task details. Response 200: ```json { "ok": true, "task": { "id": "uuid", "url": "https://x.com/...", "filename": "video.mp4", "status": "queued" | "running" | "done" | "failed", "created_at": "2026-01-01T00:00:00Z", "finished_at": "2026-01-01T00:01:00Z", "error": null, "error_code": null, "file_size": 12345678, "duration": 120.5, "width": 1920, "height": 1080, "stage": "downloading" | "uploading" | null, "playlist_index": 0, "playlist_total": 3 } } ``` Status flow: `queued` → `running` → `done` | `failed` ### GET /api/file/{id} Get the video file. Returns 302 redirect to the file URL. Pass `?dl=1` to trigger the explicit-download path (counts against the daily quota); without it, the redirect is treated as a read-only preview and does not consume quota. Response 302: Redirects to the file's public R2 URL. Response 409: `{"ok": false, "message": "Video not ready for download."}` — task hasn't finished processing yet. Response 429: `{"ok": false, "code": "RATE_LIMIT", "message": "..."}` — daily download limit reached (only on `?dl=1`). Response 404: `{"ok": false, "message": "Download not found."}` or no file URL available. ### GET /api/downloads List user's downloads (requires auth). Query params: - `limit` (number, optional, default 50, max 200) Response 200: ```json { "ok": true, "items": [ { "id": "uuid", "url": "https://x.com/...", "status": "done", "created_at": "2026-01-01T00:00:00Z" } ] } ``` ### DELETE /api/downloads/{id} Delete a download record. Cannot delete running tasks. Response 200: `{"ok": true, "message": "deleted"}` Response 409: `{"ok": false, "error": "cannot delete running task"}` ### GET /api/user/usage Get today's usage (requires auth). Response 200: ```json { "ok": true, "used": 1, "limit": 2, "remaining": 1, "plan": "free" } ``` For SVIP users, `limit` and `remaining` are -1 (unlimited). ### GET /api/user/subscription Get subscription info. Response 200: ```json { "ok": true, "plan": "free" | "svip", "subscription": { "status": "active", "cancelAtPeriodEnd": false, "currentPeriodEnd": null, "isManaged": false } | null } ``` `subscription` is `null` for free-plan users. `isManaged` is `false` for one-time lifetime SVIP purchases (nothing to manage — no recurring billing) and `true` only for legacy recurring subscriptions. ## Typical AI Agent Workflow 1. `POST /api/download` with the video URL 2. Poll `GET /api/downloads/{id}` every 5 seconds until `status === "done"` 3. `GET /api/file/{id}?dl=1` to get the download URL (302 redirect) 4. Download the file from the redirect URL ## Error Codes - `EMPTY_URL`: No URL provided - `INVALID_URL_FORMAT`: URL doesn't start with http(s) - `UNSUPPORTED_HOST`: Only x.com and twitter.com are supported - `RATE_LIMIT`: Daily download limit reached - `FILE_SIZE_LIMIT`: Video exceeds the plan's file size limit - `VIDEO_NOT_FOUND`, `PRIVATE_TWEET`, `GEO_BLOCKED`, `NETWORK_ERROR`: video-specific fetch failures (not upgrade-related) Full reference: /developers/api ## MCP Server Install and configure the MCP server for AI agent integration: ```json { "mcpServers": { "x-downloader": { "command": "npx", "args": ["-p", "@roudanio/x-downloader-cli", "x-downloader-mcp"] } } } ``` The MCP server provides these tools: - `download_video`: Submit a video URL for download - `check_status`: Check download task status - `get_download_url`: Get the file download URL - `list_downloads`: List recent downloads - `check_usage`: Check daily usage and limits First run `npx -p @roudanio/x-downloader-cli x-downloader auth login` to authorize the MCP server. Full reference: /developers/mcp ## CLI Direct downloads run your local yt-dlp and never call this API — no auth needed: ``` npm install -g @roudanio/x-downloader-cli x-downloader https://x.com/user/status/123 ``` Full reference: /developers/cli