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

@socialrouter/sdk

v0.7.5

Published

Official TypeScript SDK for the SocialRouter API

Readme

SocialRouter SDK

A unified API to extract data from social media platforms. SocialRouter routes each call across several data sources (Apify, Bright Data…) behind one contract per service, with normalized records and automatic failover.

Installation

npm install @socialrouter/sdk

Quick Start

import { SocialRouter } from "@socialrouter/sdk";

const sr = new SocialRouter({ apiKey: "sr_live_xxxxxxxxxxxxx" });

const result = await sr.run("linkedin/post.likes", {
  url: "https://www.linkedin.com/posts/johndoe_ai-sales-1234567890",
  limit: 100,
});

console.log(result.served_by); // "apify/apimaestro" — the offer that answered
for (const person of result.data) {
  console.log(`${person.name} — ${person.title} @ ${person.company}`);
}

The call is synchronous end to end: the returned run is already completed or failed.

Services and offers

A service is platform/service, e.g. reddit/subreddit.posts — one endpoint, one contract, one output shape. An offer is one concrete implementation of that service by a source: apify/harshmaur, brightdata/reddit.

By default you don't pick an offer: the router runs the failover chain (cheapest first, skipping offers whose batch cap is too small) and tells you which one answered via served_by. Pin one with provider when you want that offer and nothing else — pinning disables failover.

await sr.run("reddit/subreddit.posts", {
  url: "https://www.reddit.com/r/programming",
  provider: "apify/harshmaur", // optional — omit to let the router route
  options: { sort: "top", time: "week" },
});

Browse the catalogue at socialrouter.io/services, or fetch it (see below).

Typed per-service methods

Every callable service also has a typed method, grouped by platform:

await sr.reddit.subredditPosts({ url: "https://www.reddit.com/r/programming" });
await sr.linkedin.profileInfo({ url: "https://linkedin.com/in/alice", options: { includeEmail: false } });
await sr.googlemaps.placeSearch({ queries: ["coffee shops in Brooklyn"] });

run() and the typed methods are the same call. Both are correlated with the service at compile time:

  • a URL service takes url / urls, a query service takes query / queries — mixing them is a type error;
  • options is the exact set that service declares (sort: "hot" | "new" | "top" | "rising" on reddit/subreddit.posts), and a service with no options rejects the field.

The service list is generated from the live registry, so it only contains services that are actually served.

Configuration

const sr = new SocialRouter({
  apiKey: "sr_live_xxxxxxxxxxxxx",          // Required
  baseUrl: "https://api.socialrouter.io",   // Optional (default)
});

Batching

Pass urls (or queries) to send several inputs in one call. Each service's max_inputs per offer is in the catalogue; offers whose cap is smaller than your batch drop out of the failover chain instead of failing the call.

const result = await sr.run("linkedin/profile.info", {
  urls: [
    "https://linkedin.com/in/alice",
    "https://linkedin.com/in/bob",
    "https://linkedin.com/in/carol",
  ],
  limit: 50,
});

Catalogue

// Every callable service, with offers in failover order
const services = await sr.listServices();
for (const s of services) {
  console.log(s.endpoint, s.offers.map((o) => `${o.offer} $${o.price_per_record}`).join(", "));
}

// One platform, or one service
await sr.listServices({ platform: "reddit" });
const svc = await sr.getService("reddit/subreddit.posts");
console.log(svc.accepts);  // accepted input shapes, with examples
console.log(svc.options);  // typed options

// The sources behind the offers
const sources = await sr.listSources();

Fetching a past run

const result = await sr.getExtraction("ext_abc123");

Account

const balance = await sr.getBalance();
console.log(`Balance: $${balance.balance} ${balance.currency}`);

const usage = await sr.getUsage(30); // last 30 days
console.log(usage.total_requests, usage.total_records, usage.total_credits);
console.log(usage.by_provider); // keyed by offer id, e.g. "apify/harshmaur"

Error Handling

import {
  SocialRouter,
  AuthenticationError,
  InsufficientCreditsError,
  RateLimitError,
  SocialRouterError,
} from "@socialrouter/sdk";

try {
  await sr.run("linkedin/post.likes", { url: "..." });
} catch (err) {
  if (err instanceof AuthenticationError) {
    // 401 — invalid, revoked or missing API key.
    // `err.message` already tells the user to create a new key on the
    // dashboard; `err.hint` is that sentence alone, for your own UI.
    console.error(err.hint);
  } else if (err instanceof InsufficientCreditsError) {
    // 402 — not enough credits
  } else if (err instanceof RateLimitError) {
    // 429 — too many requests
    console.log(`Retry after ${err.retryAfter} seconds`);
  } else if (err instanceof SocialRouterError) {
    console.error(err.code, err.message, err.status);
  }
}

Validation errors (400/404) are written to be self-correcting: they name the field, the expected shape, and the valid alternatives — an unknown service lists the platform's services, an unknown offer lists the ones that serve it.

TypeScript Types

import type {
  SocialRouterConfig,
  RunInput,
  Extraction,
  ExtractionRecord,
  ExtractionStatus,
  Platform,
  ServiceName,
  ServiceSlug,
  ServiceOptionsMap,
  CatalogueService,
  CatalogueOffer,
  InputFormat,
  ServiceOption,
  SourceInfo,
  AccountBalance,
  UsageSummary,
  ApiErrorDetail,
} from "@socialrouter/sdk";

Migrating from 0.3.x

The API moved to one endpoint per service; the SDK follows.

| 0.3.x | 0.4.0 | |---|---| | extract({ url, provider: "apify/linkedin/profile.info" }) | run("linkedin/profile.info", { url }) | | search({ queries, provider: "apify/googlemaps/place.search" }) | run("googlemaps/place.search", { queries }) | | provider: "apify/reddit/group.posts:trudax" | run("reddit/subreddit.posts", { provider: "apify/trudax" }) | | fallback: false | pin provider (pinning is what disables failover) | | extractAndWait / searchAndWait | run — calls are synchronous | | listProviders() / getProvider(id) | listServices() / getService(slug); listSources() for the source view | | result.provider | result.served_by | | result.source / result.type | result.platform / result.service | | result.kind | gone — the input kind is a property of the service |

Some services were renamed with the migration (reddit/group.postsreddit/subreddit.posts, youtube/profile.postsyoutube/channel.videos, tiktok/post.infotiktok/video.info…). listServices() returns the current names.

License

MIT