REST API
/api/v1/audio/speech
Text to speech. Send text, get the audio file back in the response body. The request follows OpenAI's audio.speech.create, so the OpenAI SDKs work with the Railwail base URL.
- URL
- https://railwail.com/api/v1/audio/speech
- Key scope
- audio
- Returns
- audio bytes (mp3 default)
- Billing
- per 1,000 characters or per run
Request body
JSON. Unknown fields are rejected with 400 validation_failed.
modelrequiredinputrequiredvoicevoice_idresponse_formatmp3speedlanguagestability / similarity_boostvoice_sampleconsentuserOpenAI vs. other speech models
Voice cloning
Models whose page shows a reference-voice input (chatterbox, qwen3-tts, openvoice-v2) speak the text in the voice of a short clip. Only clone your own voice or a voice you have permission to use; see the terms of service. Two steps:
1. Upload the clip: POST /api/v1/audio/uploads, multipart field file, key scope audio. MP3, WAV, WebM or Ogg, at most 10 MB and 60 seconds, 20 uploads per hour. The answer holds the clip's url; the file is deleted after 24 hours.
2. Pass that url as voice_sample together with consent: true. Without consent the request is refused with 400 voice_consent_required and nothing is charged. The job stores the url and the consent, never the audio.
# 1. upload the reference clip
curl https://railwail.com/api/v1/audio/uploads \
-H "Authorization: Bearer $RAILWAIL_API_KEY" \
-F "[email protected]"
# -> { "object": "audio.upload", "url": "https://.../audio-uploads/...wav", "duration_sec": 12.4, ... }
# 2. speak with that voice
curl https://railwail.com/api/v1/audio/speech \
-H "Authorization: Bearer $RAILWAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "xtts-v2",
"input": "Hello, this is my cloned voice.",
"voice_sample": "<url from step 1>",
"consent": true
}' \
--output cloned.wavExamples
Key in RAILWAIL_API_KEY, with the audio scope. Each example writes speech.mp3.
curl https://railwail.com/api/v1/audio/speech \
-H "Authorization: Bearer $RAILWAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai-tts-1",
"input": "Hello from Railwail. This sentence was spoken by a text-to-speech model.",
"voice": "alloy"
}' \
--output speech.mp3import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["RAILWAIL_API_KEY"],
base_url="https://railwail.com/api/v1",
)
with client.audio.speech.with_streaming_response.create(
model="openai-tts-1",
voice="alloy",
input="Hello from Railwail. This sentence was spoken by a text-to-speech model.",
) as response:
response.stream_to_file("speech.mp3")// ESM: save as speech.mjs
import fs from "node:fs/promises";
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.RAILWAIL_API_KEY,
baseURL: "https://railwail.com/api/v1",
});
const speech = await client.audio.speech.create({
model: "openai-tts-1",
voice: "alloy",
input: "Hello from Railwail. This sentence was spoken by a text-to-speech model.",
});
await fs.writeFile("speech.mp3", Buffer.from(await speech.arrayBuffer()));Response
HTTP 200 with the audio as the body; Content-Type matches response_format (for example audio/mpeg for mp3). Errors come back as JSON like every other endpoint. The railwail npm SDK has no speech method; use the OpenAI SDK or plain HTTP.
Models you can call here
- OpenAI TTS-1
openai-tts-1USD 0.018/1.000 caracteres - OpenAI TTS-1 HD
openai-tts-1-hdUSD 0.036/1.000 caracteres
Errors
| Status | Code | What to do |
|---|---|---|
| 400 | validation_failed | A field is unknown or out of range. |
| 400 | voice_consent_required | voice_sample without consent: true. |
| 400 | invalid_voice_sample | voice_sample is not the url of your own upload from /api/v1/audio/uploads. |
| 400 | voice_cloning_unsupported | The model has no reference-voice input (message: model does not support voice cloning). |
| 400 | voice_sample_required | The model only speaks in a cloned voice; send voice_sample and consent. |
| 402 | insufficient_credits | Top up on the billing page. |
| 402 | monthly_limit_exceeded | Your monthly spending limit is reached. |
| 403 | insufficient_scope | The key lacks the audio scope. |
| 404 | model_not_found | Check the slug in the list above. |
| 429 | trial_limit | Trial rules (at most 2 credits per run until the first top-up). |
| 503 | model_unavailable | No verified price or the provider is not reachable; pick another model. |
All codes: Error codes.