serverURL takes the /api/v1 prefix:
pip install openrouter,
then pass server_url:
Text to speech
ReadableStream: audio bytes arrive as they are synthesized.
The optional voice field takes a voice ID from your Fish voice library
(omitted or empty = model default — see
Voices).
input_references — OpenRouter’s first-class field for stateless voice
cloning, reference audio plus its transcript — is supported: no saved voice
model required. Both SDKs carry the field on their SpeechRequest type
(inputReferences in TypeScript, input_references in Python).
temperature, top_p, repetition_penalty
These three sit at the top level of the speech request body, next to
model and input — no provider.options wrapper required — matching what
the catalog advertises in each model’s supported_parameters.
fetch, or through the OpenAI SDK’s
extra_body.
The same three are also reachable through provider.options.fish-audio and the
X-Fish-* headers, and if you set a value two ways the more specific one wins:
X-Fish-* header beats provider.options, and provider.options beats the
top level. Everything else in the Fish parameter set — latency,
chunk_length, normalize, prosody, references — is available only through
those two channels, not at the top level. See
Fish-native parameters.
Transcription
input_audio also work, matching OpenRouter’s schema.
For word-level timing, ask for response_format="verbose_json" together with
timestamp_granularities=["word"].
Model catalog
Model schema: every required field
is present, and the ones with no Fish counterpart are explicitly null rather
than missing — SDK model validation passes and model.context_length reads
as None/null instead of raising.
Four fields are always null: context_length, default_parameters,
per_request_limits, and supported_voices. Six optional ones —
alias_target, benchmarks, expiration_date, hugging_face_id,
knowledge_cutoff, and reasoning — are simply absent; don’t write code that
expects the key to exist. Filter with output_modalities=speech or text to
list one direction only.
Error handling
Errors use the OpenRouter envelope, with the HTTP status as an integer inerror.code. Failures from the Fish Audio API surface as
type: "provider_error" with their original status and
metadata.provider_name: "fish-audio"; errors from the compatibility layer
itself carry no metadata — just code, message, and type.
Going further
Compatibility
The full contract: mappings, limits, and explicit refusals.
Fish-native parameters
Reach
latency, chunk_length, normalize, and more via
provider.options or X-Fish-* headers.
