User Guide API Reference

Errors

Understanding API error responses and how to handle them

Error Response Format

When an error occurs, 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)
        }
    }
}

HTTP Status Codes

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 request)
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Something went wrong on our end

Error Codes

INVALID_REQUEST

400 Bad Request

The request parameters are invalid or missing required fields.

{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Invalid request parameters",
    "details": {
      "text": ["This field is required."],
      "voice_id": ["Invalid voice ID."]
    }
  }
}

Common causes:

  • Missing required fields (text, voice_id)
  • Invalid field types (e.g., string instead of number)
  • Text exceeds maximum character limit
  • Invalid voice ID format

UNAUTHORIZED

401 Unauthorized

Authentication failed due to missing or invalid API key.

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

Common causes:

  • Missing Authorization header
  • Invalid API key format
  • API key has been revoked or deleted
  • API key has expired

INSUFFICIENT_CREDITS

402 Payment Required

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

{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "You do not have enough credits to generate speech.",
    "details": {
      "credits_remaining": 50,
      "credits_required": 150
    }
  }
}

Response Headers:

Header Description
Moknah-Credits-Remaining Credits left in your account
Moknah-Credits-Used Credits required for this request

Solution: Purchase more credits or reduce the request size (shorter text).


FORBIDDEN

403 Forbidden

Your API key doesn't have permission to access this resource.

{
  "error": {
    "code": "FORBIDDEN",
    "message": "You don't have permission to access this voice"
  }
}

Common causes:

  • Trying to use a voice you don't have access to
  • API key permissions are restricted
  • Accessing a feature not included in your plan

NOT_FOUND

404 Not Found

The requested resource doesn't exist.

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Voice not found",
    "details": {
      "voice_id": "abc123"
    }
  }
}

Common causes:

  • Invalid voice ID
  • Resource has been deleted
  • Typo in the endpoint URL

CONCURRENT_REQUEST

409 Conflict

Another request from your account is already being processed. Moknah processes one request per user at a time.

{
  "error": {
    "code": "CONCURRENT_REQUEST",
    "message": "Another request is already in progress. Please wait for it to complete."
  }
}

Solution: Wait for the current request to complete before sending another. Implement a queue system on your end if you need to process multiple requests.


RATE_LIMIT_EXCEEDED

429 Too Many Requests

You've exceeded the rate limit for API requests.

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Contact sales@moknah.io to increase limits.",
    "details": {
      "credits_remaining": 0,
      "reset_after": 45
    }
  }
}

Response Headers:

Header Description
Retry-After Seconds until you can retry
RateLimit-Reset Unix timestamp when the rate limit resets

Solution: Wait for the reset period, then retry. Implement exponential backoff in your client. See Rate Limits for more details.


INTERNAL_ERROR

500 Internal Server Error

An unexpected error occurred on our servers.

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred. Please try again later."
  }
}

Solution: Retry the request after a short delay. If the problem persists, contact support@moknah.io.


Handling Errors

Here's an example of how to handle errors in your 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}",
        "Content-Type": "application/json"
    }
    payload = {
        "text": text,
        "voice_id": voice_id
    }
    
    response = requests.post(url, json=payload, headers=headers)
    
    if response.status_code == 200:
        return response.content  # Audio bytes
    
    elif response.status_code == 400:
        error = response.json()["error"]
        raise ValueError(f"Invalid request: {error['details']}")
    
    elif response.status_code == 401:
        raise PermissionError("Invalid API key")
    
    elif response.status_code == 402:
        error = response.json()["error"]
        raise Exception(f"Insufficient credits: {error['details']['credits_remaining']} remaining")
    
    elif response.status_code == 429:
        retry_after = int(response.headers.get("Retry-After", 60))
        print(f"Rate limited. Retrying in {retry_after} seconds...")
        time.sleep(retry_after)
        return generate_speech(text, voice_id, api_key)  # Retry
    
    elif response.status_code == 409:
        print("Concurrent request in progress. Waiting...")
        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 url = "https://moknah.io/api/v1/tts/generate";
  
  const response = await fetch(url, {
    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(); // Audio blob
  }
  
  const error = await response.json();
  
  switch (response.status) {
    case 400:
      throw new Error(`Invalid request: ${JSON.stringify(error.error.details)}`);
    
    case 401:
      throw new Error("Invalid API key");
    
    case 402:
      throw new Error(`Insufficient credits: ${error.error.details.credits_remaining} remaining`);
    
    case 429:
      const retryAfter = response.headers.get("Retry-After") || 60;
      console.log(`Rate limited.Retrying in ${retryAfter}s...`);
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      return generateSpeech(text, voiceId, apiKey); // Retry
    
    case 409:
      console.log("Concurrent request in progress. Waiting...");
      await new Promise(r => setTimeout(r, 5000));
      return generateSpeech(text, voiceId, apiKey); // Retry
    
    default:
      throw new Error(`API error: ${response.status}`);
  }
}
# Check response status and handle errors
response=$(curl -s -w "\n%{http_code}" \
  -X POST "https://moknah.io/api/v1/tts/generate" \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello world", "voice_id": "voice_123"}')

# Extract status code (last line)
http_code=$(echo "$response" | tail -n1)

# Extract body (all but last line)
body=$(echo "$response" | sed '$d')

case $http_code in
  200)
    echo "Success!"
    ;;
  400)
    echo "Invalid request: $body"
    ;;
  401)
    echo "Invalid API key"
    ;;
  402)
    echo "Insufficient credits"
    ;;
  429)
    echo "Rate limited. Check Retry-After header"
    ;;
  409)
    echo "Concurrent request in progress"
    ;;
  *)
    echo "Error: $http_code - $body"
    ;;
esac
Best Practice: Exponential Backoff

For 429 and 5xx errors, implement exponential backoff: wait 1s, then 2s, then 4s, etc. This prevents overwhelming the API during outages.

API Support

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