Skip to main content

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:
{
  "detail": "Error message describing the issue"
}
Some errors may include additional fields:
{
  "detail": "Validation error",
  "errors": [
    {
      "field": "address",
      "message": "Invalid Ethereum address format"
    }
  ]
}

HTTP Status Codes

Client Errors (4xx)

StatusNameDescription
400Bad RequestInvalid request parameters
401UnauthorizedMissing or invalid API key
402Payment RequiredAuthentication required (API key or x402 payment)
403ForbiddenInsufficient permissions
404Not FoundResource not found
422Unprocessable EntityValidation error
429Too Many RequestsRate limit or credit limit exceeded

Server Errors (5xx)

StatusNameDescription
500Internal Server ErrorUnexpected server error
502Bad GatewayUpstream service error
503Service UnavailableService temporarily unavailable
504Gateway TimeoutRequest timed out

Common Errors and Solutions

{ "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
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;
}
{ "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
// 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
}
{
  "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",
      "network": "base"
    }
  ]
}
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
Solutions:
  • Add an API key: Authorization: Bearer YOUR_API_KEY
  • Or use x402 payment: Sign a USDC transfer and include in X-PAYMENT header
  • See Authentication Guide for details
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
  }
}
{ "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
{ "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
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;
  }
}
{
  "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 below.
{
  "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:
FieldDescription
errorMachine-readable error code
messageHuman-readable explanation
planCurrent plan (free, pro, growth)
monthly_limitTotal CUs allowed per month
usedCUs consumed this billing period
remainingCUs remaining (0 when limit reached)
period_endWhen credits reset (ISO 8601)
upgrade_urlLink 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
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);
}
{ "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 for outages

Implementing Error Handling

Basic Error Handling

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

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

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:
PlanCred Units/MonthPrice
Free1,000$0
Pro50,000$249/mo
EnterpriseUnlimitedCustom

Handling Rate Limits

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

Validate Input

Validate addresses and parameters before making API calls

Use Retries

Implement exponential backoff for transient errors

Cache Responses

Cache successful responses to reduce API calls

Log Errors

Log errors with context for debugging

Logging Example

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;
  }
}