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

# Error Handling Guide

> How to handle errors from the Cred Protocol API

## Overview

This guide covers how to handle errors from the Cred Protocol API, including common error codes, error response formats, and best practices for building resilient applications.

## Error Response Format

All API errors return a JSON response with a `detail` field:

```json theme={null}
{
  "detail": "Error message describing the issue"
}
```

Some errors may include additional fields:

```json theme={null}
{
  "detail": "Validation error",
  "errors": [
    {
      "field": "address",
      "message": "Invalid Ethereum address format"
    }
  ]
}
```

## HTTP Status Codes

### Client Errors (4xx)

| Status | Name                 | Description                                       |
| ------ | -------------------- | ------------------------------------------------- |
| 400    | Bad Request          | Invalid request parameters                        |
| 401    | Unauthorized         | Missing or invalid API key                        |
| 402    | Payment Required     | Authentication required (API key or x402 payment) |
| 403    | Forbidden            | Insufficient permissions                          |
| 404    | Not Found            | Resource not found                                |
| 422    | Unprocessable Entity | Validation error                                  |
| 429    | Too Many Requests    | Rate limit or credit limit exceeded               |

### Server Errors (5xx)

| Status | Name                  | Description                     |
| ------ | --------------------- | ------------------------------- |
| 500    | Internal Server Error | Unexpected server error         |
| 502    | Bad Gateway           | Upstream service error          |
| 503    | Service Unavailable   | Service temporarily unavailable |
| 504    | Gateway Timeout       | Request timed out               |

## Common Errors and Solutions

<AccordionGroup>
  <Accordion title="400 Bad Request">
    ```json theme={null}
    { "detail": "Invalid Ethereum address format" }
    ```

    **Causes:**

    * Malformed Ethereum address
    * Invalid ENS name
    * Missing required parameters

    **Solutions:**

    * Validate addresses before making requests
    * Use checksummed addresses when possible
    * Verify ENS names resolve correctly

    ```javascript theme={null}
    import { isAddress } from 'viem';

    function validateAddress(address) {
      // Check if it's a valid address or ENS name
      if (isAddress(address)) return true;
      if (address.endsWith('.eth')) return true;
      return false;
    }
    ```
  </Accordion>

  <Accordion title="401 Unauthorized">
    ```json theme={null}
    { "detail": "Invalid or missing API key" }
    ```

    **Causes:**

    * Missing `Authorization` header
    * Invalid API key format
    * Revoked API key

    **Solutions:**

    * Ensure header format is `Bearer YOUR_API_KEY`
    * Check for typos in the API key
    * Verify the key hasn't been revoked
    * Generate a new key if needed

    ```javascript theme={null}
    // Correct format
    headers: {
      'Authorization': 'Bearer sk_live_abc123'
    }

    // Common mistakes
    headers: {
      'Authorization': 'sk_live_abc123'  // Missing "Bearer "
      'Api-Key': 'sk_live_abc123'        // Wrong header name
    }
    ```
  </Accordion>

  <Accordion title="402 Payment Required">
    ```json theme={null}
    {
      "error": "authentication_required",
      "message": "This endpoint requires authentication. Choose one of the following methods:",
      "cred_units": 1,
      "options": [
        {
          "method": "api_token",
          "description": "Use an API token from your dashboard",
          "header": "Authorization: Bearer YOUR_API_KEY",
          "cost": "1 Cred Units"
        },
        {
          "method": "x402_payment",
          "description": "Pay per request with USDC (no account required)",
          "price": "$0.01",
          "currency": "USDC",
          "networks": ["base", "base-sepolia", "skale-base", "skale-base-sepolia"]
        }
      ]
    }
    ```

    **Causes:**

    * No `Authorization` header or `X-PAYMENT` header provided
    * This is an informational response, not an error

    **Response Headers:**

    * `X-PAYMENT-REQUIRED`: Base64-encoded payment requirements for x402-compatible clients (includes options for all enabled networks)

    **Solutions:**

    * Add an API key: `Authorization: Bearer YOUR_API_KEY`
    * Or use x402 payment: Sign a USDC transfer and include in `X-PAYMENT` header
    * Supported networks: Base, Base Sepolia, SKALE Base, SKALE Base Sepolia
    * See [Authentication Guide](/guides/authentication) for details

    ```javascript theme={null}
    async function handlePaymentRequired(response) {
      if (response.status === 402) {
        const data = await response.json();

        // Option 1: Prompt user to authenticate
        console.log('Authentication options:', data.options);

        // Option 2: If using x402, process payment header
        const paymentRequired = response.headers.get('X-PAYMENT-REQUIRED');
        // ... sign and retry with X-PAYMENT header
      }
    }
    ```
  </Accordion>

  <Accordion title="403 Forbidden">
    ```json theme={null}
    { "detail": "Insufficient permissions" }
    ```

    **Causes:**

    * Accessing a feature not in your plan
    * Account restrictions

    **Solutions:**

    * Check your plan features in the Dashboard
    * Upgrade your plan if needed
    * Contact support for account issues
  </Accordion>

  <Accordion title="404 Not Found">
    ```json theme={null}
    { "detail": "Address not found or has no on-chain activity" }
    ```

    **Causes:**

    * Address has never been used on-chain
    * ENS name doesn't exist
    * Incorrect endpoint path

    **Solutions:**

    * Verify the address exists on Etherscan
    * Check ENS name resolution
    * Verify the endpoint URL

    ```javascript theme={null}
    async function getScoreWithFallback(address) {
      try {
        const response = await fetch(`${API_URL}/api/v2/score/address/${address}`, {
          headers: { 'Authorization': `Bearer ${API_KEY}` }
        });
        
        if (response.status === 404) {
          // Address has no activity - return a default or handle gracefully
          return { score: null, message: 'No on-chain activity found' };
        }
        
        return response.json();
      } catch (error) {
        throw error;
      }
    }
    ```
  </Accordion>

  <Accordion title="429 Too Many Requests (Rate Limit)">
    ```json theme={null}
    {
      "detail": "Rate limit exceeded",
      "retry_after": 60
    }
    ```

    **Causes:**

    * Exceeded requests per minute limit

    **Solutions:**

    * Implement exponential backoff
    * Cache responses where possible
    * Wait for the `retry_after` period

    See [Rate Limiting](#rate-limiting) below.
  </Accordion>

  <Accordion title="429 Too Many Requests (Credit Limit Exceeded)">
    ```json theme={null}
    {
      "detail": {
        "error": "credit_limit_exceeded",
        "message": "You have reached your monthly limit of 1,000 Cred Units. Your usage resets on day 15 of each month.",
        "plan": "free",
        "monthly_limit": 1000,
        "used": 1000,
        "remaining": 0,
        "period_end": "2024-02-15T00:00:00+00:00",
        "upgrade_url": "https://app.credprotocol.com/dashboard/billing"
      }
    }
    ```

    **Causes:**

    * Exceeded monthly Cred Unit allocation

    **Response Fields:**

    | Field           | Description                          |
    | --------------- | ------------------------------------ |
    | `error`         | Machine-readable error code          |
    | `message`       | Human-readable explanation           |
    | `plan`          | Current plan (free, pro, growth)     |
    | `monthly_limit` | Total CUs allowed per month          |
    | `used`          | CUs consumed this billing period     |
    | `remaining`     | CUs remaining (0 when limit reached) |
    | `period_end`    | When credits reset (ISO 8601)        |
    | `upgrade_url`   | Link to upgrade plan                 |

    **Solutions:**

    * Wait until `period_end` for credits to reset
    * Upgrade your plan for more Cred Units
    * Optimize API usage by caching responses
    * Use summary endpoints (lower CU cost) where possible

    ```javascript theme={null}
    async function handleCreditLimitError(response) {
      const error = await response.json();

      if (error.detail?.error === 'credit_limit_exceeded') {
        const resetDate = new Date(error.detail.period_end);
        console.log(`Credit limit reached. Resets on ${resetDate.toLocaleDateString()}`);
        console.log(`Upgrade at: ${error.detail.upgrade_url}`);

        // Optionally notify user or disable API calls until reset
        return {
          limitReached: true,
          resetsAt: resetDate,
          upgradeUrl: error.detail.upgrade_url
        };
      }

      throw new Error(error.detail);
    }
    ```
  </Accordion>

  <Accordion title="500 Internal Server Error">
    ```json theme={null}
    { "detail": "Internal server error" }
    ```

    **Causes:**

    * Unexpected server-side error
    * Temporary system issue

    **Solutions:**

    * Retry with exponential backoff
    * Contact support if the issue persists
    * Check [status page](https://credprotocol.instatus.com) for outages
  </Accordion>
</AccordionGroup>

## Implementing Error Handling

### Basic Error Handling

```javascript theme={null}
async function getScore(address) {
  const response = await fetch(
    `https://api.credprotocol.com/api/v2/score/address/${address}`,
    {
      headers: { 'Authorization': `Bearer ${API_KEY}` }
    }
  );

  if (!response.ok) {
    const error = await response.json();
    
    switch (response.status) {
      case 400:
        throw new Error(`Invalid request: ${error.detail}`);
      case 401:
        throw new Error('Authentication failed. Check your API key.');
      case 404:
        throw new Error('Address not found or has no activity.');
      case 429:
        throw new Error('Rate limit exceeded. Please try again later.');
      default:
        throw new Error(`API error: ${error.detail}`);
    }
  }

  return response.json();
}
```

### With Retry Logic

```javascript theme={null}
async function fetchWithRetry(url, options, maxRetries = 3) {
  let lastError;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      
      // Don't retry client errors (except 429)
      if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        const error = await response.json();
        throw new Error(error.detail);
      }
      
      // Retry on rate limit
      if (response.status === 429) {
        const retryAfter = response.headers.get('Retry-After') || 60;
        await sleep(retryAfter * 1000);
        continue;
      }
      
      // Retry on server errors
      if (response.status >= 500) {
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        await sleep(delay);
        continue;
      }
      
      return response.json();
      
    } catch (error) {
      lastError = error;
      
      // Only retry on network errors
      if (error.name !== 'TypeError') throw error;
      
      const delay = Math.pow(2, attempt) * 1000;
      await sleep(delay);
    }
  }
  
  throw lastError;
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
```

### Python Example

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

def create_session():
    session = requests.Session()
    
    retries = Retry(
        total=3,
        backoff_factor=1,
        status_forcelist=[500, 502, 503, 504],
        allowed_methods=["GET"]
    )
    
    adapter = HTTPAdapter(max_retries=retries)
    session.mount("https://", adapter)
    
    return session

def get_score(address):
    session = create_session()
    
    response = session.get(
        f'https://api.credprotocol.com/api/v2/score/address/{address}',
        headers={'Authorization': f'Bearer {API_KEY}'}
    )
    
    if response.status_code == 429:
        retry_after = int(response.headers.get('Retry-After', 60))
        time.sleep(retry_after)
        return get_score(address)  # Retry once
    
    response.raise_for_status()
    return response.json()
```

## Rate Limiting

### Understanding Usage Limits

API usage is measured in Cred Units (CUs) based on your plan:

| Plan       | Cred Units/Month | Price    |
| ---------- | ---------------- | -------- |
| Free       | 1,000            | \$0      |
| Pro        | 50,000           | \$249/mo |
| Enterprise | Unlimited        | Custom   |

### Handling Rate Limits

```javascript theme={null}
class RateLimitedClient {
  constructor(apiKey, requestsPerMinute = 10) {
    this.apiKey = apiKey;
    this.requestsPerMinute = requestsPerMinute;
    this.requestTimes = [];
  }

  async request(endpoint) {
    // Wait if we've hit the rate limit
    await this.waitForRateLimit();
    
    this.requestTimes.push(Date.now());
    
    const response = await fetch(
      `https://api.credprotocol.com${endpoint}`,
      {
        headers: { 'Authorization': `Bearer ${this.apiKey}` }
      }
    );
    
    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
      await this.sleep(retryAfter * 1000);
      return this.request(endpoint); // Retry
    }
    
    return response.json();
  }

  async waitForRateLimit() {
    const now = Date.now();
    const oneMinuteAgo = now - 60000;
    
    // Remove old request times
    this.requestTimes = this.requestTimes.filter(t => t > oneMinuteAgo);
    
    if (this.requestTimes.length >= this.requestsPerMinute) {
      const oldestRequest = this.requestTimes[0];
      const waitTime = oldestRequest + 60000 - now;
      await this.sleep(waitTime);
    }
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Validate Input" icon="check">
    Validate addresses and parameters before making API calls
  </Card>

  <Card title="Use Retries" icon="rotate">
    Implement exponential backoff for transient errors
  </Card>

  <Card title="Cache Responses" icon="database">
    Cache successful responses to reduce API calls
  </Card>

  <Card title="Log Errors" icon="file-lines">
    Log errors with context for debugging
  </Card>
</CardGroup>

### Logging Example

```javascript theme={null}
async function getScore(address) {
  try {
    const data = await fetchScore(address);
    return data;
  } catch (error) {
    console.error('Cred API Error:', {
      address,
      error: error.message,
      timestamp: new Date().toISOString()
    });
    
    // Re-throw or handle gracefully
    throw error;
  }
}
```
