Skip to content
Heimdallm
Marketplace/Documentation
Documentation · API reference

Speech

POST https://api-staging.impossiblecarrot.cc/api/v1/audio/speech

Text in, audio out. The response body is the recording itself — no JSON envelope — in the codec you asked for. Requires a bearer API key.

Routed only to models whose output modality is speech, and only to endpoints that declare the voice, the codec and the speed you asked for. An endpoint speaks the voices it loaded, so one it did not is a different endpoint's request. Routing and provider preferences otherwise work as on chat.

Use a voice sample

Send a sample instead of selecting a named voice. Choose an endpoint with supports_voice_cloning: true; reference input is required capability, so an endpoint that cannot clone the sample is excluded. The sample takes precedence over voice. It is sent for this request and does not create a saved voice.

curl https://api-staging.impossiblecarrot.cc/api/v1/audio/speech \
-H "Authorization: Bearer $HEIMDALL_API_KEY" \
-F model=openbmb/voxcpm2 \
-F 'input=Hello, this is a new sentence in the reference voice.' \
-F file=@voice.wav \
-F response_format=wav \
--output speech.wav

WAV, MP3, FLAC, OGG, M4A and WebM uploads are accepted, up to 5 MiB. The endpoint must accept the sample format. A short, clear recording works best. Add a transcript form field if the sample's words are known. Other form fields follow the JSON request; provider is a JSON-encoded object.

For JSON clients, send input_references: [{type: "input_audio", input_audio: {data: "BASE64_BYTES", format: "wav"}, transcript: "The sample's words"}]. A sample may carry role: "reference", which is the one role an audio item takes and the meaning a sample naming no role already has. The data member contains raw base64, without a data-URL prefix. Named voices continue to work when no sample is supplied.

Voices

GET /speech/models lists every speech model with the voices, codecs and speed range its endpoints declare, and that is the list voice is matched against. Providers name their own voices; nothing obliges an endpoint to load the names the OpenAI SDKs enumerate — alloy, ash, ballad, coral, echo, fable, nova, onyx, sage, shimmer, verse — and those SDKs make voice a required argument, so one of the eleven is what an unchanged client sends.

Each of them lands in one of three places:

  • An endpoint of the model declares it. It is routed to that endpoint and spoken as asked.
  • None declares it, and the model's endpoints offer one voice between them. That voice is spoken and named back in the X-Heimdall-Voice response header. An endpoint offers a voice when it declares one called default, or when it declares exactly one voice — the two cases where standing in for a voice it does not have involves no choice.
  • None declares it, and the endpoints offer several voices or none. The request is a 400 whose message lists the voices the model is served with, and metadata.declared_voices carries them as an array. Voices do not sound alike, so picking one of several for you would serve a voice you did not choose and bill you for it.

Leaving voice out asks for whichever voice the endpoint defaults to. No X-Heimdall-Voice is sent then: the default is a voice only the endpoint knows the name of.

Request body

FieldTypeNotes
modelstringRequired.
inputstringRequired. The text to synthesize, at most 15,000 characters.
voicestringA voice the endpoint declares. Absent uses the endpoint's own default.
response_formatstringmp3 (default), wav, pcm, opus, flac, aac, mulaw, alaw.
speednumber0.25 to 4.0, honoured where the endpoint declares a range covering it.
seedinteger0 to 2,147,483,647, where the endpoint declares seeded sampling.
max_new_tokensinteger1 to 4,096 on the public wire; the selected endpoint may declare a narrower range.
instructionsstringFree text steering delivery, where the endpoint honours it.
input_referencesarrayOne input_audio voice sample to clone, where the endpoint declares it.
userstring (≤ 256)Opaque end-user identifier.
providerobjectProvider preferences.

provider.options carries a provider's own knobs, keyed by provider slug. Only the entry for the provider that serves the request is read, and a name that provider published no descriptor for is refused with a 400 naming it rather than forwarded.

Example

curl https://api-staging.impossiblecarrot.cc/api/v1/audio/speech \
-H "Authorization: Bearer $HEIMDALL_API_KEY" \
-H "Content-Type: application/json" \
-o speech.wav \
-d '{
  "model": "openbmb/voxcpm2",
  "input": "The sky above the port was the color of television.",
  "voice": "alloy",
  "response_format": "wav",
  "max_new_tokens": 2048
}'

Response

The body is the encoded audio, served with the codec's own content type:

response_formatContent-Type
mp3audio/mpeg
wavaudio/wav
pcmaudio/pcm
opusaudio/ogg; codecs=opus
flacaudio/flac
aacaudio/aac
mulawaudio/basic
alawaudio/x-alaw-basic

X-Heimdall-Voice is sent when the voice that spoke is not the one you named, carrying the name of the voice that did. X-Generation-Id and X-Provider-Name are sent as on chat completions, and what the call cost is read back from GET /generation under that id. There is no Content-Length: an endpoint that encodes as it decodes has no length to declare, and one that answers whole would have to be buffered here to find out. Where a provider streams its audio the body is relayed as it arrives, so a long passage starts playing before it finishes decoding.

A failed synthesis comes back as a provider error with the endpoint's own message, and nothing is billed. Once the first byte of audio has gone out the status is fixed at 200, so a failure after that arrives as a body that stops early rather than as an error document. The reference VoxCPM2 endpoint declares max_new_tokens from 3 to 4,096. A cap below 3 produces no audio on its serving engine. It declares no seed: its engine draws the sound's noise from a generator no request seeds, so a seed would change nothing.

Billing

Billed per character of input, counted by the marketplace from your request. The count is known before the request is sent, so a synthesis costs exactly what its text does and nothing reconciles afterwards. An endpoint that publishes no character price records the characters as unpriced rather than billing them at zero.

Seconds of audio produced are not billed. Where the codec states its own duration exactly — wav does, every compressed container does not — the figure is measured and published as a deployment metric. What you pay for is the text you submitted; how long an engine takes to say it is the engine's own pacing, and two callers submitting the same sentence pay the same amount.

Voice cloning input is your audio: it is forwarded to the endpoint for that one request and retained nowhere.

Discovery

GET https://api-staging.impossiblecarrot.cc/api/v1/speech/models

Every served speech model, in the same shape GET /models answers in, plus a speech object holding the union across the model's live endpoints:

{
  "id": "openbmb/voxcpm2",
  "name": "OpenBMB: VoxCPM 2",
  "architecture": { "input_modalities": ["text"], "output_modalities": ["speech"] },
  "speech": {
    "voices": ["default", "narrator"],
    "formats": ["mp3", "wav"],
    "min_speed": 0.5,
    "max_speed": 2
  },
  "links": { "details": "/api/v1/models/openbmb/voxcpm2/endpoints" }
}

A union is what the marketplace can be asked for somewhere, not what any one endpoint keeps: one endpoint speaks each of these voices and no endpoint speaks all of them. What a single endpoint speaks is speech_envelope on the endpoints listing links.details names, which is the reading to use when you pin a provider with provider.only. min_speed and max_speed are present together or not at all; absent, no endpoint declared a range and the model speaks at its own pace only.

DiscoverFind your next modelModel libraryAll models, versions, prices and capabilitiesBenchmarksMeasured quality, cost and time by serving configurationShowcaseImages, video and 3D made on the marketplaceProvidersCompare independent inference providersActivityMarketplace traffic and usageNetwork statusAvailability, latency and endpoint healthChatConversations, model settings and filesImage, video & 3DGenerate images, video and 3D assetsScene studioStage a shot in 3D, direct it and render it as a videoCoding playgroundBuild software with an AI agent on a machine of your ownOverview & routingIdentity, workspaces and routing preferencesCredits & billingBalance, deposits, ledger and reserved fundsAPI keysKeys scoped to workspaces, limits and expirationOrganizationsTeams, members and shared workspacesRequest historyRequests, receipts, sandbox sessions and account historyReferralsInvitations and referral earningsPrivacyData settings, exports and deletionDocumentationQuickstart, SDKs, pricing and guidesAPI referenceInteractive endpoint reference