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
| Code | Name | Description |
|---|---|---|
400 | Bad Request | Invalid request parameters or malformed JSON |
401 | Unauthorized | Missing or invalid API key |
402 | Payment Required | Quota exceeded |
403 | Forbidden | Missing scope, feature unavailable, or model tier restricted |
404 | Not Found | Resource not found |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Server-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-Afterperiod - 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:
| Field | Description |
|---|---|
error | Human-readable error message |
*_remaining | How many units remain in this bucket |
*_needed | How many units this request requires |
quota_multiplier | Model tier multiplier applied |
credits_estimated | Estimated credits for the operation |
Always check your quota before making requests using the /quota endpoint to avoid 402 errors.
Best Practices
- Always handle errors gracefully - Don't let unhandled errors crash your application
- Implement retries with backoff - For 429 and 5xx errors
- Log error responses - Include the full response for debugging
- Monitor quota usage - Check
/quotaproactively to avoid 402 errors - Use appropriate scopes - Only request scopes your application needs