npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

fastsaverapi

v0.1.0

Published

Official FastSaverAPI SDK for Node.js and TypeScript: Instagram, TikTok (no watermark), YouTube (MP4 up to 4K, MP3), Pinterest, Facebook, X/Twitter, RuTube and Likee downloader API, YouTube audio as a Telegram file_id, and Shazam song recognition. Zero de

Readme

fastsaverapi: Instagram, TikTok and YouTube downloader API for Node.js and TypeScript

One typed API to download Instagram reels and posts, TikTok videos without watermark, YouTube as MP4 (up to 4K) or MP3, plus Pinterest, Facebook, X/Twitter, RuTube and Likee. Includes Shazam song recognition and YouTube audio delivered as a Telegram file_id for music bots. Zero dependencies; Node.js, Bun, Deno, Cloudflare Workers and Vercel Edge. Free tier: 1,000 credits, no credit card.

npm types License: MIT CI Free 1,000 credits Telegram

import { FastSaver } from 'fastsaverapi'

const client = new FastSaver({ apiKey: 'fs_sk_...' }) // or set FASTSAVER_API_KEY
const video = await client.fetch('https://www.tiktok.com/@scout2015/video/6718335390845095173')
console.log(video.downloadUrl) // direct MP4 link, no watermark

Start free: 1,000 credits, no credit card. Create a key at api.fastsaver.io (email, Google or Telegram login), npm install fastsaverapi, and you are downloading in under a minute. Or try it from the shell: FASTSAVER_API_KEY=fs_sk_... npx fastsaverapi fetch <url>

Sign up · API docs · Pricing · Telegram community · Python SDK


Table of contents

Why FastSaverAPI

Scraping social networks yourself means proxies, cookies, IP bans, captchas and a parser that breaks every time Instagram or TikTok ships an update. FastSaverAPI is a hosted social media downloader API: you send a link and get back direct download URLs and metadata in one stable JSON shape.

  • Free to start: 1,000 credits on sign-up, no credit card, 10 requests per minute.
  • One endpoint for 7 platforms: Instagram, TikTok, Pinterest, Facebook, X/Twitter, RuTube and Likee through client.fetch(url), plus a full YouTube toolset. Every result has the same fields.
  • TikTok without watermark, Instagram reels, carousels and stories, YouTube up to 2160p (4K) and MP3, YouTube community posts.
  • Built for Telegram bots: turn a YouTube video into a Telegram file_id in one call. Your bot never downloads or uploads the file.
  • Shazam built in: recognise a song from a voice message and browse the charts.
  • Predictable billing: a fixed credit price per call; a failed resolve costs 0.1 credit.

Why this SDK

  • Zero dependencies, ESM and CommonJS, full TypeScript types, TSDoc on every method.
  • Runs everywhere: Node.js 18.3+, Bun, Deno, Cloudflare Workers, Vercel Edge.
  • Typed results: camelCase fields, convenience helpers (downloadUrls, isAlbum, bestVideoFormat, bestMatch, ...) and the untouched JSON on raw.
  • Precise errors: AuthenticationError, InsufficientCreditsError, RateLimitError, NotFoundError, FetchError, and InvalidArgumentError for mistakes caught before a request (so they cost nothing).
  • Automatic retries with backoff for network errors, HTTP 429 and 5xx; timeouts and AbortSignal on every call.
  • save() streams any download link to disk without sending your API key to the CDN.
  • A CLI: npx fastsaverapi fetch <url>.

FastSaverAPI vs DIY scrapers and ytdl-core-style libraries

Scraping libraries run on your server, and that is the whole difference:

| | Self-hosted scrapers / ytdl-core-style libraries | FastSaverAPI + fastsaverapi | |---|---|---| | Keeps working when platforms change | You upgrade, redeploy and hope | Handled server-side, same JSON | | Proxies, cookies, IP bans, captchas | Your problem and your cost | Included | | TikTok without watermark | Depends on the current extractor | Yes | | YouTube 4K and MP3 | Needs ffmpeg and CPU on your server | Done upstream, you get a link | | Telegram delivery | Download, then upload to Telegram | file_id in one call, no upload | | Song recognition | A separate service | Built in | | Runs on edge runtimes | Usually not (Node APIs, binaries) | Yes, it is just fetch | | Works without a third party | Yes | No, it is a hosted API | | Cost | Servers, proxies, your time | From $0; pricing |

When a self-hosted tool is still the right choice: one-off personal downloads, archiving your own content at zero marginal cost, or systems that must not call an external service.

Supported platforms and credit costs

| Platform | What you can download | Credits per call | |---|---|---| | Instagram | Posts, reels, carousels (albums) | 1.5 | | Instagram | Stories and highlights | 5 | | TikTok | Videos without watermark, photo posts, the original sound (tiktok.com, vm.tiktok.com, tt.site) | 1.5 | | YouTube | Video info and formats; community posts (images, polls, videos) | 2 | | YouTube | Search (YouTube Music) | 2 | | YouTube | Download MP4 144p–1080p, or audio (MP3) | 15 | | YouTube | Download 1440p (2K) or 2160p (4K) | 25 | | YouTube → Telegram | Audio as a Telegram file_id | 7 cached · 15 first time | | Pinterest | Pins, videos, idea pins (pinterest.com, country domains, pin.it) | 1.5 | | Facebook | Videos and reels (facebook.com, fb.com, fb.watch) | 1.5 | | X / Twitter | Videos, GIFs and images (x.com, twitter.com) | 1.5 | | RuTube | Videos (rutube.ru, rutube.com) | 3 | | Likee | Videos (likee.video, likee.com) | 3 | | Shazam | Identify a song from audio | 5 | | Shazam | Top charts by country | 1 | | Account | Balance and plan | free |

A call that cannot be resolved costs 0.1 credit, and so does a fetch() link whose host is not supported. Requests rejected before that (bad key, no credits, rate limit, invalid format) are free. The same numbers are exported as CREDIT_COSTS.

Install

npm install fastsaverapi
pnpm add fastsaverapi      # or: yarn add fastsaverapi · bun add fastsaverapi

Deno: import { FastSaver } from 'npm:fastsaverapi'. The package has no dependencies.

Get your API key

  1. Sign up at api.fastsaver.io (email, Google or Telegram). No credit card.
  2. Copy your fs_sk_... key from the dashboard.
  3. Put it in the environment: export FASTSAVER_API_KEY=fs_sk_..., or pass apiKey to the client.

Keep the key on your server. It is a secret: never ship it in browser code.

Quickstart

import { FastSaver, FastSaverError } from 'fastsaverapi'

const client = new FastSaver() // reads FASTSAVER_API_KEY

try {
  const { plan, credits, rpmLimit } = await client.balance() // free
  console.log(`${plan}: ${credits} credits, ${rpmLimit} requests/min`)

  const media = await client.fetch('https://www.instagram.com/reel/DRsmm9UjKfH/')
  console.log(media.platform, media.type) // 'instagram' 'video'
  for (const url of media.downloadUrls) console.log(url) // one link, or every album item
} catch (error) {
  if (error instanceof FastSaverError) console.error(error.name, error.message)
  else throw error
}

Create one client and reuse it; it is safe for concurrent calls.

Instagram downloader API (reels, posts, stories)

const post = await client.fetch('https://www.instagram.com/p/C9AlbumXyz1/')
if (post.isAlbum) {
  for (const item of post.items) console.log(item.type, item.downloadUrl) // carousel
} else {
  console.log(post.downloadUrl, post.width, post.height, post.duration)
}

// Stories and highlights use the same call (5 credits)
const story = await client.fetch('https://www.instagram.com/stories/natgeo/3456789012345678901/')
await client.save(story.downloadUrls[0], 'downloads/') // Node.js, Bun, Deno

Only public content is supported. A private or deleted post throws NotFoundError (0.1 credit).

TikTok downloader API (no watermark)

const tiktok = await client.fetch('https://vm.tiktok.com/ZMabc123/')
console.log(tiktok.downloadUrl) // MP4 without watermark
console.log(tiktok.music?.title, tiktok.musicUrl) // the original sound
if (tiktok.isAlbum) console.log(tiktok.downloadUrls) // photo posts come back as an album

YouTube to MP4 and MP3 download API (up to 4K)

// Metadata and every available format with its size (2 credits).
const info = await client.youtube.info('https://youtu.be/dQw4w9WgXcQ')
if (!info.isPost) console.log(info.title, info.availableFormats, info.bestVideoFormat)

// YouTube Music search: 10 results per page.
const { results } = await client.youtube.search('the weeknd', { page: 1 })
console.log(results[0]?.title, results[0]?.durationSeconds, results[0]?.url)

// MP4 '144p' ... '2160p', or 'audio' (MP3): 15 credits, 25 for 1440p/2160p.
const video = await client.youtube.download('https://youtu.be/dQw4w9WgXcQ', { format: '1080p' })

// The link is short-lived and needs no API key: save it right away.
const path = await client.save(video.downloadUrl, 'downloads/')
console.log(path)
  • An invalid format throws InvalidArgumentError before any request, so it costs nothing.
  • downloadUrl works for about 5 minutes and up to 3 opens (10 minutes for 2K/4K links), so download it immediately or hand it straight to your user.
  • save() follows redirects, never sends your key, writes through a temporary .part file and has an idle timeout instead of a total one, so large files are fine.

YouTube community posts

youtube.info() also takes community post links at the same price. Check isPost first:

const result = await client.youtube.info('https://www.youtube.com/post/UgkxB7POmB4C7U0I3kIEWRZpYfE2t-ieP9CA')
if (result.isPost) {
  const { post } = result
  console.log(post.type, result.author, post.text, post.likes) // image, video, text, poll, quiz, ...
  // Images are direct links; the CDN serves every one as "unnamed.jpg", so name them yourself.
  for (const [i, url] of post.downloadUrls.entries()) {
    await client.save(url, `downloads/${post.postId}_${i + 1}.jpg`)
  }
  for (const url of post.videoUrls) console.log('video:', url) // use youtube.download()
}

Telegram music bot: YouTube audio as a file_id (grammY, Telegraf)

audioForBot() puts the audio into your bot and returns its Telegram file_id: your bot just calls sendAudio with it, with no download, no upload and no 50 MB limit. 7 credits when the track is cached, 15 the first time. A file_id only works for the bot it was requested for.

grammY

import { Bot } from 'grammy'
import { FastSaver, FastSaverError, extractYoutubeId } from 'fastsaverapi'

const bot = new Bot(process.env.BOT_TOKEN ?? '')
const client = new FastSaver()

bot.on('message:text', async (ctx) => {
  const text = ctx.message.text.trim()
  const videoId = extractYoutubeId(text)
  if (!videoId || videoId === text) return ctx.reply('Send me a YouTube link.')
  try {
    const { fileId } = await client.youtube.audioForBot(videoId, ctx.me.username)
    if (fileId) await ctx.replyWithAudio(fileId)
  } catch (error) {
    if (error instanceof FastSaverError) return ctx.reply(`Sorry: ${error.message}`)
    throw error
  }
})

await bot.start()

Telegraf

import { Telegraf } from 'telegraf'
import { message } from 'telegraf/filters'
import { FastSaver, extractYoutubeId } from 'fastsaverapi'

const bot = new Telegraf(process.env.BOT_TOKEN ?? '')
const client = new FastSaver()

bot.on(message('text'), async (ctx) => {
  const videoId = extractYoutubeId(ctx.message.text)
  if (!videoId) return
  const { fileId } = await client.youtube.audioForBot(videoId, ctx.botInfo.username)
  if (fileId) await ctx.replyWithAudio(fileId)
})

await bot.launch()

Prefer forwarding the original message? audioForBot(id, bot, { returnFileId: false }) returns channelId and messageId for copyMessage instead. A full bot that also names songs from voice messages: examples/telegram-bot-grammy.ts.

Pinterest, Facebook, X/Twitter, RuTube and Likee downloader

Same call, same result shape:

for (const url of [
  'https://pin.it/1a2b3c4d',
  'https://www.facebook.com/reel/1234567890',
  'https://x.com/NASA/status/1234567890123456789',
  'https://rutube.ru/video/0123456789abcdef0123456789abcdef/',
  'https://l.likee.video/v/AbCdEf',
]) {
  const media = await client.fetch(url)
  console.log(media.platform, media.type, media.downloadUrls)
}

detectPlatform(url) tells you which platform a link belongs to before you spend a credit; it returns 'youtube' for YouTube links (use client.youtube.*) and null for unsupported hosts.

Shazam API for Node.js: identify songs, top charts

import { readFile } from 'node:fs/promises'

// mp3, m4a, ogg (Telegram voice notes) or mp4, up to 50 MiB; 5-15 seconds is enough (5 credits).
const match = await client.shazam.identify('clip.mp3') // a path, bytes, a Blob or a stream
console.log(match.artist, match.title, match.thumbnail, match.bestMatch?.url)

// In-memory audio: say what it is with the file name.
const voice = await client.shazam.identify(await readFile('voice.oga'), { filename: 'voice.ogg' })
console.log(voice.title)

// Charts for a country code or 'world': 10 tracks per page (1 credit).
const { results } = await client.shazam.top({ country: 'us', page: 1 })
for (const track of results) console.log(track.title, track.duration)

No match throws NotFoundError (code Music not recognized). Formats the API does not accept (wav, flac, WebM) are rejected before upload, free of charge.

Error handling

import {
  FastSaverError, AuthenticationError, InsufficientCreditsError, RateLimitError,
  InvalidArgumentError, NotFoundError, FetchError, RequestTimeoutError,
} from 'fastsaverapi'

try {
  await client.fetch('https://www.instagram.com/p/private-or-deleted/')
} catch (error) {
  if (error instanceof NotFoundError) console.log('private or deleted (0.1 credit)')
  else if (error instanceof FetchError) console.log('could not resolve:', error.code)
  else if (error instanceof InsufficientCreditsError) console.log('top up your balance')
  else if (error instanceof RateLimitError) console.log('slow down, retry in', error.retryAfter)
  else if (error instanceof AuthenticationError) console.log('check FASTSAVER_API_KEY')
  else if (error instanceof InvalidArgumentError) console.log('bad input, nothing was sent')
  else if (error instanceof RequestTimeoutError) console.log('timed out')
  else if (error instanceof FastSaverError) console.log(error.name, error.message)
  else throw error
}
FastSaverError
├── ConfigurationError         no API key, browser without opt-in, save() without a file system
├── InvalidArgumentError       rejected before any request (free)
├── APIError                   status, detail, body, requestId, retryAfter
│   ├── AuthenticationError    401
│   ├── InsufficientCreditsError
│   ├── RateLimitError         429 (after automatic retries)
│   ├── InvalidRequestError    "Invalid URL", invalid format, ...
│   │   └── ValidationError    422
│   ├── FetchError             could not resolve the media (code: fetch.failed, ...)
│   │   └── NotFoundError      private, deleted, or no Shazam match
│   └── ServerError            5xx (after automatic retries)
├── TransportError             network failure
│   └── RequestTimeoutError
└── DownloadError              save(): the link answered non-2xx (for example, it expired)

instanceof works across the ESM and CommonJS builds. Every API error carries requestId; include it when you contact support.

Retries, timeouts, abort and proxies

const client = new FastSaver({
  timeout: 30_000,  // ms per attempt (default 60 s; youtube.download 120 s, audioForBot 300 s)
  maxRetries: 3,    // network errors, 429 and 5xx; default 2
  userAgent: 'my-bot/1.0',
})

// Per call: a different timeout, or your own AbortSignal.
const controller = new AbortController()
setTimeout(() => controller.abort(), 10_000)
await client.youtube.info('https://youtu.be/dQw4w9WgXcQ', { timeout: 15_000, signal: controller.signal })
  • 429 is retried after 2 s and then 4 s, network errors and 5xx after 0.5 s and then 1 s, and Retry-After is honoured. Our own timeouts are never retried, because the server may already have done (and billed) the work.
  • API calls never follow redirects, so your key is never sent to another host.

Proxy or custom fetch: pass any fetch implementation, for example undici with a proxy:

import { fetch as undiciFetch, ProxyAgent } from 'undici'

const dispatcher = new ProxyAgent('http://proxy.local:8080')
const client = new FastSaver({ fetch: (url, init) => undiciFetch(url, { ...init, dispatcher }) })

Edge runtimes: Next.js, Cloudflare Workers, Deno, Bun

The SDK uses only fetch, so the same code runs on Node.js, Bun, Deno, Cloudflare Workers and Vercel Edge. save() and file-path uploads need a file system (Node.js, Bun, Deno); on edge runtimes, hand the downloadUrl to the user or stream it with fetch.

// Cloudflare Worker: redirect the user to a fresh MP3 link.
interface Env {
  FASTSAVER_API_KEY: string
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url).searchParams.get('url') ?? ''
    const client = new FastSaver({ apiKey: env.FASTSAVER_API_KEY }) // secrets come from env
    const audio = await client.youtube.download(url, { format: 'audio' })
    return audio.downloadUrl ? Response.redirect(audio.downloadUrl, 302) : new Response('no link', { status: 502 })
  },
}

Never put your fs_sk_ key in browser code. The API does not accept cross-origin browser requests anyway, and new FastSaver() refuses to start in a browser unless you pass dangerouslyAllowBrowser: true (for browser extensions with host permissions). Call the API from your server instead, as in the Next.js route handler example. Anyone who can reach such a route spends your credits, so protect it.

Command-line interface

export FASTSAVER_API_KEY=fs_sk_...
npx fastsaverapi fetch "https://www.tiktok.com/@scout2015/video/6718335390845095173"
npx fastsaverapi youtube info "https://youtu.be/dQw4w9WgXcQ"
npx fastsaverapi youtube search "the weeknd" --page 2
npx fastsaverapi youtube download "https://youtu.be/dQw4w9WgXcQ" --format audio --out downloads/
npx fastsaverapi youtube tg-audio dQw4w9WgXcQ --bot @my_music_bot
npx fastsaverapi shazam identify clip.mp3
npx fastsaverapi shazam top --country us
npx fastsaverapi balance
npx fastsaverapi save "<download url>" downloads/

Output is JSON (--compact for one line), errors go to stderr with exit code 1, usage errors exit with 2. Global options: --api-key, --base-url, --timeout (seconds), --help, --version.

npx fastsaverapi --compact fetch "$URL" | jq -r '.download_url // .items[].download_url'

Credits and rate limits

  • Free plan: 1,000 credits, 10 requests per minute, no credit card.
  • Paid plans add credits and higher limits: api.fastsaver.io/pricing.
  • The limit is per API key, in a fixed 60-second window shared by every paid endpoint. balance() is free and has its own limit of 60 calls per minute.
  • client.balance() returns plan, credits, rpmLimit and planExpiresAt. The bulk example paces requests to your rpmLimit: examples/bulk.ts.

FAQ

How do I download an Instagram reel or post in Node.js? await client.fetch(url) returns downloadUrl (or items for a carousel), and await client.save(url, 'downloads/') writes it to disk. Stories and highlights use the same call.

How do I download TikTok videos without a watermark in JavaScript? (await client.fetch(tiktokUrl)).downloadUrl is the clean MP4. Photo posts come back as an album, and music.downloadUrl is the original sound.

How do I convert YouTube to MP3 or MP4 without ytdl-core or ffmpeg? client.youtube.download(url, { format: 'audio' }) (or '1080p', up to '2160p') returns a short-lived link; save it with client.save(). Conversion happens on FastSaverAPI's side.

How do I send YouTube music in a Telegram bot without downloading it? client.youtube.audioForBot(videoId, '@your_bot') returns a Telegram file_id; pass it to sendAudio (ctx.replyWithAudio in grammY and Telegraf).

How do I recognise a song from a Telegram voice message? client.shazam.identify(bytes, { filename: 'voice.ogg' }) (5 credits) returns the title, artist, cover art and matching YouTube tracks.

Is it really free? Every account gets 1,000 credits without a card, at 10 requests per minute. That is about 660 Instagram or TikTok links, or 66 YouTube downloads.

Do I need cookies, a login or proxies? No. You send a public URL and an API key; everything else happens on FastSaverAPI's side.

Can I use it in a commercial app or a public bot? The SDK is MIT-licensed. Use of the API is governed by the FastSaverAPI terms at api.fastsaver.io.

Does it work with TypeScript, ESM and CommonJS? Yes: import { FastSaver } from 'fastsaverapi' or const { FastSaver } = require('fastsaverapi'), with full types for every result.

Is there a Python SDK? Yes: pip install fastsaver (GitHub), with the same features.

Examples

quickstart · bulk downloads · YouTube download · YouTube posts · Shazam · grammY bot · Next.js route · Cloudflare Worker

Support, contributing, license