NovaKitv1.0

Error Codes

Understanding API error responses and handling

Error Codes

All API errors return a consistent JSON format with an error field describing what went wrong. This guide covers all error codes and how to handle them.

Error Response Format

{
  "error": "Error message describing what went wrong"
}

Some errors include additional context:

{
  "error": "Image generation limit exceeded",
  "generations_remaining": 0,
  "generations_needed": 1,
  "quota_multiplier": 1.5,
  "credits_estimated": 15
}

HTTP Status Codes

CodeNameDescription
400Bad RequestInvalid request parameters or malformed JSON
401UnauthorizedMissing or invalid API key
402Payment RequiredQuota exceeded
403ForbiddenMissing scope, feature unavailable, or model tier restricted
404Not FoundResource not found
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error

Error Details

400 Bad Request

Returned when your request is malformed or missing required parameters.

Examples:

{"error": "prompt is required"}
{"error": "input text cannot be empty"}
{"error": "speed must be between 0.25 and 4.0"}
{"error": "Invalid operation"}
{"error": "mask is required for inpainting"}

Solutions:

  • Check that all required parameters are included
  • Verify parameter types match the expected format
  • Ensure JSON is valid
  • Check parameter value ranges

401 Unauthorized

Returned when authentication fails.

{"error": "Invalid API key"}
{"error": "API key not found"}
{"error": "API key has been revoked"}

Solutions:

  • Verify your API key is correct
  • Check that the key hasn't been revoked in the dashboard
  • Ensure the Authorization header format is: Bearer sk_xxx

402 Payment Required

Returned when you've exceeded a quota bucket.

{
  "error": "Image generation limit exceeded",
  "generations_remaining": 0,
  "generations_needed": 1,
  "quota_multiplier": 1.5
}

Other examples:

{"error": "Credits limit exceeded"}
{"error": "TTS character limit exceeded", "chars_remaining": 0, "chars_needed": 500}
{"error": "STT limit exceeded", "seconds_remaining": 0, "seconds_needed": 60}
{"error": "Video seconds limit exceeded"}
{"error": "Agent run quota exceeded"}

Solutions:

  • Upgrade your plan for higher limits
  • Wait for your quota to reset (monthly)
  • Purchase a top-up bundle

403 Forbidden

Returned when your API key lacks permissions.

{"error": "Missing required scope: image"}
{"error": "Model tier not allowed"}
{"error": "AI Agents are not available on your current plan"}

Solutions:

  • Create a new API key with the required scopes
  • Upgrade to a plan that includes the feature
  • Use a model from an allowed tier

404 Not Found

Returned when a resource doesn't exist.

{"error": "Agent not found"}
{"error": "Job not found"}
{"error": "Webhook not found"}

Solutions:

  • Verify the resource ID is correct
  • Check if the resource was deleted
  • Ensure you have access to the resource

429 Too Many Requests

Returned when you've exceeded the rate limit.

{
  "error": "Rate limit exceeded. Try again in 30 seconds.",
  "retry_after": 30
}

Solutions:

  • Wait for the specified Retry-After period
  • Implement exponential backoff
  • Request a higher rate limit for enterprise needs

500 Internal Server Error

Returned when something goes wrong on our end.

{"error": "Internal server error"}
{"error": "Generation failed"}

Solutions:

  • Retry the request after a short delay
  • Check our status page for outages
  • Contact support if the issue persists

Handling Errors

import requests
import time

class NovaKitError(Exception):
    def __init__(self, message, status_code, data=None):
        self.message = message
        self.status_code = status_code
        self.data = data or {}

class AuthenticationError(NovaKitError):
    pass

class QuotaExceededError(NovaKitError):
    pass

class RateLimitError(NovaKitError):
    def __init__(self, message, status_code, retry_after, data=None):
        super().__init__(message, status_code, data)
        self.retry_after = retry_after

def make_api_request(endpoint, data, max_retries=3):
    for attempt in range(max_retries):
        response = requests.post(
            f"https://www.novakit.ai/api/v1/{endpoint}",
            headers={"Authorization": "Bearer sk_your_key"},
            json=data
        )

        if response.status_code == 200:
            return response.json()

        error_data = response.json()
        error_msg = error_data.get("error", "Unknown error")

        if response.status_code == 401:
            raise AuthenticationError(error_msg, 401, error_data)

        elif response.status_code == 402:
            raise QuotaExceededError(error_msg, 402, error_data)

        elif response.status_code == 429:
            retry_after = error_data.get("retry_after", 60)
            if attempt < max_retries - 1:
                time.sleep(retry_after)
                continue
            raise RateLimitError(error_msg, 429, retry_after, error_data)

        elif response.status_code >= 500:
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)  # Exponential backoff
                continue
            raise NovaKitError(error_msg, response.status_code, error_data)

        else:
            raise NovaKitError(error_msg, response.status_code, error_data)

    raise NovaKitError("Max retries exceeded", 500)
class NovaKitError extends Error {
  constructor(
    message: string,
    public statusCode: number,
    public data?: Record<string, unknown>
  ) {
    super(message);
    this.name = "NovaKitError";
  }
}

class AuthenticationError extends NovaKitError {
  constructor(message: string, data?: Record<string, unknown>) {
    super(message, 401, data);
    this.name = "AuthenticationError";
  }
}

class QuotaExceededError extends NovaKitError {
  constructor(message: string, data?: Record<string, unknown>) {
    super(message, 402, data);
    this.name = "QuotaExceededError";
  }
}

class RateLimitError extends NovaKitError {
  constructor(
    message: string,
    public retryAfter: number,
    data?: Record<string, unknown>
  ) {
    super(message, 429, data);
    this.name = "RateLimitError";
  }
}

async function makeApiRequest(
  endpoint: string,
  data: object,
  maxRetries = 3
): Promise<unknown> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(
      `https://www.novakit.ai/api/v1/${endpoint}`,
      {
        method: "POST",
        headers: {
          "Authorization": "Bearer sk_your_key",
          "Content-Type": "application/json",
        },
        body: JSON.stringify(data),
      }
    );

    if (response.ok) {
      return response.json();
    }

    const errorData = await response.json();
    const errorMsg = errorData.error || "Unknown error";

    switch (response.status) {
      case 401:
        throw new AuthenticationError(errorMsg, errorData);
      case 402:
        throw new QuotaExceededError(errorMsg, errorData);
      case 429:
        const retryAfter = errorData.retry_after || 60;
        if (attempt < maxRetries - 1) {
          await sleep(retryAfter * 1000);
          continue;
        }
        throw new RateLimitError(errorMsg, retryAfter, errorData);
      default:
        if (response.status >= 500 && attempt < maxRetries - 1) {
          await sleep(Math.pow(2, attempt) * 1000);
          continue;
        }
        throw new NovaKitError(errorMsg, response.status, errorData);
    }
  }
  throw new NovaKitError("Max retries exceeded", 500);
}

const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
package novakit

import (
    "encoding/json"
    "fmt"
    "net/http"
    "time"
)

type APIError struct {
    Message    string
    StatusCode int
    Data       map[string]interface{}
}

func (e *APIError) Error() string {
    return fmt.Sprintf("%s (status %d)", e.Message, e.StatusCode)
}

func MakeAPIRequest(endpoint string, data interface{}) (map[string]interface{}, error) {
    maxRetries := 3

    for attempt := 0; attempt < maxRetries; attempt++ {
        resp, err := doRequest(endpoint, data)
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()

        var result map[string]interface{}
        json.NewDecoder(resp.Body).Decode(&result)

        if resp.StatusCode == 200 {
            return result, nil
        }

        errorMsg, _ := result["error"].(string)
        if errorMsg == "" {
            errorMsg = "Unknown error"
        }

        switch resp.StatusCode {
        case 401, 402, 403, 404:
            return nil, &APIError{errorMsg, resp.StatusCode, result}
        case 429:
            retryAfter := 60
            if ra, ok := result["retry_after"].(float64); ok {
                retryAfter = int(ra)
            }
            if attempt < maxRetries-1 {
                time.Sleep(time.Duration(retryAfter) * time.Second)
                continue
            }
            return nil, &APIError{errorMsg, 429, result}
        default:
            if resp.StatusCode >= 500 && attempt < maxRetries-1 {
                time.Sleep(time.Duration(1<<attempt) * time.Second)
                continue
            }
            return nil, &APIError{errorMsg, resp.StatusCode, result}
        }
    }
    return nil, &APIError{"Max retries exceeded", 500, nil}
}

Quota Error Details

When you receive a 402 error, the response includes helpful information:

FieldDescription
errorHuman-readable error message
*_remainingHow many units remain in this bucket
*_neededHow many units this request requires
quota_multiplierModel tier multiplier applied
credits_estimatedEstimated credits for the operation

Always check your quota before making requests using the /quota endpoint to avoid 402 errors.

Best Practices

  1. Always handle errors gracefully - Don't let unhandled errors crash your application
  2. Implement retries with backoff - For 429 and 5xx errors
  3. Log error responses - Include the full response for debugging
  4. Monitor quota usage - Check /quota proactively to avoid 402 errors
  5. Use appropriate scopes - Only request scopes your application needs

On this page