Quickstart
Install the SDK, authenticate with an API key, create a podcast, poll until it finishes, and read the audio URL.
This guide takes you from an empty project to a finished audio episode with the @marswave/listenhub-sdk JavaScript client. You will install the SDK, construct an OpenAPIClient with an API key, start a podcast, poll the task until it succeeds, and read the resulting audio URL.
Generation is asynchronous. Every create call returns an episodeId right away; you then poll a get* method until processStatus leaves pending. The full loop is at the end of this page and is copy-paste runnable.
You need an API key. Create one at listenhub.ai/settings/api-keys. Keys look like lh_sk_.... Keep them server-side — never ship a key in browser or mobile code. For user-facing apps, use ListenHubClient with OAuth instead (see Authentication).
Prerequisites
- Node.js >= 20 (the SDK is ESM-only).
- An API key from listenhub.ai/settings/api-keys.
Steps
Install the SDK
npm i @marswave/listenhub-sdkSet your API key
The OpenAPIClient reads LISTENHUB_API_KEY from the environment when you construct it with no arguments. Export it in your shell:
export LISTENHUB_API_KEY=lh_sk_...You can also pass the key explicitly: new OpenAPIClient({ apiKey: 'lh_sk_...' }). Prefer the environment variable so the secret never lands in source control.
Construct the client
import { OpenAPIClient } from '@marswave/listenhub-sdk';
const client = new OpenAPIClient(); // reads LISTENHUB_API_KEYThe client targets https://api.marswave.ai/openapi and sends Authorization: Bearer $LISTENHUB_API_KEY on every request. It unwraps the { code, message, data } envelope for you and retries 429 responses automatically. See Configuration to override the base URL, timeout, or retry count.
Pick a speaker
A podcast needs at least one speaker. List the available voices for a language and grab a speakerId:
const { items: speakers } = await client.listSpeakers({ language: 'en' });
const host = speakers[0];
console.log(`Using speaker: ${host.name} (${host.speakerId})`);Create the podcast
createPodcast accepts a query describing what you want, optional sources to ground the content, and the speakers array. It returns an episodeId immediately — generation runs in the background.
const { episodeId } = await client.createPodcast({
query: 'Explain how transformers work in large language models',
sources: [
{
type: 'url',
content: 'https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)',
},
],
speakers: [{ speakerId: host.speakerId }],
language: 'en',
});
console.log(`Created podcast: ${episodeId}`);Each source is { type: 'text' | 'url', content }. Use type: 'text' to pass raw text and type: 'url' to point at a web page the model should read.
Poll until it finishes
Call getPodcast(episodeId) on an interval. processStatus starts as pending; when it changes, the episode either succeeded (an audioUrl is present) or failed (check failCode and message).
let detail = await client.getPodcast(episodeId);
while (detail.processStatus === 'pending') {
await sleep(5000); // poll every 5 seconds
detail = await client.getPodcast(episodeId);
console.log(`Status: ${detail.processStatus}`);
}Read the audio URL
Once polling exits, read audioUrl from the detail. A finished episode also carries title, outline, and scripts.
if (detail.audioUrl) {
console.log(`Title: ${detail.title}`);
console.log(`Audio: ${detail.audioUrl}`);
} else {
console.error(`Generation failed (failCode ${detail.failCode}): ${detail.message}`);
}Full example
A single, copy-paste runnable script. It lists speakers, creates a podcast, polls until the task leaves pending, prints the audio URL, and reports remaining credits.
import { OpenAPIClient, ListenHubError } from '@marswave/listenhub-sdk';
const client = new OpenAPIClient(); // reads LISTENHUB_API_KEY
// 1. Pick a speaker.
const { items: speakers } = await client.listSpeakers({ language: 'en' });
const host = speakers[0];
console.log(`Using speaker: ${host.name}`);
// 2. Start the podcast (returns immediately).
const { episodeId } = await client.createPodcast({
query: 'Explain how transformers work in large language models',
sources: [
{
type: 'url',
content: 'https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)',
},
],
speakers: [{ speakerId: host.speakerId }],
language: 'en',
});
console.log(`Created podcast: ${episodeId}`);
// 3. Poll until generation leaves "pending".
let detail = await client.getPodcast(episodeId);
while (detail.processStatus === 'pending') {
await sleep(5000);
detail = await client.getPodcast(episodeId);
console.log(`Status: ${detail.processStatus}`);
}
// 4. Read the result.
if (detail.audioUrl) {
console.log(`Title: ${detail.title}`);
console.log(`Audio: ${detail.audioUrl}`);
} else {
console.error(`Generation failed (failCode ${detail.failCode}): ${detail.message}`);
}
// 5. Check remaining credits.
const sub = await client.getSubscription();
console.log(`Credits remaining: ${sub.totalAvailableCredits}`);
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}Flow speech instead of podcast
Flow speech turns text or a URL into narrated audio with one or more voices. The shape is the same — create, then poll a get* method — only the method names change. Use this when you want a read-through rather than a hosted conversation.
const { episodeId } = await client.createPodcast({
query: 'Explain how transformers work in large language models',
speakers: [{ speakerId: host.speakerId }],
language: 'en',
});
let detail = await client.getPodcast(episodeId);
while (detail.processStatus === 'pending') {
await sleep(5000);
detail = await client.getPodcast(episodeId);
}
console.log(detail.audioUrl);const { episodeId } = await client.createFlowSpeech({
sources: [{ type: 'text', content: 'Hello world, this is ListenHub.' }],
speakers: [{ speakerId: host.speakerId }],
language: 'en',
});
let detail = await client.getFlowSpeech(episodeId);
while (detail.processStatus === 'pending') {
await sleep(5000);
detail = await client.getFlowSpeech(episodeId);
}
console.log(detail.audioUrl);Handle errors
On a non-zero code or an HTTP error, methods throw a ListenHubError carrying status, code, and requestId. Catch it to distinguish API errors from other failures:
import { ListenHubError } from '@marswave/listenhub-sdk';
try {
await client.getPodcast('nonexistent-id');
} catch (err) {
if (err instanceof ListenHubError) {
console.error(`API error [${err.status}] code ${err.code} (request ${err.requestId})`);
} else {
throw err;
}
}Check cost before you generate
Credit costs vary by product, length, and options, so this guide does not quote numbers. Read your live balance with getSubscription() (the totalAvailableCredits field), and use the relevant estimate-credits endpoints — for example estimateVideoCredits — before committing to an expensive job. See the OpenAPI reference for which products expose an estimate endpoint.
Next steps
Authentication
API keys vs. OAuth tokens, the accessToken getter, and where credentials live.
Configuration
Base URL, timeout, retries, and environment variables for both clients.
Reference
Every client method grouped by product: podcast, TTS, image, music, video, and more.
Examples
Runnable scripts for podcasts, explainer videos, slides, images, music, and video.
OpenAPI reference
Endpoint-level detail: paths, parameters, enums, and response shapes.