Lightweight Node.js library for resolving multimedia links through third-party scraping services. Built for easy integration into backend services, bots, and other applications.
Key Features • Prerequisites • Folder Structure • Installation • Import Styles • Quick Start • Response Schema • Configuration • Error Handling • API Reference • Performance • Scraper Overview • Contributing • Issues & Requests
- Modular Scraper Engine: Lightweight HTTP parsers (
axios,cheerio) for most platforms, with optional browser automation (Puppeteer, Playwright) for Cloudflare-bypassed scrapers. - Unified JSON Schema: Every scraper speaks the same language — normalized response shape with title, thumbnail, type flags, and media download array.
- Platform Redundancy: Multiple scrapers per platform (TikTok ×4, Instagram ×3, YouTube, etc.) stacked as fallbacks when upstream services shift or go dark.
- Sub-second Responses: Direct API and page parsing instead of waiting for heavyweight browser render trees — most requests resolve in 2–6 seconds.
- Node.js >= 16.x
- npm or yarn
| Dependency | Version | Purpose |
|---|---|---|
axios |
^1.7.0 | HTTP client |
cheerio |
^1.0.0 | HTML parsing & DOM traversal |
Most scrapers use lightweight HTTP + DOM parsing only. Some scrapers require a browser for Cloudflare bypass:
# For savetik (TikTok) & fdown (Facebook) — requires Google Chrome installed
npm install puppeteer-core
# For snapinsta (Instagram)
npm install playwright-extra puppeteer-extra-plugin-stealth
npm install playwright # browser binariesscrapr/
├── index.js # Entry point — exports all platform modules
└── lib/
├── applemusic/
│ └── aplmate/
│ └── index.js
├── bandcamp/
│ └── bandcampdownloader/
│ └── index.js
├── bilibili/
│ └── snapwc/
│ └── index.js
├── douyin/
│ └── direct/
│ └── index.js
├── facebook/
│ ├── snapsave/
│ │ └── index.js
│ └── fdown/
│ └── index.js
├── instagram/
│ ├── indown/
│ │ └── index.js
│ ├── downreels/
│ │ └── index.js
│ └── snapinsta/
│ └── index.js
├── pinterest/
│ └── pindown/
│ └── index.js
├── soundcloud/
│ └── klickaud/
│ └── index.js
├── spotify/
│ ├── spotmate/
│ │ └── index.js
│ └── spotidown/
│ └── index.js
├── threads/
│ └── threadster/
│ └── index.js
├── tiktok/
│ ├── snaptik/
│ │ └── index.js
│ ├── tiktokio/
│ │ └── index.js
│ ├── savetik/
│ │ └── index.js
│ └── ssstik/
│ └── index.js
├── twitter/
│ ├── tweeload/
│ │ └── index.js
│ └── tvd/
│ └── index.js
└── youtube/
└── ytmp3/
└── index.js
- Every scraper lives in
lib/<platform>/<method>/index.js - Each
<method>/exports a single{ scrape }function <platform>/index.jsre-exports all methods for that platformindex.jsat root pulls everything together
Install the package directly from your repository URL:
npm install git+https://github.com/coflyn/scrapr.gitOr for local development / testing:
npm install /path/to/scraprconst scrapr = require("scrapr");
// Or destructure individual platforms:
const { tiktok, spotify, twitter } = require("scrapr");import scrapr from "scrapr";
import { tiktok, spotify } from "scrapr";const { tiktok, spotify } = require("scrapr");
(async () => {
// 1. TikTok video
const tiktokRes = await tiktok.tiktokio(
"https://www.tiktok.com/@_coflyn/video/7662892911448558865",
);
if (tiktokRes.status) {
console.log("Title:", tiktokRes.result.title);
console.log("Downloads:", tiktokRes.result.downloads);
}
// 2. Spotify track
const spotifyRes = await spotify.spotmate(
"https://open.spotify.com/track/5WOSNVChcadlsCRiqXE45K",
);
if (spotifyRes.status) {
console.log("Audio URL:", spotifyRes.result.downloads[0].url);
}
})();const { bandcamp } = require("scrapr");
(async () => {
const res = await bandcamp.bandcampdownloader(
"https://bandcamp.com/album/example",
{ quality: "320" }, // "128" | "320"
);
console.log(`${res.result.trackCount} tracks found`);
})();const { tiktok } = require("scrapr");
async function resolveTikTok(url) {
const fallbacks = [tiktok.tiktokio, tiktok.snaptik];
for (const scraper of fallbacks) {
const res = await scraper(url);
if (res.status) return res.result;
}
throw new Error("All TikTok scrapers failed");
}All scrapers resolve into a standardized JSON payload structure:
{
"status": true,
"result": {
"title": "Media Title / Song Name",
"thumbnail": "https://cdn.example.com/cover.jpg",
"type": "video",
"downloads": [
{
"url": "https://cdn.provider.com/file.mp4?expires=123",
"type": "video",
"quality": "720p"
}
]
}
}{
"status": false,
"message": "Error description / platform rate limit message"
}const axios = require("axios");
axios.defaults.timeout = 30000; // 30sconst axios = require("axios");
const HttpsProxyAgent = require("https-proxy-agent");
axios.defaults.httpsAgent = new HttpsProxyAgent("http://proxy:8080");| Error | Likely Cause | Fix |
|---|---|---|
Request failed with status code 4xx |
Upstream changed or blocked | Try alternate scraper |
Could not extract CSRF token |
Page structure changed | Report issue |
No download links found |
Private content or dead link | Check URL accessibility |
socket hang up / ETIMEDOUT |
Network issue / rate limit | Retry with delay or proxy |
Cannot read properties of undefined |
Parser mismatch | Report issue |
async function resolveInstagram(url) {
const scrapers = [instagram.indown, instagram.downreels];
for (const s of scrapers) {
const res = await s(url);
if (res.status) return res.result;
}
return null;
}| Platform | Scraper | Avg Time | Reliability | Notes |
|---|---|---|---|---|
| Apple Music | aplmate |
~3-5s | ⚪ Medium | Turnstile bypass, sometimes noise |
| Bilibili | snapwc |
~10-15s | 🟢 High | RSA + AES handshake, fails if snapwc down |
| Douyin | direct |
~3-5s | 🔴 Low | Fragile — page structure changes often |
snapsave |
~4-8s | 🟢 High | Packed JS unpacker, dual stream | |
fdown |
~8-15s | 🟡 Medium | Puppeteer + Chrome, Cloudflare bypass | |
| SoundCloud | klickaud |
~20-60s | 🟡 Medium | SSE worker, 128kbps only |
| TikTok | tiktokio |
~2-3s | 🟢 High | JSON API, rich metadata |
| TikTok | snaptik |
~3-5s | 🟢 High | Challenge-response AES, IP-safe |
| TikTok | ssstik |
~2-4s | 🟢 High | Axios + Cheerio, HTMX polling |
| TikTok | savetik |
~8-15s | 🟡 Medium | Puppeteer + Chrome, Cloudflare bypass |
| YouTube | ytmp3 |
~6-10s | 🟢 High | Init + poll, reliable |
indown |
~4-6s | 🟢 High | Axios + Cheerio, no browser | |
downreels |
~2-4s | 🟢 High | Direct API, lightweight | |
snapinsta |
~10-20s | 🟡 Medium | Playwright + stealth, Turnstile bypass | |
pindown |
~4-6s | 🟢 High | Token + API resolution | |
| Bandcamp | bandcampdownloader |
~5-15s | 🟢 High | Multi-track, 320kbps |
| Spotify | spotmate |
~3-5s | 🟢 High | CSRF + metadata |
| Spotify | spotidown |
~4-6s | 🟡 Medium | Cookie rotation |
| Twitter / X | tweeload |
~3-5s | 🟢 High | Multi-quality |
| Twitter / X | tvd |
~3-5s | 🟢 High | Direct CDN links |
| Threads | threadster |
~4-6s | 🟢 High | JWT token decode |
Fast response (~3-5s). Relies on form submission with Turnstile bypass. Returns MP3 download URLs. Sometimes produces duplicate links or noise ("Download Another Song").
Most complex scraper. Uses RSA key exchange + AES encryption for API communication. Requires multi-step session init and event logging. Returns muxed MP4 video with subtitles. Slower (~10-15s). Fails entirely if snapwc is down.
Pure page scrape without third-party service. Extracts video data from window._ROUTER_DATA using custom brace-balancing JSON parser. Returns both no-watermark and watermark variants. Fast (~3-5s). Most fragile — Douyin frequently changes page structure.
Uses snapsave.app backend with custom packed JS unpacker logic to decode download payloads. Returns high-resolution FB video URLs with and without audio streams.
Uses Puppeteer + Chrome browser automation via fdown.net to bypass Cloudflare protection. Requires Google Chrome installed and puppeteer-core dependency. Returns SD and HD video download links. Runs in non-headless mode for reliability.
Uses a challenge-response API with AES-256-CBC decryption. Fetches an encrypted token from /api/token, decrypts it with a known salt + SHA256-derived key, then solves a dynamic math challenge (b, r, c, m, n types) to produce an X-Verify header. Returns rich metadata including author info, stats, and both normal + HD video URLs. Fast (~3-5s). Immune to IP bans — the challenge rotates per request.
Uses direct POST requests to a JSON API endpoint. Extremely fast (~2-3s) and returns very detailed metadata (likes, comments, play count, slideshow images list, HD video, and audio). Handles photos/slideshows very well. Very clean output.
Axios + Cheerio scraper with HTMX-based token extraction. Fetches a session token from the homepage, then submits a POST via HTMX to resolve download links. Supports both video and photo/slideshow content. Returns watermarked and non-watermarked variants. Fast (~2-4s). No browser required.
Puppeteer + Chrome automation via savetik.co. Requires Google Chrome installed and puppeteer-core dependency. Returns video and audio download links. Runs in non-headless mode for Cloudflare bypass.
Worker-based scraping via SSE stream. Requires priming POST then waiting for worker completion. Slowest (~20-60s). Returns 128kbps MP3 only. No thumbnail or metadata. Worker may randomly fail on certain tracks.
Connects to ytmp3.mobi API. Requires an init handshake followed by polling a convert URL to monitor progress until the download link is ready. Reliable for both video (MP4) and audio (MP3). Polling adds slight latency (~6-10s total).
Murni Axios + Cheerio. Fetches a CSRF token and cookie from indown.io, then triggers a POST request to resolve the media URL. Resolves Instagram reels, videos, and images with good response speed (~4-6s). Does not require any headless browser runtime.
Uses direct POST requests to downreels.com API (zoraahub.com). Fast response (~2-4s) returning direct links for videos, images, and audio along with thumbnails without the need for HTML parsing. Highly stable and lightweight.
Playwright + stealth plugin automation via snapinsta.to. Uses headless Chromium to bypass Cloudflare Turnstile, fills form input, and waits for download links. Requires playwright-extra and puppeteer-extra-plugin-stealth. Slower (~10-20s) due to browser overhead.
Extracts session CSRF tokens and cookies from pindown.io, triggers backend action endpoints, and resolves intermediate API download tokens to return full high-res images and MP4 video downloads.
Uses bandcampdownloader.app backend. Resolves session cookies, extracts hidden CSRF tokens, parses a dynamically rendered base64 metadata array, and fires individual track requests. Supports single tracks as well as full albums/playlists with metadata (index, track title, artist, cover art, release year). Returns high-quality 320kbps MP3 download links.
Fetches a session cookie and CSRF token from spotmate.online, requests track metadata from /getTrackData, and triggers a track conversion via /convert. Fast response (~3-5s), returns standard MP3 downloads.
Uses spotidown.app backend. Triggers form actions with cookie rotation and returns single track downloads, album lists, or direct MP3 download buttons. Includes option for fetching high-resolution album cover arts.
POSTs target tweet URL to /en/download on tweeload.com using custom desktop user-agents. Highly reliable, returning multiple video download qualities (HD, SD, low-res) mapped via intermediate CDN download proxies.
POSTs to twittervideodownloader.com using session middleware CSRF tokens and GraphQL metadata. Extracts original, direct video.twimg.com CDN links without intermediate download proxies, giving you the fastest direct download speeds.
Resolves Instagram Threads media (images and videos) via threadster.app backend with JWT token payload decoding for direct download links.
- Fork the repo
- Create a feature branch:
git checkout -b feat/my-scraper - Add your scraper under
lib/<platform>/<method>/index.js - Export it in
lib/<platform>/index.jsand the rootindex.js - Follow the existing response schema (
{ status, result: { title, thumbnail, type, downloads } }) - Open a pull request
- Keep it lightweight — no puppeteer/playwright imports unless absolutely necessary
- Every scraper must return the standardized JSON schema
- Add a fallback scraper, don't replace existing ones
- Document quirks and failure modes in Scraper Overview
If you encounter any issues, broken scrapers, or request errors (which can happen frequently as source web pages update their endpoints), please open an issue.
You can also open an issue if you would like to request support for a new platform or scraper.
MIT - (c) 2026 coflyn