Skip to main content
Configure the client once. The Groq SDK speaks the OpenAI protocol and appends /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

Audio streams as it is synthesized. Set 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.
The SDK’s 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:
The transcription side types 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

Entries follow the OpenRouter model schema: 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.