/openai/v1 to its base URL itself — give it the bare /compat base:
/openai/v1 speaks the same protocol the OpenAI SDK uses, so everything
in Migrate from OpenAI applies
here too.
Model names your Groq code already sends keep working: playai-tts maps to
fish-audio/s2.1-pro, and whisper-large-v3, whisper-large-v3-turbo, and
distil-whisper-large-v3-en map to fish-audio/transcribe-1. Anything
outside the catalog returns a 400.
Text to speech
response_format explicitly — it
defaults to pcm on this endpoint, not mp3. voice is a Fish voice ID, not
a vendor preset — see
Voices.
The accepted formats are mp3, pcm, pcm16, wav, and opus — the same
set as the rest of /openai/v1; the flac, mulaw, and ogg
the SDK types also offer are formats Fish cannot produce and return a 400.
sample_rate (Hz) is accepted as an extension field on the request body
(untyped — extra_body in Python, an extra property in Node; defaults to
44100); asking for a rate the codec doesn’t take is a 400, and the per-codec
rate table is in
Audio formats.
Groq SDK type quirks: casting pcm, pcm16, opus — and srt, vtt
Groq SDK type quirks: casting pcm, pcm16, opus — and srt, vtt
The SDK’s The transcription side types
response_format types omit pcm, pcm16, and opus, and the
same Literal narrows the Python SDK, so type checkers flag them there
too. Cast at the call site:response_format as
json | text | verbose_json, so srt/vtt need the same cast — and the
SDK hands the subtitle document back as a plain string even though the
method is typed to return Transcription.Transcription
verbose_json, word-level timestamp granularity, and the subtitle formats
(srt, vtt) work as they do on the OpenAI protocol — details and placeholder-field
caveats in Migrate from OpenAI;
srt/vtt need the SDK type cast shown above.
Translation has no counterpart: Fish has no translation model, so
client.audio.translations returns a 404. Transcribe in the source language and
translate the text downstream.
Model catalog
id and created are populated,
but the Groq SDK’s other declared Model fields (object, owned_by,
active, context_window) come back undefined, and the richer Fish fields
(pricing, architecture, supported_parameters) are present but untyped.
Listing only: there is no per-model route, so models.retrieve(...) returns a
404.
Error handling
Errors map to the Groq SDK’s typed exceptions (AuthenticationError,
RateLimitError, …) with the same envelope as the OpenAI protocol: the HTTP
status as an integer error.code on every error. Failures from the Fish Audio
API keep their original status with type: "provider_error" and add
metadata.provider_name: "fish-audio"; errors from the compatibility layer
itself carry no metadata.
Going further
Migrate from OpenAI
Everything behind
/openai/v1: chat-modality audio, Realtime
WebSocket, error mapping.Compatibility
The full contract: mappings, limits, and explicit refusals.

