User Guide API Reference

Errors

Understanding API error responses, WebSocket close codes, and how to handle them

REST API Error Format (HTTP)

When an error occurs on a standard HTTP endpoint, the API returns a JSON response with the following structure:

{
    "success": false,
    "error": {
        "code": "ERROR_CODE",
        "message": "Human-readable error description",
        "details": {
          // Additional context (varies by error type)
        }
    }
}

WebSocket Error Format (WSS)

WebSockets handle errors differently. Errors can occur during the initial connection handshake, or mid-stream while audio is generating.

  • Fatal Errors: The connection is dropped with a specific Close Code (e.g., 4290).
  • Mid-Stream Errors: The server sends a flat JSON error frame before closing: {"error": "Insufficient credits during stream"}

Status Codes & Close Codes

HTTP Status Codes (REST)

Status Meaning Description
200 OK Request succeeded
400 Bad Request Invalid request parameters or malformed request
401 Unauthorized Missing or invalid API key
402 Payment Required Not enough credits to complete the request
403 Forbidden API key doesn't have permission for this action
404 Not Found Requested resource doesn't exist
409 Conflict Request conflicts with current state (e.g., concurrent HTTP request)
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Something went wrong on our end

WebSocket Close Codes (WSS)

Code Type Description
1000 Normal Closure Stream completed successfully.
3011 Generation Error Audio generation failed on the upstream API. Retry the request.
4001 Missing Auth No API key provided in the Authorization header during handshake.
4002 Auth Failed Invalid key, inactive account, insufficient initial credits, or IP restriction.
4290 Concurrent Limit You already have an active TTS WebSocket connection. Limit is 1 per user.

Common Error Scenarios

1. Unauthorized / Authentication Failed

HTTP: 401 WSS: 4001 / 4002

Authentication failed due to missing or invalid API key, or IP whitelist restrictions.

Common causes:

  • Missing Authorization: Bearer header
  • Invalid API key format or key has been revoked
  • Connecting from an IP address not in your key's whitelist

2. Insufficient Credits

HTTP: 402 WSS: Mid-stream JSON

Your account doesn't have enough credits to complete this request.

  • HTTP: Fails immediately before generating audio.
  • WebSocket: If you run out of credits mid-stream, the server sends {"error": "Insufficient credits during stream"} and cleanly closes the connection. Audio generated prior to running out of credits will still be delivered.

3. Concurrency & Rate Limits

HTTP: 409 / 429 WSS: 4290

Moknah processes one request per user at a time to ensure fair usage.

  • HTTP 409: Another request from your account is already being processed. Wait for it to finish.
  • HTTP 429: You exceeded the 60 Requests Per Minute (RPM) limit.
  • WSS 4290: You attempted to open a second WebSocket connection while your first one was still active.

4. Invalid Request / Bad Data

HTTP: 400 WSS: Mid-stream JSON

The request parameters are invalid, or you exceeded text chunk limits.

  • HTTP: Missing required fields (text, voice_id).
  • WebSocket: Sending a text chunk larger than 2,000 characters will result in {"error": "Chunk too large. Maximum 2000 characters per chunk."}.

Handling Errors in Code

import requests
import time

def generate_speech(text, voice_id, api_key):
    url = "https://moknah.io/api/v1/tts/generate"
    headers = {"Authorization": f"Bearer {api_key}"}

    response = requests.post(url, json={"text": text, "voice_id": voice_id}, headers=headers)

    if response.status_code == 200:
        return response.content  # Audio bytes
    elif response.status_code == 402:
        raise Exception("Insufficient credits")
    elif response.status_code == 429:
        retry_after = int(response.headers.get("Retry-After", 5))
        time.sleep(retry_after)
        return generate_speech(text, voice_id, api_key)  # Retry
    elif response.status_code == 409:
        time.sleep(5)
        return generate_speech(text, voice_id, api_key)  # Retry
    else:
        raise Exception(f"API error: {response.status_code}")
async function generateSpeech(text, voiceId, apiKey) {
  const response = await fetch("https://moknah.io/api/v1/tts/generate", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${apiKey}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ text: text, voice_id: voiceId })
  });

  if (response.ok) return await response.blob();

  if (response.status === 429) {
    const retryAfter = response.headers.get("Retry-After") || 5;
    await new Promise(r => setTimeout(r, retryAfter * 1000));
    return generateSpeech(text, voiceId, apiKey);
  }

  if (response.status === 409) {
    await new Promise(r => setTimeout(r, 5000));
    return generateSpeech(text, voiceId, apiKey);
  }

  const error = await response.json();
  throw new Error(error.error?.message || "API Error");
}
const ws = new WebSocket('wss://moknah.io/api/v1/api/v1/tts/ws/', {
    headers: { "Authorization": `Bearer ${apiKey}` }
});

// Handle Mid-Stream JSON Errors
ws.on('message', (data, isBinary) => {
    if (!isBinary) {
        const msg = JSON.parse(data.toString());
        if (msg.error) {
            console.error("Stream Error:", msg.error);
            // e.g., "Insufficient credits during stream"
        }
    }
});

// Handle Fatal Close Codes
ws.on('close', (code, reason) => {
    switch(code) {
        case 1000:
            console.log("Stream finished successfully.");
            break;
        case 4001:
        case 4002:
            console.error("Authentication failed. Check your API key.");
            break;
        case 4290:
            console.error("Concurrency limit reached. Close other active streams.");
            // Wait a few seconds before attempting to reconnect
            setTimeout(reconnect, 5000);
            break;
        case 3011:
            console.error("Upstream generation failed. Please try again.");
            break;
        default:
            console.error(`Disconnected with code ${code}`);
    }
});
Best Practice: Exponential Backoff

For 429 (HTTP), 409 (HTTP), and 4290 (WSS) errors, implement exponential backoff: wait 1s, then 2s, then 4s, etc. This prevents overwhelming the API and guarantees your request will succeed once the lock clears.

API Support

For API-related questions or issues, contact us at api@moknah.io.