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

# Error Handling

> Learn how to handle errors and rate limits in the Bouncy.ai API

## Error Response Format

All API errors follow a consistent JSON structure:

```json theme={null}
{
  "error": {
    "code": "invalid_request",
    "message": "The url field is required",
    "field": "url"
  }
}
```

**Fields**:

* `code` - Machine-readable error code (e.g., `invalid_request`, `rate_limit_exceeded`)
* `message` - Human-readable error description
* `field` - (Optional) The specific field that caused the error

## HTTP Status Codes

| Status Code                 | Description                                   |
| --------------------------- | --------------------------------------------- |
| `200 OK`                    | Request successful                            |
| `201 Created`               | Resource created successfully                 |
| `400 Bad Request`           | Invalid request parameters                    |
| `401 Unauthorized`          | Missing or invalid API key                    |
| `403 Forbidden`             | Insufficient permissions or quota exceeded    |
| `404 Not Found`             | Resource not found                            |
| `409 Conflict`              | Resource conflict (e.g., slug already exists) |
| `429 Too Many Requests`     | Rate limit exceeded                           |
| `500 Internal Server Error` | Server error (contact support if persists)    |

## Common Errors

### 401 Unauthorized - Missing API Key

```json theme={null}
{
  "error": {
    "code": "missing_api_key",
    "message": "API key is required. Include it in the Authorization header as a Bearer token."
  }
}
```

**Solution**: Add your API key to the `Authorization` header as a Bearer token (e.g., `Authorization: Bearer bcy_live_pk_YOUR_API_KEY`).

### 401 Unauthorized - Invalid API Key

```json theme={null}
{
  "error": {
    "code": "invalid_api_key",
    "message": "API key is invalid or has been revoked"
  }
}
```

**Solutions**:

* Verify you've copied the full API key correctly
* Check if the key has been revoked in the [API Dashboard](https://bouncy.ai/api/dashboard)
* Generate a new API key if needed

### 403 Forbidden - Plan Upgrade Required

```json theme={null}
{
  "error": {
    "code": "plan_upgrade_required",
    "message": "Your subscription plan does not include API access. Please upgrade to Growth or higher.",
    "currentPlan": "solo"
  }
}
```

**Solution**: [Upgrade to a Growth plan](https://bouncy.ai/pricing) (\$35/month) or higher.

### 403 Forbidden - Quota Exceeded

```json theme={null}
{
  "error": {
    "code": "quota_exceeded",
    "message": "You've reached your plan's link limit",
    "currentPlan": "growth",
    "currentLinks": 1000,
    "maxLinks": 1000
  }
}
```

**Solutions**:

* Delete unused links
* [Upgrade your plan](https://bouncy.ai/pricing) for higher limits

### 404 Not Found

```json theme={null}
{
  "error": {
    "code": "link_not_found",
    "message": "Link not found"
  }
}
```

**Solutions**:

* Check that the link ID is correct
* Verify the link hasn't been deleted
* Ensure you have permission to access this link

### 409 Conflict - Slug Already Exists

```json theme={null}
{
  "error": {
    "code": "slug_already_exists",
    "message": "The slug 'summer' is already in use"
  }
}
```

**Solutions**:

* Choose a different slug
* Omit the `slug` field to auto-generate a random one
* Check if you already created this link

### 429 Too Many Requests - Rate Limit Exceeded

```json theme={null}
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "limit": 1000,
    "remaining": 0,
    "resetAt": 1738886400
  }
}
```

**Solution**: Wait until the rate limit resets (see `resetAt` timestamp) or [upgrade your plan](https://bouncy.ai/pricing).

## Rate Limiting

### Rate Limit Headers

Every API response includes rate limit information in the headers:

```
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1738886400
```

* `X-RateLimit-Limit` - Your maximum requests per hour
* `X-RateLimit-Remaining` - Requests remaining in current window
* `X-RateLimit-Reset` - Unix timestamp when limit resets

### Rate Limits by Plan

| Plan      | Requests/Hour | Requests/Day |
| --------- | ------------- | ------------ |
| Growth    | 1,000         | 10,000       |
| Scaling   | 5,000         | 50,000       |
| Dominance | 10,000        | 100,000      |

### Handling Rate Limits

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function makeRequest(url, options) {
    const response = await fetch(url, options);

    // Check if rate limited
    if (response.status === 429) {
      const resetTime = response.headers.get('X-RateLimit-Reset');
      const waitMs = (resetTime * 1000) - Date.now();

      console.log(`Rate limited. Waiting ${waitMs}ms...`);
      await new Promise(resolve => setTimeout(resolve, waitMs));

      // Retry request
      return makeRequest(url, options);
    }

    return response.json();
  }
  ```

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

  def make_request(url, headers):
      response = requests.get(url, headers=headers)

      # Check if rate limited
      if response.status_code == 429:
          reset_time = int(response.headers.get('X-RateLimit-Reset'))
          wait_seconds = reset_time - int(time.time())

          print(f"Rate limited. Waiting {wait_seconds}s...")
          time.sleep(wait_seconds)

          # Retry request
          return make_request(url, headers)

      return response.json()
  ```
</CodeGroup>

### Best Practices for Rate Limiting

<AccordionGroup>
  <Accordion title="Monitor rate limit headers">
    Check `X-RateLimit-Remaining` before making requests:

    ```javascript theme={null}
    const remaining = response.headers.get('X-RateLimit-Remaining');
    if (remaining < 10) {
      console.warn('Approaching rate limit!');
    }
    ```
  </Accordion>

  <Accordion title="Implement exponential backoff">
    Retry failed requests with increasing delays:

    ```javascript theme={null}
    async function retryWithBackoff(fn, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          return await fn();
        } catch (error) {
          if (i === maxRetries - 1) throw error;
          const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
          await new Promise(resolve => setTimeout(resolve, delay));
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Batch requests when possible">
    Use bulk endpoints to reduce request count:

    * Use `POST /v1/links/bulk` instead of multiple single creates
    * Use `DELETE /v1/links/bulk` for deleting multiple links
  </Accordion>

  <Accordion title="Cache responses">
    Cache data that doesn't change frequently:

    ```javascript theme={null}
    const cache = new Map();

    async function getCachedLink(linkId) {
      if (cache.has(linkId)) {
        return cache.get(linkId);
      }

      const link = await fetchLink(linkId);
      cache.set(linkId, link);
      return link;
    }
    ```
  </Accordion>
</AccordionGroup>

## Retry Logic

Implement automatic retries for transient errors (500, 502, 503, 504):

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function fetchWithRetry(url, options, maxRetries = 3) {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const response = await fetch(url, options);

        // Don't retry client errors (4xx)
        if (response.status >= 400 && response.status < 500) {
          throw new Error(`Client error: ${response.status}`);
        }

        // Retry server errors (5xx)
        if (response.status >= 500 && attempt < maxRetries) {
          const delay = Math.pow(2, attempt) * 1000;
          console.log(`Retry ${attempt}/${maxRetries} after ${delay}ms`);
          await new Promise(resolve => setTimeout(resolve, delay));
          continue;
        }

        return response.json();
      } catch (error) {
        if (attempt === maxRetries) throw error;
      }
    }
  }
  ```

  ```python Python theme={null}
  import time
  import requests
  from requests.adapters import HTTPAdapter
  from requests.packages.urllib3.util.retry import Retry

  def create_session_with_retries():
      session = requests.Session()

      retry_strategy = Retry(
          total=3,
          status_forcelist=[429, 500, 502, 503, 504],
          backoff_factor=2  # 2s, 4s, 8s
      )

      adapter = HTTPAdapter(max_retries=retry_strategy)
      session.mount("https://", adapter)

      return session

  # Usage
  session = create_session_with_retries()
  response = session.get(
      'https://api.bouncy.ai/v1/links',
      headers={'Authorization': 'Bearer bcy_live_pk_YOUR_API_KEY'}
  )
  ```
</CodeGroup>

## Validation Errors

Field-specific validation errors include the `field` property:

```json theme={null}
{
  "error": {
    "code": "invalid_url",
    "message": "The URL must be a valid HTTP or HTTPS URL",
    "field": "url"
  }
}
```

Common validation errors:

| Error Code               | Description                         |
| ------------------------ | ----------------------------------- |
| `missing_required_field` | Required field is missing           |
| `invalid_url`            | URL is not a valid HTTP/HTTPS URL   |
| `invalid_slug`           | Slug contains invalid characters    |
| `invalid_email`          | Email address is invalid            |
| `value_too_long`         | Field value exceeds maximum length  |
| `value_too_short`        | Field value is below minimum length |

## Error Logging

Log errors for debugging:

```javascript theme={null}
try {
  const response = await fetch('https://api.bouncy.ai/v1/links', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer bcy_live_pk_YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ url: 'https://example.com' })
  });

  if (!response.ok) {
    const error = await response.json();
    console.error('API Error:', {
      status: response.status,
      code: error.error?.code,
      message: error.error?.message,
      timestamp: new Date().toISOString()
    });
    throw new Error(error.error?.message || 'API request failed');
  }

  return response.json();
} catch (error) {
  // Log to error tracking service (Sentry, etc.)
  console.error('Request failed:', error);
  throw error;
}
```

## Need Help?

If you encounter persistent errors:

* Check the [API Status Page](https://status.bouncy.ai)
* Review your [API Dashboard](https://bouncy.ai/api/dashboard) for usage limits
* Contact support at [help.bouncy.ai](mailto:help.bouncy.ai)
