Home/API Reference

API Reference

Complete reference for the SayIt API • Base URL: https://api.sayitapi.com • Version: v1

Overview

The SayIt API converts text to speech and returns a signed audio URL. There are no streaming endpoints, no WebSocket connections, and no SDKs required - just a single JSON endpoint.

Requests are authenticated using an SAYIT-API-KEY header. Get your API key from your dashboard.

Authentication

All requests must include your API key in the SAYIT-API-KEY header. API keys are prefixed with sk_live_ for production and sk_test_ for sandbox testing.

Never expose your API key in client-side code. Always make requests from your server.
http - request header
SAYIT-API-KEY: sk_live_your_api_key_here

Endpoint

POSThttps://api.sayitapi.com/v1/tts

This is the only endpoint in v1. Submit text and configuration parameters, receive a JSON response with your audio URL.

Parameters

Send a JSON body with the following fields:

ParameterTypeRequiredDescription
textstringYesText to synthesize. Max length depends on your plan (1,000–10,000 chars).
voice_idstringNoVoice: eve, ara, rex, sal, or leo. Default: eve.
languagestringNoBCP-47 language code (e.g. "en", "es", "fr") or "auto" for auto-detect. Default: auto.
output_formatstringNo"mp3" or "wav". Default: mp3.
speech_tagsbooleanNoEnable SSML-like speech tag parsing. Default: false.
speednumberNoSpeaking speed multiplier. Range: 0.5–2.0. Default: 1.0.
sample_rateintegerNoAudio sample rate in Hz: 8000, 16000, 22050, or 44100. Default: 22050.

Response fields

FieldTypeDescription
idstringUnique request ID (tts_01h...).
statusstring"completed" on success.
audio_urlstringURL to the generated audio file, valid for 24 hours. Serve directly or download.
duration_secondsnumberLength of the generated audio in seconds.
characters_usedintegerBillable characters consumed by this request.
voice_idstringVoice used for this request.
created_atstringISO 8601 timestamp of when the request was processed.

Speech Tags

Speech tags are always active - embed them directly in your text for fine-grained control over pronunciation and pacing.

TagExampleEffect
<emphasis>This is <emphasis>very</emphasis> important.Adds vocal stress to the wrapped text.
<whisper><whisper>don't tell anyone</whisper>Speaks the wrapped text in a whispered tone.
<soft>She said <soft>quietly</soft>.Speaks the wrapped text more softly.
[pause]Wait. [pause] Then continue.Inserts a brief natural pause at that point.
[laugh]That's funny! [laugh]Inserts a natural-sounding laugh.

Code Examples

cURL

bash
curl -X POST https://api.sayitapi.com/v1/tts \
  -H "SAYIT-API-KEY: sk_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Hello! This is <emphasis>SayIt API</emphasis>.",
    "voice_id": "eve",
    "language": "en",
    "output_format": "mp3",
    "speech_tags": true
  }'

Python

python
import requests

response = requests.post(
    "https://api.sayitapi.com/v1/tts",
    headers={
        "SAYIT-API-KEY": "sk_live_your_api_key_here",
        "Content-Type": "application/json",
    },
    json={
        "text": "Hello! This is SayIt API.",
        "voice_id": "eve",
        "language": "en",
        "output_format": "mp3",
        "speech_tags": False,
    },
    timeout=15,
)
response.raise_for_status()
data = response.json()

print(data["audio_url"])           # Stream or download this URL
print(data["duration_seconds"])    # e.g. 2.8
print(data["characters_used"])     # billed characters

JavaScript / TypeScript

typescript
interface TtsResponse {
  id: string;
  status: string;
  audio_url: string;
  duration_seconds: number;
  characters_used: number;
  voice_id: string;
  created_at: string;
}

async function synthesize(text: string): Promise<TtsResponse> {
  const res = await fetch("https://api.sayitapi.com/v1/tts", {
    method: "POST",
    headers: {
      "SAYIT-API-KEY": process.env.SAYIT_API_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      text,
      voice_id: "eve",
      language: "en",
      output_format: "mp3",
    }),
  });

  if (!res.ok) {
    const error = await res.json();
    throw new Error(`SayIt error: ${error.error.code} - ${error.error.message}`);
  }

  return res.json();
}

Rate Limits

Rate limits apply per API key. When exceeded, requests return a 429 status with a Retry-After header.

PlanReq / minConcurrentMax text (chars)
Free1011,000
Paid2001010,000

Error Codes

Errors return a JSON body with an error object containing code, message, and optionally param.

json - error response
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "You have exceeded 200 requests per minute. Retry after 18 seconds.",
    "retry_after": 18
  }
}
CodeHTTPDescription
invalid_api_key401API key is missing, malformed, or revoked.
rate_limit_exceeded429Requests per minute limit exceeded. Check Retry-After header.
text_too_long400Text exceeds the maximum character limit for your plan.
invalid_voice400voice_id is not recognized. Use: eve, ara, rex, sal, or leo.
invalid_language400language code is not valid BCP-47 or unsupported.
quota_exceeded402Monthly character quota exhausted. Upgrade or wait for reset.
invalid_request400Malformed JSON, missing required fields, or invalid parameter value.
server_error500Internal server error. Our team is automatically alerted.