Permalink to AudioAudio

Audio endpoints have different response shapes:

RouteShape
POST /v1/audio/speechsynchronous — returns audio bytes, or JSON
POST /v1/audio/transcriptionssynchronous — returns text or JSON
GET /v1/audio/transcriptions/:request_idread-only — retrieves a saved transcription
POST /v1/audio/musicasynchronous — returns a job

Check availability first

Audio models are enabled per deployment. Find the ones your key can reach before you write against a name:

bash
curl -s https://hypit.ai/v1/models \
  -H "Authorization: Bearer $HYPIT_API_KEY" |
  jq -r '.data[] | select(.endpoints[] | test("audio|transcriptions")) | "\(.id)\t\(.endpoints)"'

Permalink to speechSpeech

bash
curl https://hypit.ai/v1/audio/speech \
  -H "Authorization: Bearer $HYPIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "'"$MODEL"'", "input": "The boat went over the weir.", "voice": "alloy"}' \
  --output speech.mp3

client.audio.speech.* in the OpenAI SDKs works against this route unchanged.

Permalink to request-bodyRequest body

FieldTypeNotes
modelstringrequired
inputstringthe text. text is accepted as a synonym
promptstringfree-form direction, on models that take one
lyricsstring
voicestringa named voice
reference_idstringa cloned-voice id
voice_descriptionstringdescribe a voice instead of naming one
response_formatstringmp3, wav, pcm, opus, flac, aac, ogg. format is a synonym
speed, volume, loudnessnumbervendor-defined ranges
sample_rate, bitrateintegermp3_bitrate is a synonym for bitrate
duration_secondsnumberseconds and duration are synonyms
ninteger1–8, default 1
seedinteger
languagestring
instrumental, loopboolean
guidance_scale, prompt_influencenumber
reference_audioarray of stringshttp(s) or data: URLs
auto_generate_textboolean
outputstringbinary (default), b64_json, or url

At least one of input/text, prompt, lyrics or voice_description is required. Combined input + lyrics is capped at 40 000 characters; the whole JSON body at 12 MiB. Unmodelled keys are forwarded to the upstream verbatim.

Permalink to mimo-voicecloneMiMo VoiceClone

mimo-v2.5-tts-voiceclone requires exactly one MP3 or WAV voice sample. Prefer reference_audio; it accepts either an http(s) URL or a complete data URL, and the gateway fetches and inlines it when the selected provider requires bytes. If you use voice directly, send a data URL (bare base64 from older clients is also accepted):

json
{
  "model": "mimo-v2.5-tts-voiceclone",
  "input": "This sentence is spoken in the voice from the reference audio.",
  "reference_audio": ["data:audio/wav;base64,UklGRg..."],
  "response_format": "wav"
}

MiMo requires data:audio/mpeg;base64,... or data:audio/wav;base64,..., with at most 10 MiB in the base64 portion. The gateway checks the file's magic bytes before dispatch, so a spoofed MIME type or another audio container returns 400 without spending an upstream call.

Permalink to responseResponse

With output unset or binary, the response is raw audio bytes with the sniffed Content-Type (defaulting to audio/mpeg), X-Content-Type-Options: nosniff and a Content-Disposition: inline; filename="job_….mp3".

With output set to b64_json or url, the response is JSON:

json
{
  "object": "audio.speech",
  "created": 1787824589,
  "model": "…",
  "format": "mp3",
  "mime_type": "audio/mpeg",
  "b64_json": "SUQzBA…",
  "seconds": 3.4,
  "usage": { "characters": 28, "audio_seconds": 3.4 }
}

output: "url" quietly falls back to b64_json when no stored URL could be produced, so handle both keys. A voice-design request answers with "object": "audio.voice_previews" and a previews[] array of {voice_id, name, description, b64_json|url, mime_type, format, seconds}.

Permalink to transcriptionsTranscriptions

bash
curl https://hypit.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $HYPIT_API_KEY" \
  -F model="$MODEL" \
  -F file=@interview.mp3 \
  -F response_format=verbose_json

client.audio.transcriptions.* in the OpenAI SDKs works against this route unchanged.

Accepts multipart/form-data or application/json. Anything else is 400 unsupported_content_type.

FieldNotes
modelrequired
filethe clip — multipart only
urlan http(s) URL instead of a file. Exactly one of file / url
response_formatjson (default), text, verbose_json
languageISO code hint
promptcontext hint
temperaturenumber
timestamp_granularities[]repeated: segment and/or word. The bare spelling timestamp_granularities is also accepted

Limits: 100 MiB per uploaded file, 120 MiB for the whole multipart body, 4 MiB for a JSON body. The declared Content-Type of the part is ignored — the bytes are sniffed, and the accepted containers are WAV, FLAC, Ogg (Vorbis/Opus), MP3, ADTS AAC, M4A, MP4 and WebM. Raw headerless PCM is rejected.

response_format: "text" returns text/plain with the bare transcript. Everything else returns JSON:

json
{
  "task": "transcribe",
  "language": "en",
  "duration": 92.4,
  "text": "…",
  "segments": [
    { "id": 0, "start": 0.0, "end": 3.2, "text": "…", "no_speech_prob": 0.01 }
  ],
  "words": [{ "word": "the", "start": 0.10, "end": 0.22 }],
  "usage": { "audio_seconds": 92.4 }
}

task, language, duration, segments and words only appear for verbose_json. The usage block is ours and is present on the plain json shape too — unlike OpenAI, which omits it.

Permalink to retrieve-a-saved-transcriptionRetrieve a saved transcription

Save the server-generated X-Request-Id and Location response headers from the transcription POST. Once its durable reservation exists, Location points to this read-only endpoint, including when the POST later reports an uncertain upstream result:

bash
curl https://hypit.ai/v1/audio/transcriptions/req_YOUR_SERVER_REQUEST_ID \
  -H "Authorization: Bearer $HYPIT_API_KEY"

Use a currently valid API key belonging to the original account. OAuth tokens need user:jobs; user:inference:audio alone does not permit this read. Historical results remain readable if the model is later removed or the key's groups change. Another account receives the same 404 as an unknown ID. Reading never submits another upstream request or charges again.

json
{
  "id": "req_YOUR_SERVER_REQUEST_ID",
  "object": "audio.transcription",
  "model": "your-model",
  "status": "completed",
  "settlement_state": "settled",
  "created_at": 1788900000,
  "completed_at": 1788900010,
  "expires_at": 1791492010,
  "result": {
    "text": "Hello world.",
    "language": "en",
    "duration": 4.5,
    "usage": { "audio_seconds": 4.5 }
  }
}

The saved result is JSON regardless of the POST's response_format; segments and words are included when supplied by the provider. Provider raw responses and input URLs are not exposed.

HTTPstatusMeaning
202pending, processingNo saved result yet; wait at least Retry-After seconds before reading again
200completedThe saved transcript is in result
200failedThe upstream operation definitively failed
200unavailableProcessing ended without a reliable saved transcript
410expiredThe transcript's retention period ended

status describes the transcript; settlement_state describes billing independently. A recovered transcript can be completed while historical billing remains unknown. Normal successful POSTs save their results before sending the body. Accepted Replicate transcription tasks can also be polled after a restart using their saved upstream task ID; this does not promise background result recovery for every provider or every failure.

Results use the deployment's storage retention period: 30 days by default, measured from first result persistence; a negative retention setting keeps them indefinitely. Expiry removes the transcript while retaining financial records. Responses use Cache-Control: private, no-store. The saved public result must fit the same 32 MiB limit as buffered upstream responses; oversized results are rejected, never silently truncated. A confirmed oversized, undeliverable result returns 502 and releases the customer reservation; it is exposed as unavailable, with zero customer charge. An ordinary client disconnect or a missing GET body is not evidence for a refund.

This route requires the server-generated request ID. If the connection drops before those headers arrive, the client cannot discover that ID through this endpoint. A client-supplied X-Request-Id is not an idempotency key. Repeating the POST creates another request and can charge again; GET recovery does not add POST idempotency.

Permalink to musicMusic

POST /v1/audio/music is asynchronous. It accepts exactly the same body as /v1/audio/speech — same fields, same synonyms, same validation, same 12 MiB cap — and answers 202 Accepted with a job whose kind is audio.

bash
curl https://hypit.ai/v1/audio/music \
  -H "Authorization: Bearer $HYPIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "'"$MODEL"'",
    "prompt": "slow lo-fi piano, rain outside a window",
    "lyrics": "",
    "instrumental": true,
    "duration_seconds": 60
  }'

The fields that matter here are prompt, lyrics, instrumental, duration_seconds, voice, reference_id, reference_audio, language, seed and n. The output field is parsed and validated but has no effect: a job's artifacts always arrive through the assets API.

Then poll and collect exactly as in Asynchronous jobs. Two aliases point at the same handlers:

bash
curl https://hypit.ai/v1/audio/music/$JOB_ID \
  -H "Authorization: Bearer $HYPIT_API_KEY"

curl -L -o track.mp3 https://hypit.ai/v1/audio/music/$JOB_ID/content \
  -H "Authorization: Bearer $HYPIT_API_KEY"

Permalink to sdk-examplesSDK examples

python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["HYPIT_API_KEY"],
    base_url=os.environ.get("HYPIT_BASE_URL", "https://hypit.ai/v1"),
)

with client.audio.speech.with_streaming_response.create(
    model=os.environ["MODEL"],
    voice="alloy",
    input="The boat went over the weir.",
) as response:
    response.stream_to_file("speech.mp3")

with open("interview.mp3", "rb") as f:
    print(client.audio.transcriptions.create(model=os.environ["MODEL"], file=f).text)
js
import { createWriteStream } from "node:fs";
import { Readable } from "node:stream";
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.HYPIT_API_KEY,
  baseURL: process.env.HYPIT_BASE_URL ?? "https://hypit.ai/v1",
});

const speech = await client.audio.speech.create({
  model: process.env.MODEL,
  voice: "alloy",
  input: "The boat went over the weir.",
});
Readable.fromWeb(speech.body).pipe(createWriteStream("speech.mp3"));

Permalink to errors-specific-to-these-routesErrors specific to these routes

StatuscodeRouteMeaning
400missing_modelallmodel is required
400missing_inputspeech, musicno text, prompt, lyrics or voice description
413text_too_longspeech, musicover 40 000 characters
400invalid_nspeech, musicn must be 1–8
400invalid_durationspeech, musicduration must not be negative
400invalid_response_formatspeech, musicnot one of the seven audio formats
400invalid_outputspeech, musicnot binary, b64_json or url
413voice_sample_too_largespeechthe MiMo VoiceClone base64 sample exceeds 10 MiB
400unsupported_content_typetranscriptionsneither JSON nor multipart
400missing_file / ambiguous_inputtranscriptionssupply exactly one of file / url
400invalid_response_formattranscriptionsnot json, text or verbose_json
400invalid_timestamp_granularitytranscriptionsnot segment or word
400invalid_temperaturetranscriptionsnot a number
400unsupported_audio_formattranscriptionsthe bytes are not a recognised container
400empty_file / unreadable_filetranscriptionsthe part carried nothing usable
413file_too_largetranscriptionsover 100 MiB
413body_too_largeallover the per-route body cap
502empty_audio_response / empty_transcription_responsespeech, transcriptionsthe upstream returned nothing usable