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

@eldritchlogic/heygen-sdk

v0.1.0

Published

Fully-typed Node.js/TypeScript SDK for the HeyGen API — videos, video agent, avatars, voices, translations, lipsync, streaming, webhooks, and every other documented endpoint.

Readme

@eldritchlogic/heygen-sdk

Fully-typed Node.js/TypeScript SDK for the HeyGen APIevery documented endpoint is covered: videos, Video Agent, avatars, realtime avatars, voices & TTS, video translation, lipsync, AI clipping, HyperFrames, assets, webhooks, brand, workflows, background removal, plus the complete legacy (pre-v3) API surface.

  • 100% endpoint coverage, mechanically verified. A test suite walks HeyGen's published OpenAPI specs (vendored in specs/) and fails if any operation lacks an SDK method or hits the wrong method/path.
  • Fully typed. Request and response types are generated from HeyGen's official OpenAPI spec — every schema is exported by name.
  • Zero runtime dependencies. Built on the global fetch (Node.js ≥ 18.17, Bun, Deno, edge runtimes, modern browsers).
  • Batteries included. Cursor auto-pagination, job polling helpers, automatic retries with Retry-After support, idempotency keys, SSE streaming, and webhook signature verification.

Install

npm install @eldritchlogic/heygen-sdk

Quick start

import { HeyGen } from "@eldritchlogic/heygen-sdk";

const heygen = new HeyGen({ apiKey: process.env.HEYGEN_API_KEY });

// Create a video and wait for it to render.
const { video_id } = await heygen.videos.create({
  type: "avatar",
  avatar_id: "your_avatar_look_id",
  script: "Welcome to our product tour!",
  aspect_ratio: "auto",
  resolution: "1080p",
});
const video = await heygen.videos.waitForCompletion(video_id);
console.log(video.video_url);

Or let the Video Agent do everything from a prompt:

const session = await heygen.videoAgents.create({
  prompt: "A 30-second product intro for an AI note-taking app, upbeat tone",
});
const done = await heygen.videoAgents.waitForCompletion(session.session_id);

Authentication & configuration

const heygen = new HeyGen({
  apiKey: "...",            // default: process.env.HEYGEN_API_KEY (sent as x-api-key)
  accessToken: "...",       // alternative: OAuth2 bearer token
  baseUrl: "https://api.heygen.com",  // override for testing/proxies
  timeoutMs: 60_000,        // per-request timeout
  maxRetries: 2,            // automatic retries on 429/5xx/network errors
  defaultHeaders: {},       // merged into every request
  fetch: customFetch,       // bring your own fetch
});

Every method also accepts per-request options as its last argument:

await heygen.videos.create(body, {
  idempotencyKey: crypto.randomUUID(), // safe retries for mutations
  timeoutMs: 120_000,
  signal: abortController.signal,
  headers: { "x-trace-id": "..." },
});

Retries: GET/PUT/DELETE requests are retried automatically on 429, 5xx, and network errors (respecting Retry-After). POST/PATCH requests are only retried when you pass an idempotencyKey, which HeyGen deduplicates server-side for 24 h.

Pagination

List endpoints return a Page<T> with data, hasMore, and nextToken. Iterate the page to walk all pages lazily, or use nextPage() / toArray(limit):

for await (const voice of await heygen.voices.list({ language: "English" })) {
  console.log(voice.name);
}

const firstHundred = await (await heygen.videos.list()).toArray(100);

Polling helpers

Async jobs expose waitForCompletion (and friends) that poll until a terminal status:

await heygen.videos.waitForCompletion(videoId, { intervalMs: 5000, timeoutMs: 15 * 60_000 });
await heygen.videoTranslations.waitForCompletion(translationId);
await heygen.lipsyncs.waitForCompletion(lipsyncId);
await heygen.aiClipping.waitForCompletion(jobId);
await heygen.hyperframes.renders.waitForCompletion(renderId);
await heygen.backgroundRemovals.waitForCompletion(jobId);
await heygen.voices.waitForClone(voiceId);
await heygen.avatars.waitForTraining(groupId);
await heygen.videoTranslations.proofreads.waitForCompletion(proofreadId);

Prefer webhooks over polling in production — see below.

Webhooks

import { verifyWebhookSignature, WebhookVerificationError } from "@eldritchlogic/heygen-sdk";

// Register an endpoint (store the returned secret — it's only shown once):
const endpoint = await heygen.webhooks.endpoints.create({
  url: "https://yourapp.com/webhooks/heygen",
  events: ["avatar_video.success", "avatar_video.fail"],
});

// In your webhook handler (keep the raw body — parsed JSON won't verify):
app.post("/webhooks/heygen", express.raw({ type: "application/json" }), async (req, res) => {
  try {
    const event = await verifyWebhookSignature({
      payload: req.body,                          // raw Buffer/string
      signature: req.header("Heygen-Signature")!, // hex HMAC-SHA256
      timestamp: req.header("Heygen-Timestamp"),  // replay protection (±300 s)
      secret: process.env.HEYGEN_WEBHOOK_SECRET!,
    });
    // handle event ...
    res.sendStatus(200);
  } catch (err) {
    if (err instanceof WebhookVerificationError) return res.sendStatus(401);
    throw err;
  }
});

Error handling

All API failures throw typed subclasses of APIError with status, code, param, docUrl, requestId, headers, and the raw body:

| Status | Error class | | --- | --- | | 400 | BadRequestError | | 401 | AuthenticationError | | 403 | PermissionDeniedError | | 404 | NotFoundError | | 409 | ConflictError (e.g. idempotent retry still in flight) | | 422 | UnprocessableEntityError | | 429 | RateLimitError (with .retryAfter seconds) | | 5xx | InternalServerError | | network | APIConnectionError / APITimeoutError |

import { RateLimitError } from "@eldritchlogic/heygen-sdk";

try {
  await heygen.videos.create(body);
} catch (err) {
  if (err instanceof RateLimitError) console.log(`retry in ${err.retryAfter}s`);
  throw err;
}

Endpoint reference

Every operation in HeyGen's OpenAPI specs, and the SDK method that calls it.

Videos — heygen.videos

| Method | Endpoint | | --- | --- | | create(body) | POST /v3/videos | | get(videoId) | GET /v3/videos/{video_id} | | list(params?) | GET /v3/videos | | delete(videoId) | DELETE /v3/videos/{video_id} | | statuses(params?) | GET /v3/videos/statuses | | batches.create(body) | POST /v3/videos/batches | | batches.get(batchId, params?) | GET /v3/videos/batches/{batch_id} | | createV2(body) (deprecated) | POST /v2/videos | | getV2(videoId) (deprecated) | GET /v2/videos/{video_id} | | listV2(params?) (deprecated) | GET /v2/videos | | deleteV2(videoId) (deprecated) | DELETE /v2/videos/{video_id} |

Video Agent — heygen.videoAgents

| Method | Endpoint | | --- | --- | | create(body) | POST /v3/video-agents | | get(sessionId) | GET /v3/video-agents/{session_id} | | list(params?) | GET /v3/video-agents | | sendMessage(sessionId, body) | POST /v3/video-agents/{session_id} | | stop(sessionId, body?) | POST /v3/video-agents/{session_id}/stop | | listVideos(sessionId) | GET /v3/video-agents/{session_id}/videos | | getResource(sessionId, resourceId) | GET /v3/video-agents/{session_id}/resources/{resource_id} | | listStyles(params?) | GET /v3/video-agents/styles | | generateV1(body) (deprecated) | POST /v1/video_agent/generate |

Avatars — heygen.avatars

| Method | Endpoint | | --- | --- | | create(body) | POST /v3/avatars | | listGroups(params?) | GET /v3/avatars | | getGroup(groupId) | GET /v3/avatars/{group_id} | | deleteGroup(groupId) | DELETE /v3/avatars/{group_id} | | createConsent(groupId, body) | POST /v3/avatars/{group_id}/consent | | looks.list(params?) | GET /v3/avatars/looks | | looks.get(lookId) | GET /v3/avatars/looks/{look_id} | | looks.update(lookId, body) | PATCH /v3/avatars/looks/{look_id} | | looks.delete(lookId) | DELETE /v3/avatars/looks/{look_id} |

Avatar Realtime — heygen.avatarRealtime

| Method | Endpoint | | --- | --- | | createSession(body) | POST /v3/avatar-realtime | | getSession(streamId) | GET /v3/avatar-realtime/{stream_id} | | appendText(streamId, body) | POST /v3/avatar-realtime/{stream_id}/text | | cancel(streamId) | POST /v3/avatar-realtime/{stream_id}/cancel | | streamWords(streamId) — async iterator over SSE | GET /v3/avatar-realtime/{stream_id}/words |

Voices & audio — heygen.voices, heygen.audio

| Method | Endpoint | | --- | --- | | voices.list(params?) | GET /v3/voices | | voices.get(voiceId) | GET /v3/voices/{voice_id} | | voices.delete(voiceId) | DELETE /v3/voices/{voice_id} | | voices.clone(body) | POST /v3/voices/clone | | voices.design(body) | POST /v3/voices | | voices.speech(body) — TTS | POST /v3/voices/speech | | voices.textToSpeechV1(body) (deprecated) | POST /v1/audio/text_to_speech | | voices.listV1(params?) (deprecated) | GET /v1/audio/voices | | audio.searchSounds(params) | GET /v3/audio/sounds |

Templates — heygen.templates

| Method | Endpoint | | --- | --- | | list(params?) | GET /v3/templates | | get(templateId) | GET /v3/templates/{template_id} | | generate(templateId, body) | POST /v3/templates/{template_id} |

Video translation — heygen.videoTranslations

| Method | Endpoint | | --- | --- | | create(body) | POST /v3/video-translations | | get(id) | GET /v3/video-translations/{video_translation_id} | | list(params?) | GET /v3/video-translations | | update(id, body) | PATCH /v3/video-translations/{video_translation_id} | | delete(id) | DELETE /v3/video-translations/{video_translation_id} | | listLanguages() | GET /v3/video-translations/languages | | statuses(params?) | GET /v3/video-translations/statuses | | proofreads.create(body) | POST /v3/video-translations/proofreads | | proofreads.get(id) | GET /v3/video-translations/proofreads/{proofread_id} | | proofreads.downloadSrt(id) | GET /v3/video-translations/proofreads/{proofread_id}/srt | | proofreads.uploadSrt(id, body) | PUT /v3/video-translations/proofreads/{proofread_id}/srt | | proofreads.generateVideo(id, body?) | POST /v3/video-translations/proofreads/{proofread_id}/generate | | batches.create(body) | POST /v3/video-translations/batches | | batches.get(batchId, params?) | GET /v3/video-translations/batches/{batch_id} | | createV2(body) (deprecated) | POST /v2/video_translate | | listTargetLanguagesV2() (deprecated) | GET /v2/video_translate/target_languages | | getCaptionV2(params) (deprecated) | GET /v2/video_translate/caption |

Lipsync — heygen.lipsyncs

| Method | Endpoint | | --- | --- | | create(body) | POST /v3/lipsyncs | | get(lipsyncId) | GET /v3/lipsyncs/{lipsync_id} | | list(params?) | GET /v3/lipsyncs | | update(lipsyncId, body) | PATCH /v3/lipsyncs/{lipsync_id} | | delete(lipsyncId) | DELETE /v3/lipsyncs/{lipsync_id} | | statuses(params?) | GET /v3/lipsyncs/statuses | | batches.create(body) | POST /v3/lipsyncs/batches | | batches.get(batchId, params?) | GET /v3/lipsyncs/batches/{batch_id} |

HyperFrames — heygen.hyperframes

| Method | Endpoint | | --- | --- | | renders.create(body) | POST /v3/hyperframes/renders | | renders.get(renderId) | GET /v3/hyperframes/renders/{render_id} | | renders.list(params?) | GET /v3/hyperframes/renders | | renders.delete(renderId) | DELETE /v3/hyperframes/renders/{render_id} |

AI Clipping — heygen.aiClipping

| Method | Endpoint | | --- | --- | | create(body) | POST /v3/ai-clipping | | get(jobId) | GET /v3/ai-clipping/{job_id} | | list(params?) | GET /v3/ai-clipping | | delete(jobId) | DELETE /v3/ai-clipping/{job_id} |

Assets — heygen.assets

| Method | Endpoint | | --- | --- | | upload(file, params?) — multipart | POST /v3/assets | | get(assetId) | GET /v3/assets/{asset_id} | | list(params) | GET /v3/assets | | delete(assetId) | DELETE /v3/assets/{asset_id} | | search(params) | GET /v3/assets/search | | createDirectUpload(body) | POST /v3/assets/direct-uploads | | completeUpload(assetId, body?) | POST /v3/assets/{asset_id}/complete | | statuses(params?) | GET /v3/assets/statuses | | batches.createDirectUploads(body) | POST /v3/assets/direct-uploads/batches | | batches.complete(body) | POST /v3/assets/complete/batches | | batches.get(batchId, params?) | GET /v3/assets/batches/{batch_id} |

Webhooks — heygen.webhooks

| Method | Endpoint | | --- | --- | | endpoints.create(body) | POST /v3/webhooks/endpoints | | endpoints.list(params?) | GET /v3/webhooks/endpoints | | endpoints.update(endpointId, body) | PATCH /v3/webhooks/endpoints/{endpoint_id} | | endpoints.delete(endpointId) | DELETE /v3/webhooks/endpoints/{endpoint_id} | | endpoints.rotateSecret(endpointId) | POST /v3/webhooks/endpoints/{endpoint_id}/rotate-secret | | listEventTypes() | GET /v3/webhooks/event-types | | listEvents(params?) | GET /v3/webhooks/events | | verify(params) | — HMAC verification helper |

Brand, users, workflows, background removal

| Method | Endpoint | | --- | --- | | brand.listKits(params?) | GET /v3/brand-kits | | brand.listGlossaries(params?) | GET /v3/brand-glossaries | | users.me() | GET /v3/users/me | | users.meV1() (deprecated) | GET /v1/user/me | | workflows.list() | GET /v1/workflows | | workflows.createExecution(body) | POST /v1/workflows/executions | | workflows.createGraphExecution(body) | POST /v1/workflows/graph-executions | | workflows.getExecution(executionId) | GET /v1/workflows/executions/{execution_id} | | backgroundRemovals.create(body) | POST /v3/background-removals | | backgroundRemovals.get(jobId) | GET /v3/background-removals/{job_id} | | backgroundRemovals.list(params?) | GET /v3/background-removals | | backgroundRemovals.delete(jobId) | DELETE /v3/background-removals/{job_id} |

Legacy API — heygen.legacy

The complete pre-v3 surface (supported by HeyGen until October 31, 2026). Prefer the v3 resources above for new work; these methods return loosely-typed objects where HeyGen does not document response schemas.

| Namespace | Endpoints | | --- | --- | | legacy.streaming | newSession, startSession, listSessions, sendTask, interruptTask, stopSession, createSessionToken, listAvatars/v1/streaming.* | | legacy.photoAvatars | generatePhotos, generateLooks, getGeneration, createGroup, addLooksToGroup, train, getTrainingStatus, addMotion, addSoundEffect, upscale, get, listGroups, listGroupAvatars/v2/photo_avatar/*, /v2/avatar_group* | | legacy.videoAvatars | create, getStatus, delete/v2/video_avatar | | legacy.videos | generate, getStatus, list, delete, createWebm/v2/video/generate, /v1/video_status.get, /v1/video.list, /v1/video.delete, /v1/video.webm | | legacy.templates | list, get, generate, getVariableSchema/v2/templates, /v2/template/{id}, /v2/template/{id}/generate, /v3/template/{id} | | legacy.webhooks | listEndpoints, addEndpoint, updateEndpoint, deleteEndpoint, listAvailableEvents/v1/webhook/* | | legacy.folders | create, list, update, trash, restore/v1/folders* | | legacy.brandVoices | list, update/v1/brand_voice/* | | legacy.avatars | listGET /v2/avatars | | legacy.voices | listGET /v2/voices | | legacy.user | getRemainingQuotaGET /v2/user/remaining_quota | | legacy.videoTranslate | getStatusGET /v2/video_translate/{id} | | legacy.assets | upload(file, contentType)POST upload.heygen.com/v1/asset |

Types

Every schema in HeyGen's OpenAPI spec is exported by name:

import type { CreateVideoV3RequestBody, VideoDetail, AvatarLookItem } from "@eldritchlogic/heygen-sdk";

Regenerating types

The specs are vendored in specs/. To refresh from HeyGen's published spec:

curl -s https://developers.heygen.com/openapi/external-api.json -o specs/external-api.json
npm run gen:types   # regenerates src/generated/*, then `npm test` verifies coverage

If HeyGen adds an endpoint, the coverage test fails until the SDK implements it.

License

MIT