Skip to content

About

API to fetch and download video, audio and photos from 18 social platforms (YouTube, TikTok, Instagram, X and more). Stream or prepare modes, permanent media store, SQLite/PostgreSQL/MySQL/MongoDB.

Resources

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

BlazFetch Backend

One API to fetch and download media from 18 social platforms.
Resolve a link, choose a format, and download. Media is streamed or prepared temporarily; metadata is stored.

Node TypeScript Express Databases Platforms Open Source

Frontend repository · API reference · VPS deployment · OpenAPI


Tip

Want the web app? The ready-made frontend for this API is ssanaullahrais/blazfetch-web: start this backend, then run the frontend on top of it.

It takes a link from YouTube, TikTok, Instagram, X/Twitter, Facebook, Reddit, Vimeo, Dailymotion, Bluesky, Streamable, Rutube, SoundCloud, Snapchat, Twitch, Pinterest, Loom, Newgrounds or Tumblr and lets you download the video, audio or photos in it. It doesn't keep a copy of anyone's media: it fetches the file, checks that it actually works, sends it straight through to whoever asked, and deletes any temporary files afterward. (It does remember each link's details, such as title and formats, so repeat requests are instant. See Stored media.)

Demo

🚀 Live Demo

Open BlazFetch

social.blaztools.com

BlazFetch demo: paste a link, pick a quality, download

Click the animation to open the full video (demo.mp4).

Highlights

  • 18 platform adapters: metadata, formats and downloads. See sample status for known limitations.
  • Three ways to download with a single request, and streaming always comes first (preparing a file puts load on the server): stream and auto pipe the file straight through (merges and HLS included, preferring an H.264 file so it plays on phones) and fall back to preparing it on the server only if streaming fails before the first byte; prepare builds a compatible H.264/AAC file first.
  • Audio as MP3 on request: AUDIO_FORCE_MP3=true delivers every audio download as MP3 (converted live, or through a temporary file that is deleted after sending).
  • Everything fetched is remembered forever (metadata only), with stable page paths like /youtube/Cwkej79U3ek, a weekly check that notices deleted videos, and usage statistics.
  • Best-effort recovery: built-in platform fallbacks and an optional independent provider.
  • Your choice of database: SQLite (default, nothing to install), PostgreSQL, MySQL/MariaDB or MongoDB.
  • Optional bot protection: Cloudflare Turnstile can be switched on from .env (off by default).
  • Temporary media only: prepared files are removed after delivery, cancellation, failure or expiry; cancellation stops download processes.

Quick start

Needs Node.js 22+, current yt-dlp with EJS support, and ffmpeg (see Install).

npm install
npm run setup        # pick a database (press Enter for SQLite); creates .env and the tables
npm run dev          # http://localhost:4000
# 1. look up a link (metadata, thumbnail, formats)
curl -X POST http://localhost:4000/api/v1/fetch   -H "content-type: application/json"   -d '{"url":"https://www.tiktok.com/@scout2015/video/6718335390845095173"}'

# 2. download it in one request (best quality; falls back automatically if it can't be streamed)
curl -G http://localhost:4000/api/v1/stream   --data-urlencode "url=https://www.tiktok.com/@scout2015/video/6718335390845095173"   -d mode=auto -o video.mp4

Real responses for every platform are in docs/API.md.

Platform status

Sample results are network-dependent, not a guarantee for every URL. See latest sample checks and response examples.

Platform Status Notes
YouTube ✅ Sample passed Video/audio transfers and playlist listing; fallback availability varies
TikTok ✅ Confirmed yt-dlp primary, @tobyg74/tiktok-api-dl fallback
Instagram ✅ Confirmed Posts and reels (profile listing is not supported)
X / Twitter ✅ Confirmed Both x.com and legacy twitter.com. Posts whose media X hides from yt-dlp ("No video could be found in this tweet", often sensitive posts) are served by a fallback provider (FixTweet), like TikTok's
Facebook ✅ Confirmed Reels and public videos. Posts that need a login return an error
Reddit ✅ Confirmed Including separate video+audio DASH streams
Vimeo ✅ Confirmed Auto-retries via embed URL when watch page requires login. DRM-protected videos return a clear error
Dailymotion ✅ Confirmed Some clips have no audio at the source — not a bug
Bluesky ✅ Confirmed
Streamable ✅ Confirmed
Rutube ✅ Confirmed
SoundCloud ✅ Confirmed (single track) Profile/browse pages rejected with a clear error. DRM-protected (Go+) tracks can't be downloaded
Snapchat ✅ Confirmed
Twitch ✅ Samples passed Clip and complete 50-minute VOD at 160p; other qualities remain source-dependent
Pinterest ✅ Confirmed (pins + boards) Public API, no auth needed — see below
Loom ✅ Confirmed Video and audio, including HLS streams
Newgrounds ⚠️ Network blocked Latest test network returned HTTP 403
Tumblr ✅ Sample passed Public video, including generic-extractor recovery

Install

1. System tools (install first; Node.js 22+ is required for the YouTube solver):

pip install -U "yt-dlp[default]"
# ffmpeg: sudo apt-get install ffmpeg (Linux) or https://ffmpeg.org/download.html

Verify each:

node --version
yt-dlp --version
ffmpeg -version && ffprobe -version

2. Project setup (one command picks and configures your database):

npm install
npm run setup         # choose SQLite / PostgreSQL / MySQL / MongoDB; writes .env and creates the tables
npm run diagnostics   # checks the database, yt-dlp and ffmpeg are all reachable
npm run dev           # starts on http://localhost:4000

Choosing a database

npm run setup asks which database to use. SQLite is the default and needs nothing installed: just press Enter and you're running. Pick another if you already have a server:

Database Status You need Connection URL example
SQLite (default) ✅ Confirmed nothing none (the file is created automatically)
PostgreSQL 14+ ✅ Confirmed an empty database created first postgres://user:password@localhost:5432/blazfetch
MySQL 8+ / MariaDB ✅ Confirmed an empty database created first mysql://user:password@localhost:3306/blazfetch
MongoDB 6+ ✅ Confirmed a running server mongodb://localhost:27017/blazfetch

Confirmed = tested end to end from a fresh clone: setup, server start, fetch, download, and rows written to the jobs, cache and stats storage.

Setup creates the tables, not the database itself. For PostgreSQL run createdb -E UTF8 blazfetch first, for MySQL CREATE DATABASE blazfetch CHARACTER SET utf8mb4;. All four store the same data (metadata cache, jobs, stats, never the downloaded media) and the app behaves identically on each.

npm run setup starts from .env.example, so a fresh install gets the recommended settings, including AUDIO_FORCE_MP3=true (every audio download is delivered as MP3; see Optional: everything as MP3).

Prefer to configure by hand? Copy .env.example to .env, set DATABASE_DRIVER (sqlite, postgres, mysql or mongodb) and DATABASE_URL, then run npm run migrate. Setup can also run non-interactively: npm run setup -- --driver=postgres --url=postgres://user:pass@host:5432/blazfetch.

npm run typecheck and npm test verify the project builds and passes its test suite.

Usage

See docs/API.md for every endpoint with full request/response examples (including a real response for each platform) and a frontend walkthrough. The endpoints:

POST   /api/v1/fetch                     resolve metadata + formats (rangeStart/rangeEnd for Pinterest boards)
POST   /api/v1/fetch/audio               resolve audio options
GET    /api/v1/media/<platform>/<id>     stored media by stable path (e.g. /youtube/Cwkej79U3ek)
GET    /api/v1/media/<platform>/<id>/logs  that media's download logs (public, persisted, YouTube only for now)
GET    /api/v1/stream                    one-request download: ?mode=stream (default) | prepare | auto
POST   /api/v1/download                  start a download job (formatId defaults to "best")
GET    /api/v1/jobs/:id                  poll job status/progress
GET    /api/v1/downloads/:id             stream the file once the job is ready
DELETE /api/v1/downloads/:id             cancel + clean up temp files
DELETE /api/v1/jobs/:id                  cancel a job
POST   /api/v1/playlist/download         prepare a bounded playlist batch
GET    /api/v1/playlist/downloads/:id     poll batch and per-item results
DELETE /api/v1/playlist/downloads/:id     cancel unfinished playlist work
GET    /api/v1/platforms                 list supported platforms + domains
GET    /api/v1/stats                     public all-time totals: successful fetches and downloads (total and per platform)
GET    /api/v1/config                    public settings (is Turnstile on, and its site key)
POST   /api/v1/turnstile/verify          swap a solved Turnstile token for a pass cookie (only when Turnstile is on)
GET    /health, /health/ready            liveness / readiness (DB, yt-dlp, ffmpeg)

Which download method?

You want Use
It to work for every source in one request, streaming when it can (recommended, default) GET /stream?mode=auto
Fastest start, nothing on the server's disk (may not play on some phones for VP9/AV1/HEVC sources) GET /stream?mode=stream
Compatible H.264/AAC output GET /stream?mode=prepare
Server-side progress for a long file POST /download, then poll GET /jobs/:id

Optional bot check (Cloudflare Turnstile)

Set TURNSTILE_ENABLED=true with a site key and secret key (see .env.example) and fetching, streaming and downloading need a passed check. The official frontend handles it by itself and only shows the widget when Cloudflare needs a click. Details and setup: docs/API.md and docs/REQUIREMENTS.md. If you build your own frontend, call GET /config, render the widget, then POST /turnstile/verify when the visitor first does something protected.

Optional: everything as MP3

Set AUDIO_FORCE_MP3=true in .env (restart the server) and every audio download is delivered as an MP3. /fetch, /fetch/audio and /media/... then list all audio options as MP3 (the format ids stay the same), and any source that is not MP3 already is converted with ffmpeg: piped live in stream/auto mode with no file on disk, or, in prepare and POST /download mode, saved to a temporary file, converted, sent and deleted. The shipped .env.example turns it on (recommended); if the variable is left out it is off.

Building a frontend

The official one already exists: blazfetch-web. To build your own:

  • Send credentials: 'include' on every request: the server identifies visitors with a guest cookie (rate limits and concurrency use it).
  • Put your frontend's exact origin in CORS_ALLOWED_ORIGINS (* does not work with cookies).
  • Use stored.path from a fetch response for your own page URLs, and call GET /media/<platform>/<id> to render them.

Stored media

Everything fetched is kept in the database forever (metadata only, never the media files), so repeat requests are answered in milliseconds and every item gets a stable path such as /youtube/Cwkej79U3ek (playlists: /youtube/Cwkej79U3ek/playlist/RDCwkej79U3ek). Each item is re-checked every 7 days, so deleted or private videos are noticed and answer a clear 410 MEDIA_UNAVAILABLE instead of stale data, and usage statistics are stored per item and per event. See docs/API.md.

Configuration

npm run setup writes the database settings; everything else has a working default. The settings you are most likely to change (all in .env, full list with comments in .env.example):

Setting Default What it does
DATABASE_DRIVER, DATABASE_URL sqlite Which database, and where (npm run setup sets these)
CORS_ALLOWED_ORIGINS http://localhost:3000 Your frontend's origin(s), comma separated. * is refused in production
TRUST_PROXY 0 Set to 1 behind Nginx so real visitor IPs are seen
HOST 0.0.0.0 Set 127.0.0.1 behind same-host Nginx; keep the backend port private
API_AUTH_ENABLED false Require a manual server-to-server key; see API protection
API_AUTH_KEY empty Private 32-256 character key sent by the trusted proxy in X-API-Key, never frontend code
DEFAULT_DOWNLOAD_MODE stream Default for GET /stream when a request has no ?mode=
STREAM_MODE_ENABLED true Turn GET /stream off entirely
REVALIDATE_AFTER_SECONDS 604800 (7 days) How often each stored item is re-checked
TURNSTILE_ENABLED, TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY, TURNSTILE_SESSION_SECONDS false, 1800 s Optional Cloudflare Turnstile bot check in front of fetch, stream and download
YOUTUBE_FALLBACK_ENABLED true Use a fallback provider when YouTube blocks the server
MAX_PLAYLIST_ITEMS / MAX_PLAYLIST_DOWNLOAD_ITEMS 1000 / 200 Separate listing and bulk preparation limits
PLAYLIST_DOWNLOAD_CONCURRENCY 1 Bulk workers (1–4), also bounded by download limits
MAX_CONCURRENT_DOWNLOADS_GLOBAL / _PER_GUEST / _PER_IP 10 / 1 / 5 Concurrency limits (guests on one IP share _PER_IP)
MAX_CONCURRENT_CONVERSIONS / FFMPEG_THREADS 0 / 0 (auto) H.264/MP3 conversions allowed at once (auto: half the CPU cores) and the threads each gets; the rest queue, so a burst of conversions cannot exhaust the server
MEDIA_PROCESS_NICE 10 yt-dlp and ffmpeg run at this lower priority, so the API keeps answering under load
MIN_FREE_DISK_MB 1024 Downloads prepared on disk are refused ("server busy") while TEMP_DIR has less free space
RATE_LIMIT_MAX_GUEST / RATE_LIMIT_MAX_DOWNLOAD 30 / 10 per minute Rate limits per visitor
RATE_LIMIT_IP_MULTIPLIER 10 Per-IP ceiling, as a multiple of the per-visitor limits (only when TRUST_PROXY is set correctly behind a proxy)
MAX_DOWNLOAD_SIZE_BYTES, TEMP_DIR 2 GB, ./tmp Size cap and temporary folder

For existing deployments, update MAX_PLAYLIST_ITEMS explicitly, restart, and force-refresh cached playlists. See playlist and fallback setup for bulk jobs and optional providers.

Security

See SECURITY.md for how to report a problem, what the project already protects against, and what to do when you deploy.

Documentation

Document What's in it
docs/API.md Every endpoint, error codes, real responses per platform, stored media, modes
docs/examples/ Each platform's response as a JSON file
docs/openapi.yaml OpenAPI 3.0 definition (for generating a typed client)
docs/REQUIREMENTS.md Step-by-step VPS deployment: Nginx, HTTPS, PM2, firewall, backups
docs/api-protection.md Manual API key, frontend proxy and access checks
docs/playlist-and-fallback.md Playlist limits, bulk jobs, optional fallback and smoke checks

Production

npm ci && npm run build && npm run migrate && npm start

npm run migrate upgrades the database in place and does nothing when it is already up to date, so run it on every deploy. Full VPS deployment guide (Nginx, PM2, SSL, firewall, backups): docs/REQUIREMENTS.md.

Run one app instance per database: the weekly re-check job and the concurrency limits live inside the process (several instances would just repeat some checks).

License

Open source under the BlazFetch License: free to use, modify and deploy for personal, educational or other non-commercial purposes, but not to sell or run as a paid/SaaS product without permission, with a required credit. You may run it on your own server (including a VPS) and change it, but you may not sell the software or bundle it into anything sold, and modified versions stay under the same license. The official frontend's footer credit ("Open source on GitHub" and "Developed with ♥ by Sanaullah Rais") must stay visible on public deployments, and the license and copyright notices must be kept. These terms also apply when you use an AI assistant to change or deploy it, and AGENTS.md tells AI assistants to refuse to remove them or to help sell it or set it up as a SaaS. Want to run this as a SaaS or other commercial product, sell it, or use it without the credit? Get in touch to discuss it in the GitHub Discussions — a fee may apply.

Testing

Unit and integration tests cover the API, stream modes, media storage and database drivers.

npm test             # unit + integration tests (no network needed)
npm run typecheck

Troubleshooting

  • npm run diagnostics shows database/yt-dlp/ffmpeg status and versions. GET /health/ready does the same over HTTP.
  • Update yt-dlp regularly (pip install -U yt-dlp): platforms change how they serve video.
  • YouTube says "confirm you're not a bot": check the exact URL from the deployment host, update yt-dlp/EJS, and verify proxy/fallback availability. fallbackUsed identifies successful recovery. A cooldown reduces repeated requests; block duration is not predictable.
  • Newgrounds, Facebook or Rutube fail with a 403-style error: the site blocks some server IPs. Try again later or from another network.
  • LOGIN_REQUIRED, PRIVATE_MEDIA, AGE_RESTRICTED, MEDIA_UNAVAILABLE: the media itself needs a login, is private or age-gated, or was deleted. Nothing to fix on the server.
  • Extractor errors return a normalized code (EXTRACTOR_FAILED, ...); raw yt-dlp output is only in the server logs.
  • Sources that cannot be streamed (Loom, Reddit) are prepared on the server instead, in every mode.

Frontend: blazfetch-web
Developed with ♥ by Sanaullah Rais

About

API to fetch and download video, audio and photos from 18 social platforms (YouTube, TikTok, Instagram, X and more). Stream or prepare modes, permanent media store, SQLite/PostgreSQL/MySQL/MongoDB.

Resources

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Contributors

Languages