> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nouvel.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Error codes, response formats, and handling strategies

## Error Response Format

All API errors follow a consistent JSON response format:

<CodeGroup>
  ```json Standard Error Response theme={null}
  {
    "error": "Error message",
    "reason": "Detailed explanation of what went wrong",
    "usage": {
      "used": 145,
      "limit": 100
    }
  }
  ```
</CodeGroup>

<ResponseField name="error" type="string" required>
  Short error message describing what went wrong
</ResponseField>

<ResponseField name="reason" type="string">
  Human-readable explanation with additional context (optional)
</ResponseField>

<ResponseField name="usage" type="object">
  Usage information when error is quota-related (optional)

  <ResponseField name="used" type="number">
    Number of videos generated this billing period
  </ResponseField>

  <ResponseField name="limit" type="number">
    Maximum videos allowed for your plan
  </ResponseField>
</ResponseField>

## HTTP Status Codes

### 400 Bad Request

The request was malformed or contained invalid parameters.

**Common causes:**

* Invalid JSON in request body
* Missing required fields (`url`, `variantCount`, etc.)
* Invalid URL format (not a valid HTTP/HTTPS URL)
* Invalid `variantCount` (must be 1-3)
* `urls` not an array of 1-3 valid URLs

<CodeGroup>
  ```json Missing/Invalid URLs theme={null}
  {
    "error": "urls must be an array of 1-3 URLs"
  }
  ```

  ```json Invalid URL Format theme={null}
  {
    "error": "Invalid URL: https://not a valid url"
  }
  ```

  ```json Invalid Variant Count theme={null}
  {
    "error": "variantCount must be 1-3"
  }
  ```
</CodeGroup>

<Warning>
  Always validate your request parameters before sending to avoid 400 errors. These errors indicate a problem with your code, not the API.
</Warning>

### 401 Unauthorized

Authentication failed. Your API key is missing, invalid, or malformed.

**Common causes:**

* Missing `Authorization` header
* Invalid API key format (must start with `nvl_`)
* API key has been revoked
* API key does not exist

<CodeGroup>
  ```json Invalid or Missing Key theme={null}
  {
    "error": "Unable to resolve user from API key"
  }
  ```
</CodeGroup>

<Tip>
  Always use the `Bearer` authentication scheme:

  ```
  Authorization: Bearer nvl_your_api_key_here
  ```
</Tip>

### 402 Payment Required

You have exceeded your plan's quota or attempted to use a feature not available on your plan.

**Common causes:**

* Generated more videos than your plan allows this billing period
* Trial period has ended
* Plan does not include API access

<CodeGroup>
  ```json Quota Exceeded theme={null}
  {
    "error": "quota_exceeded",
    "reason": "You've used all 30 videos for this billing period. Add credits ($8/video) or upgrade your plan.",
    "usage": {
      "used": 30,
      "limit": 30
    }
  }
  ```

  ```json Wrong Plan theme={null}
  {
    "error": "API access requires a Scale or Business plan"
  }
  ```
</CodeGroup>

<Note>
  The `usage` object is included when the error is quota-related, showing your current usage and plan limit.
</Note>

### 403 Forbidden

Your API key lacks the required permission to perform this action.

**Common causes:**

* API key does not have `generate` permission (for POST /api/v1/generate)
* API key does not have `projects:read` permission (for GET endpoints)
* Attempting to access another user's resources

<CodeGroup>
  ```json Missing Permission theme={null}
  {
    "error": "This API key does not have the 'generate' permission"
  }
  ```
</CodeGroup>

<Warning>
  API key permissions cannot be modified after creation. If you need different permissions, revoke the key and create a new one with the correct permissions.
</Warning>

See the [Permissions](/api-reference/permissions) page for details on permission scopes.

### 404 Not Found

The requested resource does not exist.

**Common causes:**

* Job ID does not exist
* Project ID does not exist
* Typo in the endpoint URL

<CodeGroup>
  ```json Job Not Found theme={null}
  {
    "error": "Not found",
    "reason": "Job not found"
  }
  ```

  ```json Project Not Found theme={null}
  {
    "error": "Not found",
    "reason": "Project not found"
  }
  ```
</CodeGroup>

<Tip>
  Job IDs are UUIDs (e.g., `550e8400-e29b-41d4-a716-446655440000`). Double-check that you're using the correct ID format from the generation response.
</Tip>

### 422 Unprocessable Entity

The request was well-formed, but the server cannot process it due to semantic errors.

**Common causes:**

* URL is not a product page (detected as homepage, category page, blog post, etc.)
* URL is a valid product page but cannot be processed (unsupported e-commerce platform)

<CodeGroup>
  ```json Not a Product Page theme={null}
  {
    "error": "not_a_product",
    "reason": "example.com is not a product page. Paste a link to a product you want to advertise."
  }
  ```
</CodeGroup>

<Warning>
  The special error code `not_a_product` is always returned with a 422 status. This indicates the URL was successfully accessed but determined to be a non-product page.
</Warning>

### 429 Too Many Requests

You have exceeded the rate limit of 60 requests per minute.

<CodeGroup>
  ```json Rate Limit Exceeded theme={null}
  {
    "error": "Rate limit exceeded",
    "reason": "You have made too many requests. Please wait before trying again.",
    "retryAfter": 23
  }
  ```
</CodeGroup>

<ResponseField name="retryAfter" type="number">
  Number of seconds to wait before retrying (optional)
</ResponseField>

The response also includes a `Retry-After` header with the same value.

See the [Rate Limits](/api-reference/rate-limits) page for detailed guidance on handling rate limits.

### 500 Internal Server Error

An unexpected error occurred on the server.

**Common causes:**

* Temporary service outage
* Database connection issue
* Unexpected data format from upstream service

<CodeGroup>
  ```json Internal Error theme={null}
  {
    "error": "Internal server error",
    "reason": "An unexpected error occurred. Please try again later."
  }
  ```
</CodeGroup>

<Warning>
  500 errors are rare but can happen. Always implement retry logic with exponential backoff for 5xx errors.
</Warning>

## Error Handling Best Practices

### 1. Implement Comprehensive Error Handling

Always check both the HTTP status code and the error response body:

<CodeGroup>
  ```typescript TypeScript theme={null}
  async function generateVideo(url: string) {
    try {
      const response = await fetch('https://app.nouvel.ai/api/v1/generate', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.NOUVEL_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ urls: [url], variantCount: 3 })
      });

      const data = await response.json();

      if (!response.ok) {
        // Handle specific error cases
        switch (response.status) {
          case 400:
            throw new Error(`Invalid request: ${data.reason}`);
          case 401:
            throw new Error('Authentication failed. Check your API key.');
          case 402:
            throw new Error(`Quota exceeded: ${data.reason}. Used: ${data.usage?.used}/${data.usage?.limit}`);
          case 403:
            throw new Error(`Permission denied: ${data.reason}`);
          case 404:
            throw new Error('Resource not found');
          case 422:
            if (data.error === 'not_a_product') {
              throw new Error(`Not a product page: ${data.reason}`);
            }
            throw new Error(`Cannot process: ${data.reason}`);
          case 429:
            throw new Error(`Rate limited. Retry after ${data.retryAfter} seconds.`);
          case 500:
            throw new Error('Server error. Please try again later.');
          default:
            throw new Error(`API error: ${data.error}`);
        }
      }

      return data;
    } catch (error) {
      console.error('Video generation failed:', error);
      throw error;
    }
  }
  ```

  ```python Python theme={null}
  import requests
  import time

  def generate_video(url, max_retries=3):
      attempt = 0

      while attempt < max_retries:
          try:
              response = requests.post(
                  'https://app.nouvel.ai/api/v1/generate',
                  headers={
                      'Authorization': f'Bearer {os.getenv("NOUVEL_API_KEY")}',
                      'Content-Type': 'application/json'
                  },
                  json={'urls': [url], 'variantCount': 3}
              )

              data = response.json()

              if response.status_code == 200:
                  return data

              # Handle specific errors
              if response.status_code == 400:
                  raise ValueError(f'Invalid request: {data.get("reason")}')
              elif response.status_code == 401:
                  raise PermissionError('Authentication failed')
              elif response.status_code == 402:
                  usage = data.get('usage', {})
                  raise Exception(f'Quota exceeded: {usage.get("used")}/{usage.get("limit")}')
              elif response.status_code == 422:
                  if data.get('error') == 'not_a_product':
                      raise ValueError(f'Not a product page: {data.get("reason")}')
              elif response.status_code == 429:
                  # Rate limited - retry with backoff
                  backoff = 2 ** attempt
                  time.sleep(backoff)
                  attempt += 1
                  continue
              elif response.status_code >= 500:
                  # Server error - retry with backoff
                  backoff = 2 ** attempt
                  time.sleep(backoff)
                  attempt += 1
                  continue

              raise Exception(f'API error: {data.get("error")}')

          except requests.exceptions.RequestException as e:
              print(f'Request failed: {e}')
              attempt += 1
              if attempt >= max_retries:
                  raise
              time.sleep(2 ** attempt)

      raise Exception('Max retries exceeded')
  ```
</CodeGroup>

### 2. Retry Strategy

**Retry 5xx errors and 429 errors** with exponential backoff. **Do not retry 4xx errors** (except 429).

| Status Code | Retry? | Strategy                            |
| ----------- | ------ | ----------------------------------- |
| 400-403     | ❌ No   | Client error - fix your request     |
| 404         | ❌ No   | Resource doesn't exist              |
| 422         | ❌ No   | Semantic error - fix your input     |
| 429         | ✅ Yes  | Exponential backoff (1s, 2s, 4s...) |
| 500-599     | ✅ Yes  | Exponential backoff (1s, 2s, 4s...) |

### 3. Log Errors for Debugging

Always log the full error response for debugging:

<CodeGroup>
  ```typescript TypeScript theme={null}
  console.error('API Error:', {
    status: response.status,
    error: data.error,
    reason: data.reason,
    usage: data.usage,
    timestamp: new Date().toISOString()
  });
  ```

  ```python Python theme={null}
  import logging

  logging.error('API Error', extra={
      'status': response.status_code,
      'error': data.get('error'),
      'reason': data.get('reason'),
      'usage': data.get('usage'),
      'timestamp': datetime.now().isoformat()
  })
  ```
</CodeGroup>

### 4. Handle Special Error Types

#### Quota Exceeded (402)

When you receive a 402 error, show the user their current usage and guide them to upgrade:

<CodeGroup>
  ```typescript TypeScript theme={null}
  if (response.status === 402) {
    const { usage } = data;
    console.log(`Quota exceeded: ${usage.used}/${usage.limit} videos used`);
    console.log('Upgrade your plan at: https://app.nouvel.ai/settings/billing');
  }
  ```
</CodeGroup>

#### Not a Product (422)

When you receive `not_a_product`, help the user understand what went wrong:

<CodeGroup>
  ```typescript TypeScript theme={null}
  if (data.error === 'not_a_product') {
    console.error('The URL provided is not a product page.');
    console.error('Please ensure you are providing a direct link to a product detail page,');
    console.error('not a homepage, category page, or blog post.');
  }
  ```
</CodeGroup>

## Contact Support

If you encounter persistent errors or need help debugging:

* Review the [API documentation](https://docs.nouvel.ai) for correct endpoint usage
* Contact support at [support@nouvel.ai](mailto:support@nouvel.ai)

<Tip>
  Include the error response, timestamp, and request ID (if available) when contacting support for faster resolution.
</Tip>
