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

fetchtium-wrapper

v2.3.0

Published

TypeScript wrapper for Fetchtium social media downloader - Extract media from Instagram, Facebook, Twitter, TikTok, YouTube, Bluesky, Reddit, Bilibili, SoundCloud, Pixiv, Eporner, Rule34Video, and more

Readme

Fetchtium API Wrapper v2.2.0

TypeScript wrapper for Fetchtium API social media downloader. Extract media from Instagram, Facebook, Twitter, TikTok, YouTube, and 9 more platforms with advanced features like automatic retry, caching, rate limiting, and batch processing.

Features

  • 🚀 Zero runtime dependencies (uses native fetch)
  • 📦 Full TypeScript support with type definitions
  • 🎯 Simple API - one function call to fetch media
  • 🔄 Automatic retry with exponential backoff
  • 💾 Response caching with TTL
  • 🚦 Rate limiting and request queue
  • 📦 Batch processing for multiple URLs
  • 🎬 YouTube video+audio merge support
  • 🌐 14 platforms supported

Installation

npm install fetchtium-wrapper
# or
yarn add fetchtium-wrapper

Quick Start

import { FetchtiumClient } from 'fetchtium-wrapper';

const client = new FetchtiumClient({
  apiKey: 'sk-dwa_xxxxx',
  baseUrl: 'http://localhost:8080', // optional
});

// Fetch media from URL
const result = await client.fetch('https://www.instagram.com/p/ABC123/');

if (result.success) {
  console.log(result.data.downloads);
  console.log(result.data.author);
  console.log(result.meta.responseTime);
}

Supported Platforms

Native Extractors (Enhanced)

| Platform | Status | Provider | |----------|--------|----------| | Instagram | ✅ | Native + Enhanced (GraphQL API) | | Facebook | ✅ | Native + Enhanced Extractor | | Twitter/X | ✅ | Native + Enhanced (Syndication API) | | TikTok | ✅ | Native + Enhanced (TikWM API) | | YouTube | ✅ | yt-dlp + Enhanced (Merge support) | | Bluesky | ✅ | gallery-dl |

Generic Extractors (yt-dlp / gallery-dl)

| Platform | Status | Provider | |----------|--------|----------| | Reddit | ✅ | yt-dlp | | Bilibili | ✅ | yt-dlp | | SoundCloud | ✅ | yt-dlp | | Pixiv | ✅ | yt-dlp | | Eporner | ✅ | yt-dlp (18+) | | Rule34Video | ✅ | yt-dlp (18+) | | Generic | ✅ | yt-dlp (fallback) |

Total: 13 platforms supported

Configuration

const client = new FetchtiumClient({
  // Required
  apiKey: string,      // Your API key
  
  // Optional
  baseUrl?: string,    // API base URL (default: 'http://localhost:8080')
  timeout?: number,    // Request timeout in ms (default: 30000)
  
  // Retry Configuration
  retry?: {
    maxRetries?: number;           // Max retry attempts (default: 3)
    retryDelay?: number;           // Initial delay in ms (default: 1000)
    backoffMultiplier?: number;    // Exponential backoff (default: 2)
    retryableErrors?: ErrorCode[]; // Error codes to retry
  },
  
  // Cache Configuration
  cache?: {
    enabled?: boolean;    // Enable caching (default: false)
    ttl?: number;         // Cache TTL in seconds (default: 300)
    maxSize?: number;     // Max cache entries (default: 100)
  },
  
  // Rate Limiting Configuration
  rateLimit?: {
    maxConcurrent?: number;         // Max concurrent requests (default: 5)
    queueTimeout?: number;          // Queue timeout in ms (default: 30000)
    respectServerLimits?: boolean;  // Respect Retry-After header (default: true)
  },
});

API Reference

fetch(url, options?)

Fetch media from a URL with automatic caching and rate limiting.

const result = await client.fetch('https://www.instagram.com/p/ABC123/');

// With skipCache option to bypass cache
const fresh = await client.fetch('https://www.instagram.com/p/ABC123/', { skipCache: true });

// Response structure
{
  success: true,
  data: {
    platform: 'instagram',
    contentType: 'carousel',
    id: 'ABC123',
    title: 'Post title',
    description: 'Post caption...',
    author: { name: 'User', username: 'user', avatar: '...', verified: true },
    thumbnail: 'https://...',
    engagement: { views: 1000, likes: 500, comments: 50, shares: 10 },
    downloads: [
      { index: 0, type: 'image', quality: 'HD', url: '...', extension: 'jpg' },
      { index: 1, type: 'video', quality: '1080p', url: '...', extension: 'mp4' }
    ],
    audio_capabilities: { has_separate_audio: false, can_convert_m4a: true, can_convert_mp3: true }
  },
  meta: {
    url: 'https://www.instagram.com/p/ABC123/',
    resolvedUrl: 'https://www.instagram.com/p/ABC123/',
    responseTime: 1234,
    isPublic: true,
    cached: false
  }
}

fetchWithRetry(url, retryConfig?)

Fetch with automatic retry for transient errors.

const result = await client.fetchWithRetry(url, {
  maxRetries: 3,
  retryDelay: 1000,
  backoffMultiplier: 2,
});

// Retries on: NETWORK_ERROR, TIMEOUT, RATE_LIMITED, INTERNAL_ERROR
// Respects Retry-After header from server

fetchBatch(urls, options?)

Fetch multiple URLs in batch with concurrency control.

const results = await client.fetchBatch(
  [
    'https://www.instagram.com/p/ABC123/',
    'https://www.facebook.com/share/p/xyz/',
  ],
  {
    concurrency: 3,      // Max concurrent requests (default: 3)
    stopOnError: false,  // Stop on first error (default: false)
  }
);

// Results array
[
  {
    url: 'https://www.instagram.com/p/ABC123/',
    success: true,
    data: FetchtiumResponse,
    responseTime: 1234,
  },
  {
    url: 'https://www.facebook.com/share/p/xyz/',
    success: false,
    error: FetchtiumError,
    responseTime: 567,
  },
]

convert(options)

Convert video to audio (MP3/M4A) using FFmpeg.

const result = await client.convert({
  url: 'https://www.youtube.com/watch?v=xxx',
  format: 'mp3', // or 'm4a'
  filename: 'my_audio', // optional
});

if (result.success && result.blob) {
  // Download the audio blob
  const url = URL.createObjectURL(result.blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = result.filename || 'audio.mp3';
  a.click();
}

Note: The convert endpoint extracts audio from any video URL. It uses FFmpeg to convert the video to MP3 (192kbps) or M4A (AAC 192kbps) format.

merge(options)

Merge YouTube video and audio streams (for high quality videos).

const result = await client.merge({
  url: 'https://www.youtube.com/watch?v=xxx',
  quality: '1080p',
  filename: 'my_video', // optional
});

if (result.success && result.blob) {
  // result.blob is a Blob object
  // result.filename is the suggested filename
  // result.size is the file size in bytes
}

Generic Platform

The Generic platform uses yt-dlp as a fallback extractor for URLs that don't match native extractors. It automatically detects the platform from the yt-dlp output.

Supported via Generic (yt-dlp):

  • Reddit - Posts, videos, images
  • Bilibili - Videos, comments
  • SoundCloud - Audio tracks
  • Pixiv - Images, illustrations
  • Eporner - Adult videos (18+)
  • Rule34Video - Adult videos (18+)

How it works:

  1. Generic extractor handles any URL as fallback
  2. Runs yt-dlp to fetch metadata
  3. Automatically detects platform from yt-dlp's extractor field
  4. Returns platform-specific results

Example:

// Reddit URL - automatically detected
const result = await client.fetch('https://www.reddit.com/r/...');

// Bilibili URL - automatically detected
const result = await client.fetch('https://www.bilibili.com/video/...');

// SoundCloud URL - automatically detected
const result = await client.fetch('https://soundcloud.com/...');

Note: Generic platform only supports the platforms listed above. For other sites, you may need a separate wrapper.

Cache Management

// Get cache stats
const stats = client.getCacheStats();
// { size: 5, maxSize: 100, enabled: true }

// Clear cache
client.clearCache();

Rate Limit Stats

// Get rate limit stats
const stats = client.getRateLimitStats();
// { activeRequests: 2, queuedRequests: 1, maxConcurrent: 5 }

Examples

Basic Usage

import { FetchtiumClient } from 'fetchtium-wrapper';

const client = new FetchtiumClient({
  apiKey: 'sk-dwa_xxxxx',
});

const result = await client.fetch('https://www.instagram.com/p/ABC123/');

if (result.success) {
  result.data.downloads.forEach(download => {
    console.log(`${download.type} - ${download.quality}: ${download.url}`);
  });
}

With Retry

const client = new FetchtiumClient({
  apiKey: 'sk-dwa_xxxxx',
  retry: {
    maxRetries: 5,
    retryDelay: 2000,
    backoffMultiplier: 2,
  },
});

const result = await client.fetchWithRetry('https://www.instagram.com/p/ABC123/');

With Caching

const client = new FetchtiumClient({
  apiKey: 'sk-dwa_xxxxx',
  cache: {
    enabled: true,
    ttl: 300,      // 5 minutes
    maxSize: 50,   // Max 50 cached responses
  },
});

// First request - hits API
const result1 = await client.fetch('https://www.instagram.com/p/ABC123/');

// Second request - returns cached response (instant)
const result2 = await client.fetch('https://www.instagram.com/p/ABC123/');

With Rate Limiting

const client = new FetchtiumClient({
  apiKey: 'sk-dwa_xxxxx',
  rateLimit: {
    maxConcurrent: 3,      // Max 3 concurrent requests
    queueTimeout: 30000,   // 30 second queue timeout
    respectServerLimits: true,
  },
});

// Requests are automatically queued if over limit
const results = await Promise.all([
  client.fetch('https://www.instagram.com/p/ABC123/'),
  client.fetch('https://www.facebook.com/share/p/xyz/'),
  client.fetch('https://www.tiktok.com/@user/video/123/'),
]);

Batch Processing

const client = new FetchtiumClient({
  apiKey: 'sk-dwa_xxxxx',
});

const urls = [
  'https://www.instagram.com/p/ABC123/',
  'https://www.facebook.com/share/p/xyz/',
  'https://www.tiktok.com/@user/video/123/',
];

// Process 3 URLs concurrently
const results = await client.fetchBatch(urls, {
  concurrency: 3,
  stopOnError: false, // Continue on errors
});

results.forEach(result => {
  if (result.success) {
    console.log(`✅ ${result.url}`);
    console.log(`   Downloads: ${result.data?.data.downloads.length}`);
  } else {
    console.log(`❌ ${result.url}`);
    console.log(`   Error: ${result.error?.code}`);
  }
});

YouTube Merge

const client = new FetchtiumClient({
  apiKey: 'sk-dwa_xxxxx',
});

const result = await client.merge({
  url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
  quality: '1080p',
  filename: 'never_gonna_give_you_up',
});

if (result.success && result.blob) {
  // Download the blob
  const url = URL.createObjectURL(result.blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = result.filename || 'video.mp4';
  a.click();
}

Utility Functions

import {
  detectPlatform,
  findBestQuality,
  findByQuality,
  formatFileSize,
  getDownloadsNeedingMerge,
} from 'fetchtium-wrapper';

// Detect platform from URL
detectPlatform('https://instagram.com/p/xxx'); // 'instagram'

// Find best quality download
const best = findBestQuality(result.data.downloads, 'video');

// Find specific quality
const hd = findByQuality(result.data.downloads, '1080p');

// Format file size
formatFileSize(1024 * 1024); // '1 MB'

// Get downloads that need merge (YouTube)
const needsMerge = getDownloadsNeedingMerge(result.data.downloads);

Error Handling

import { FetchtiumError, isFetchtiumError } from 'fetchtium-wrapper';

try {
  const result = await client.fetch(url);
} catch (error) {
  if (isFetchtiumError(error)) {
    console.log(`Error Code: ${error.code}`);
    console.log(`Message: ${error.message}`);
    console.log(`User Message: ${error.getUserMessage()}`);
    console.log(`Severity: ${error.severity}`);
    console.log(`Retryable: ${error.isRetryable()}`);
    console.log(`Suggestions: ${error.suggestions}`);
    console.log(`Context:`, error.context);
    
    switch (error.code) {
      case 'PRIVATE_CONTENT':
        console.log('Content is private');
        break;
      case 'RATE_LIMITED':
        console.log('Too many requests - retry after:', error.context.retryAfter);
        break;
      case 'INVALID_URL':
        console.log('Invalid URL');
        break;
      default:
        console.log(error.getUserMessage());
    }
  }
}

Error Codes

| Code | Description | Retryable | |------|-------------|-----------| | PRIVATE_CONTENT | Content is private | No | | LOGIN_REQUIRED | Login required to access | No | | RATE_LIMITED | Too many requests | Yes | | NO_MEDIA | No downloadable media found | No | | CONTENT_REMOVED | Content has been removed | No | | NETWORK_ERROR | Network connection error | Yes | | TIMEOUT | Request timed out | Yes | | INVALID_URL | Invalid or unsupported URL | No | | UNSUPPORTED_PLATFORM | Platform not supported | No | | INVALID_API_KEY | Invalid API key | No | | INTERNAL_ERROR | Server error | Yes | | COOKIE_REQUIRED | Authentication required | No | | COOKIE_EXPIRED | Session expired | No | | CONTENT_NOT_FOUND | Content not found | No | | PARSE_ERROR | Failed to parse content | No | | CHECKPOINT_REQUIRED | Account verification required | No | | SCRAPE_ERROR | Failed to extract content | No | | UNAUTHORIZED | Authorization failed | No | | FORBIDDEN | Access forbidden | No | | BAD_REQUEST | Invalid request format | No | | STORY_EXPIRED | Story has expired | No | | AGE_RESTRICTED | Content is age-restricted | No |

Performance Tips

  1. Enable Caching: For frequently accessed URLs, enable caching to reduce response time by up to 100%
  2. Use Batch Processing: Process multiple URLs efficiently with controlled concurrency
  3. Configure Retry: Adjust retry settings based on your use case
  4. Rate Limiting: Set appropriate concurrency limits to avoid overwhelming the server

Requirements

  • Node.js >= 18.0.0 (uses native fetch)
  • TypeScript >= 5.0.0 (for type definitions)

License

GPL-3.0