TypeScript SDK

The stophy package for Node.js and TypeScript: a typed client for search, video details, transcripts, comments, channels, and playlists.

Call the Stophy API from TypeScript without hand-writing fetch. The stophy package gives you a typed Stophy class over every REST endpoint. It returns the same response envelope as the API reference.

Install

npm install stophy

Works with Node.js 18 or later. Types ship with the package, so there is no @types install.

Initialize

Create one client and reuse it. Pass your API key explicitly and keep it in an environment variable, never in source.

import { Stophy } from "stophy";

const stophy = new Stophy({ apiKey: process.env.STOPHY_API_KEY! });

Keep keys out of source code. Store them in environment variables and never commit them to git. See Authentication.

Make a request

Every method returns the standard response envelope. Read data for the result, or read creditsRemaining, requestId, and cacheState off the top level. data is typed as optional, so reach for it with data?. or narrow it first.

const { data } = await stophy.video({
  type: "transcript",
  videoUrl: "https://www.youtube.com/watch?v=D7liwdjvhWc",
});

console.log(data?.text); // full transcript as a string

video() is overloaded on type, so data is typed for the variant you ask for (transcript, comments, details, replies, livechat) without casts.

The resolved value matches the response envelope:

{
  success: true
  requestId: string
  cacheState: "hit" | "miss"
  creditsUsed: number
  creditsRemaining: number
  warning?: string
  data?: object
}

Methods

Each method maps to one endpoint. Arguments mirror the request body documented in the API reference, so any field there is valid here.

MethodEndpointDescription
stophy.video(body)POST /v1/videoDetails, transcript, comments, replies, or live chat (set type)
stophy.search(body)POST /v1/searchSearch with filters for type, sort, date, duration, features
stophy.channel(body)POST /v1/channelChannel metadata and content by tab
stophy.playlist(body)POST /v1/playlistPlaylist items, paginated
stophy.credits()GET /v1/creditsCurrent credit balance
stophy.logs(query?)GET /v1/logsRecent request logs
stophy.usage(query?)GET /v1/usageDaily credit and request counts

Examples

// Search
const { data: results } = await stophy.search({
  q: "react tutorial",
  sortBy: "popularity",
  duration: "long",
});

// Channel videos
const { data: channel } = await stophy.channel({
  channelUrl: "https://www.youtube.com/@mkbhd",
  tab: "video",
});

// Comments, then replies to the first comment
const comments = await stophy.video({
  type: "comments",
  videoUrl: "https://www.youtube.com/watch?v=D7liwdjvhWc",
  sortBy: "top",
});
const replyToken = comments.data?.items[0]?.repliesToken;
if (replyToken) {
  const replies = await stophy.video({ type: "replies", continuationToken: replyToken });
}

// Autocomplete

// Account
const balance = await stophy.credits();
console.log(balance.data?.credits);

Pagination

Comments, replies, live chat, search, channel tabs, and playlists return a continuationToken. Pass it back to fetch the next page, and stop when it is missing.

let token: string | undefined;

do {
  const page = await stophy.video({
    type: "comments",
    videoUrl: "https://www.youtube.com/watch?v=D7liwdjvhWc",
    continuationToken: token,
  });

  console.log(page.data?.items);
  token = page.data?.continuationToken ?? undefined;
} while (token);

Error handling

Non-2xx responses throw a StophyError carrying status, code, message, and requestId. Branch on code so unknown codes fall through to your default case (see Error handling).

import { Stophy, StophyError } from "stophy";

const stophy = new Stophy({ apiKey: process.env.STOPHY_API_KEY! });

try {
  const { data } = await stophy.video({
    type: "details",
    videoUrl: "https://www.youtube.com/watch?v=D7liwdjvhWc",
  });
  console.log(data);
} catch (err) {
  if (err instanceof StophyError) {
    // err.status, err.code, err.message, err.requestId
    switch (err.code) {
      case "INSUFFICIENT_CREDITS":
        // top up from dashboard
        break;
      case "CONCURRENCY_LIMITED":
        // too many requests in flight; retry once one finishes
        break;
      case "UNAUTHORIZED":
        // check your API key
        break;
      default:
        console.error(err.status, err.code, err.message);
    }
  } else {
    throw err;
  }
}

Next steps

On this page