Skip to content

Latest commit

 

History

History
781 lines (567 loc) · 14.8 KB

File metadata and controls

781 lines (567 loc) · 14.8 KB

Reference

audio

client.audio.speech({ ...params }) -> Speechify.GetSpeechResponse

📝 Description

Synthesize speech audio from text or SSML. Returns the complete audio file plus billing and speech-mark metadata in a single JSON response. For low-latency playback or long-form text, use POST /v1/audio/stream. Set output_format for explicit sample-rate/bitrate control (e.g. pcm_16000 or ulaw_8000 for telephony).

🔌 Usage

await client.audio.speech({
    audio_format: "mp3",
    input: "Hello! This is the Speechify text-to-speech API.",
    model: "simba-3.2",
    voice_id: "geffen_32"
});

⚙️ Parameters

request: Speechify.GetSpeechRequest

requestOptions: AudioClient.RequestOptions

client.audio.stream({ ...params }) -> core.BinaryResponse

📝 Description

Synthesize speech and stream the audio back as it is generated, for low-latency playback. Set output_format in the body for explicit codec/sample-rate/bitrate control (e.g. pcm_16000 or ulaw_8000 for telephony), or fall back to the Accept header for the container; the response is raw audio bytes (HTTP chunked). For Base64-encoded audio with speech-mark metadata in a single JSON response, use POST /v1/audio/speech.

🔌 Usage

await client.audio.stream({
    body: {
        input: "input",
        voice_id: "voice_id"
    }
});

⚙️ Parameters

request: Speechify.StreamAudioRequest

requestOptions: AudioClient.RequestOptions

client.audio.streamWithTimestamps({ ...params }) -> core.Stream<Speechify.SpeechStreamEvent>

📝 Description

Synthesize speech and stream it back together with word-level speech marks, for text highlighting, captions and audio-text synchronization while the audio is still arriving.

The response is a Server-Sent Events stream. Each speech.chunk event carries a Base64-encoded run of audio, the speech marks that became final with it, or both - a chunk may carry only one of the two, and the last chunk of a stream is often marks-only. A terminal speech.done event ends the stream; there is no [DONE] sentinel. Ignore any event type you do not recognize, so that new event types do not break your integration.

Speech-mark times are absolute milliseconds from the start of the synthesis, so concatenate the audio chunks into one stream and apply the marks against that single timeline. Which chunk a mark arrives on is a delivery detail and carries no meaning. Times stay correct for every output_format: changing the codec or sample rate does not change the duration.

Speech marks are produced by the streaming-native models. The default simba-3.0 and simba-3.2 both serve this route; the legacy simba-english and simba-multilingual models return 400 speech_marks_unsupported here. For Base64-encoded audio and speech marks in one non-streamed JSON response, on any model, use POST /v1/audio/speech.

🔌 Usage

const response = await client.audio.streamWithTimestamps({
    body: {
        input: "Streaming long-form audio with the Speechify API.",
        model: "simba-3.2",
        voice_id: "geffen_32"
    }
});

const audioChunks: Buffer[] = [];

for await (const item of response) {
    switch (item.type) {
        case "speech.chunk":
            // `audio` is Base64 and the SDK does not decode it for you. It is
            // absent on a marks-only chunk, which the last chunk often is.
            if (item.audio != null) {
                audioChunks.push(Buffer.from(item.audio, "base64"));
            }
            // Mark times are absolute ms from the start of the synthesis, so
            // they apply to the concatenated audio, not to this chunk.
            for (const mark of item.speech_marks ?? []) {
                console.log(mark.start_time, mark.value);
            }
            break;

        case "speech.error":
            // A failure after the stream has opened arrives as an event, NOT as
            // a thrown error: the 200 status is already committed. The SDK does
            // not raise it for you, so handle it explicitly — otherwise the loop
            // ends normally and you treat truncated audio as a success.
            throw new Error(`${item.error.code}: ${item.error.message}`);

        case "speech.done":
            // Terminal event. There is no `[DONE]` sentinel.
            console.log("billable characters:", item.billable_characters_count);
            console.log("audio duration (ms):", item.audio_duration_ms);
            break;

        default:
            // Ignore unrecognized event types so new ones cannot break you.
            break;
    }
}

const audio = Buffer.concat(audioChunks);

⚙️ Parameters

request: Speechify.StreamWithTimestampsAudioRequest

requestOptions: AudioClient.RequestOptions

models

client.models.list() -> Speechify.ModelsResponse

📝 Description

List the text-to-speech models available for synthesis. Drive a model picker from this response, then pass a model id as the model parameter to POST /v1/audio/speech or /v1/audio/stream. The response marks the default model (used when a request omits model), the routes each model may be passed to, and which voices it accepts. Multi-speaker models arrive in a separate dialogue_models array because they are valid only on POST /v1/audio/dialogue. Returns the full set in a single response: the model catalog is static platform reference data, so it is intentionally not paginated.

🔌 Usage

await client.models.list();

⚙️ Parameters

requestOptions: ModelsClient.RequestOptions

voices

client.voices.list({ ...params }) -> core.Page<Speechify.GetVoice, Speechify.ListVoicesResponse>

📝 Description

Lists the voices available to the caller - the shared voice catalog plus the workspace's cloned voices, whichever member or service-account key created them. By default the full catalogue is returned in one response. Pagination is opt-in: pass limit (and then cursor from the previous response) to page through the list while has_more is true. Max page size is 200. Narrow the list with the type and locale filters (applied before pagination, so pages stay full).

🔌 Usage

const pageableResponse = await client.voices.list({
    locale: "en",
    model: "simba-3.2"
});
for await (const item of pageableResponse) {
    console.log(item);
}

// Or you can manually iterate page-by-page
let page = await client.voices.list({
    locale: "en",
    model: "simba-3.2"
});
while (page.hasNextPage()) {
    page = page.getNextPage();
}

// You can also access the underlying response
const response = page.response;

⚙️ Parameters

request: Speechify.ListVoicesRequest

requestOptions: VoicesClient.RequestOptions

client.voices.create({ ...params }) -> Speechify.GetVoice

📝 Description

Create a cloned voice for the workspace from a 10-30 second audio sample, with verified consent from the speaker.

Cloning requires proof that the speaker agreed to it. Create a consent challenge with POST /v1/voices/consent-challenges, show the returned phrase to the speaker, record them reading it aloud, and send that recording here as consent_recording together with the challenge's consent_challenge_id. Speechify transcribes the recording, checks it against the phrase it issued, checks that its speaker is the speaker in your sample, and keeps it as the consent record for the voice. The person consenting therefore has to be the person being cloned. A challenge is single use and short-lived, so record and submit in one sitting.

The clone belongs to the workspace rather than the member who created it, and access follows the caller's workspace role and API-key scopes exactly as for any other voice: voices scopes to list it, audio scopes to synthesize with it, and the content-management permission plus a write scope on the key to delete it. Cloned voices are usable self-serve on simba-3.0, simba-english and simba-multilingual. simba-3.2 also serves cloned voices, currently as a limited release enabled per workspace; contact Speechify to have it enabled for yours.

Callers pinned before Speechify-Version: 2026-09-13 use the previous flow instead: no challenge, and a consent form field carrying the speaker's name and email as a JSON string. That flow is deprecated and will be removed after a sunset window announced in the changelog.

🔌 Usage

await client.voices.create({
    sample: fs.createReadStream("/path/to/your/file"),
    consent_recording: fs.createReadStream("/path/to/your/file"),
    "Idempotency-Key": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    name: "name",
    gender: "male",
    consent_challenge_id: "consent_challenge_id"
});

⚙️ Parameters

request: Speechify.CreateVoicesRequest

requestOptions: VoicesClient.RequestOptions

client.voices.get({ ...params }) -> Speechify.GetVoice

📝 Description

Fetch a single voice by id - a shared catalogue voice or one of the workspace's cloned voices. A cloned voice that belongs to another workspace returns 404, identical to an unknown id, so voice inventory is never enumerable across tenants.

🔌 Usage

await client.voices.get({
    voice_id: "voice_id"
});

⚙️ Parameters

request: Speechify.GetVoicesRequest

requestOptions: VoicesClient.RequestOptions

client.voices.delete({ ...params }) -> void

📝 Description

Delete one of the workspace's cloned voices. Requires the content.manage permission (owner, admin, or member); a service-account key is authorized by its scopes instead.

🔌 Usage

await client.voices.delete({
    voice_id: "voice_id"
});

⚙️ Parameters

request: Speechify.DeleteVoicesRequest

requestOptions: VoicesClient.RequestOptions

client.voices.downloadSample({ ...params }) -> core.BinaryResponse

📝 Description

Download a personal (cloned) voice sample

🔌 Usage

await client.voices.downloadSample({
    voice_id: "voice_id"
});

⚙️ Parameters

request: Speechify.DownloadSampleVoicesRequest

requestOptions: VoicesClient.RequestOptions

Voices ConsentChallenges

client.voices.consentChallenges.create({ ...params }) -> Speechify.ConsentChallenge

📝 Description

Start the consent check for a voice clone.

Returns a phrase for the speaker to read aloud and an id that identifies this challenge. Show the phrase to the speaker exactly as returned, record them reading it, then send the recording and the id to POST /v1/voices, which verifies the recording against the phrase and against the voice sample being cloned, then keeps it as the consent record.

A challenge is single use, is bound to the workspace that created it, and expires at expires_at - it is proof that a speaker was in front of a microphone just now, so create it when you are ready to record, not at the start of your flow. If it expires, create another one and record again.

Challenge creation is rate limited per workspace at a few dozen per hour, far more tightly than the rest of the voice surface, because each one precedes a person recording themselves - mint it when your speaker is ready, not speculatively. Read the live ceiling off RateLimit-* rather than hard-coding it. On a 429, always honour Retry-After rather than a fixed backoff of your own: the wait is measured in minutes and can run to most of an hour. RateLimit-* are omitted rather than reporting a bucket that is not the one refusing.

🔌 Usage

await client.voices.consentChallenges.create({
    "Idempotency-Key": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    full_name: "Jane Doe"
});

⚙️ Parameters

request: Speechify.voices.CreateConsentChallengeRequest

requestOptions: ConsentChallengesClient.RequestOptions