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

nexara-sdk

v0.4.0

Published

TypeScript SDK for the Nexara speech-to-text API: transcription, diarization, speaker roles, emotion recognition, structured LLM output, billing

Readme

Nexara TypeScript SDK

TypeScript/JavaScript SDK for the Nexara speech-to-text API: transcription, speaker diarization, speaker role tagging, structured LLM post-processing, and account billing. Full API documentation lives at docs.nexara.ru.

Requires Node.js 20+. Fully typed; ships its own .d.ts.

npm install nexara-sdk

Quickstart

import { Nexara } from "nexara-sdk";

const client = new Nexara({ apiKey: "..." }); // or set NEXARA_API_KEY

const { text } = await client.transcriptions.create({ file: "audio.mp3" });

Pass exactly one of file (a path, Uint8Array, or Blob — paths are streamed from disk, not read into memory) or url. The return type is narrowed from task × response_format: create({ file }) gives { text }, response_format: "verbose_json" gives the full object, "srt"/"vtt"/"text" give a string, and passing prompt gives an LLMResult.

Diarization

const call = await client.transcriptions.create({ file: "call.mp3", task: "diarize" });
for (const segment of call.segments) {
  console.log(`${segment.speaker}: ${segment.text}`);
}

Add meaningful speaker labels with roles"auto" lets the model invent labels, an array restricts them, an object adds descriptions:

await client.transcriptions.create({
  file: "call.mp3",
  task: "diarize",
  roles: ["client", "agent"],
});

Emotions

emotions: true attaches an emotion to each diarized segment — label (one of angry, sad, neutral, positive), confidence, and the full probs distribution when the server sends it:

const call = await client.transcriptions.create({
  file: "call.mp3",
  task: "diarize",
  model: "nexara-ru",
  emotions: true,
});
for (const segment of call.segments) {
  if (segment.emotion) {
    console.log(segment.speaker, segment.emotion.label, segment.emotion.confidence);
  }
}

The scoring runs inside the ASR model, so it requires task: "diarize", model: "nexara-ru" and a JSON response format; anything else throws NexaraValidationError before the upload (and the subtitle formats do not even accept the option in their types). Not every segment can be scored, so check segment.emotion rather than assuming it is there. It carries a per-second surcharge, charged only when emotion was actually returned.

Long audio: deferred jobs

createJob() submits the audio and returns immediately; the result is fetched by polling. A failed job is never billed, so resubmitting is free.

const job = await client.transcriptions.createJob({ file: "long_recording.mp3" });
const result = await job.wait(); // polls; default timeout 1800s

// ...or pick it up later, even from another process:
const same = await client.transcriptions.retrieveJob(jobId);

Job results live for 12 hours from creation; up to 200 jobs may be in progress per API key. In this SDK the deferred mode is createJob() — "async" refers only to Promises, not to the queue.

LLM post-processing

Pass prompt to run an LLM over the transcript, and optionally json_schema to force structured output:

const result = await client.transcriptions.create({
  file: "meeting.mp3",
  prompt: "Summarize the key decisions",
  json_schema: { type: "object", properties: { decisions: { type: "array" } } },
});
console.log(result.llm_output); // object, validated against your schema
console.log(result.transcription.text); // the transcript it was derived from

Balance and usage

client.billing reports what is on the account and what it has been spent on. Both endpoints cover the whole account, not just the key you authenticate with:

const balance = await client.billing.balance();
console.log(balance.balance, balance.currency, balance.rate_per_min);

// One page of billed calls, newest first.
const page = await client.billing.usage({ limit: 20 });
for (const item of page.items) {
  console.log(item.timestamp, item.task, item.cost, item.api_key.name);
}

// ...or let the SDK walk the pages. History is unbounded — bound it.
for await (const item of client.billing.iterUsage({ maxItems: 200 })) {
  console.log(item.request_id, item.seconds, item.cost);
}

Paging is keyset-based, not offset-based: pass a page's next_cursor as cursor to get the next (older) page, so calls arriving mid-walk cannot shift rows across a page boundary. item.cost is null — not 0 — for rows written before per-request costs were recorded, and rate_per_min covers plain transcription only (profanity_filter, roles, emotions and prompt are surcharges on top of it).

Errors and validation

Requests that the server would reject — or, worse, accept, charge for, and silently do something else with — throw NexaraValidationError before any network call. Server errors map to typed classes by status code:

import { InsufficientBalanceError } from "nexara-sdk";

try {
  await client.transcriptions.create({ file: "audio.mp3" });
} catch (e) {
  if (e instanceof InsufficientBalanceError) console.log(e.detail); // 402
}

429 and connection/timeout failures are retried with exponential backoff (honoring Retry-After). 500 is deliberately not retried: on the synchronous path the request may already have been billed, so a blind retry could pay twice. Deferred jobs bill only on success, which makes createJob() the safe path for retry-heavy workloads.

Not yet available

  • Realtime streaming — the protocol is not yet public; client.realtime throws for now.
  • Webhooks — job results are fetched by polling.

Development

npm install
npm run typecheck   # tsc --noEmit
npm test            # vitest, no network needed
npm run build       # emit dist/
./run_examples.sh   # every example, offline, on the mock transport

Tests run against an injected transport / mock fetch; NEXARA_USE_MOCK=1 runs the client and examples against an in-memory mock. See docs/design.md for the design rationale behind the interface. This SDK is a port of the Python SDK; the two share behavior and diverge only where the languages do.

License

MIT