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

# Error Handling

> Learn how to handle API errors, implement retry logic, and gracefully manage service failures.

## Overview

Proper error handling ensures your application remains resilient and provides a great user experience even when issues occur. This guide covers common errors, best practices, and implementation patterns.

***

## HTTP Status Codes

The Fraudiant API uses standard HTTP status codes to indicate success or failure:

| Status Code | Meaning               | Description                                              |
| ----------- | --------------------- | -------------------------------------------------------- |
| `200`       | Success               | Request completed successfully                           |
| `400`       | Bad Request           | Invalid input (malformed email, invalid domain)          |
| `401`       | Unauthorized          | Missing or invalid API key                               |
| `403`       | Forbidden             | Feature requires Pro account or insufficient permissions |
| `404`       | Not Found             | Resource not found (e.g., domain not in blocklist)       |
| `422`       | Unprocessable Entity  | Validation failed (e.g., duplicate blocklist entry)      |
| `429`       | Too Many Requests     | Rate limit exceeded                                      |
| `500`       | Internal Server Error | Unexpected server error                                  |
| `503`       | Service Unavailable   | Temporary service outage                                 |

***

## Common Error Responses

### Invalid Email (400)

Returned when the email address format is invalid:

```json theme={null}
{
  "status": 400,
  "error": "The email address is invalid."
}
```

**Cause:** Malformed email address (missing @, invalid characters, etc.)

**Solution:**

```javascript theme={null}
function validateEmailFormat(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!regex.test(email)) {
    return { valid: false, error: 'Invalid email format' };
  }
  return { valid: true };
}
```

***

### Unauthorized (401)

Returned when API key is missing, invalid, or expired:

```json theme={null}
{
  "status": 401,
  "error": "Unauthorized. Please provide a valid API key."
}
```

**Common causes:**

* Missing `Authorization` header
* Invalid API key format
* Expired or revoked API key
* Typo in API key

**Solution:**

```javascript theme={null}
async function validateWithAuth(email) {
  const apiKey = process.env.FRAUDIANT_API_KEY;

  if (!apiKey) {
    throw new Error('FRAUDIANT_API_KEY environment variable not set');
  }

  try {
    const response = await fetch(
      `https://api.fraudiant.com/email/${email}`,
      {
        headers: {
          'Authorization': `Bearer ${apiKey}`
        }
      }
    );

    if (response.status === 401) {
      console.error('Invalid API key. Please check your credentials.');
      // Alert admin or rotate key
      throw new Error('Authentication failed');
    }

    return response.json();
  } catch (error) {
    console.error('Authentication error:', error);
    throw error;
  }
}
```

***

### Pro Feature Required (403)

Returned when attempting to use Pro-only features (like blocklist management):

```json theme={null}
{
  "status": 403,
  "error": "This feature requires a Pro account."
}
```

**Solution:**

```javascript theme={null}
async function addToBlocklist(domain) {
  try {
    const response = await fetch(
      'https://api.fraudiant.com/blocklist',
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ domain })
      }
    );

    if (response.status === 403) {
      return {
        success: false,
        error: 'Pro account required. Upgrade at https://app.fraudiant.com/upgrade'
      };
    }

    return { success: true };
  } catch (error) {
    console.error('Blocklist error:', error);
    return { success: false, error: error.message };
  }
}
```

***

### Rate Limit Exceeded (429)

Returned when you exceed your rate limit:

```json theme={null}
{
  "status": 429,
  "error": "Too many requests",
  "retry_after": 30
}
```

**Solution with exponential backoff:**

```javascript theme={null}
async function validateWithRetry(email, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(
        `https://api.fraudiant.com/email/${email}`,
        {
          headers: {
            'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}`
          }
        }
      );

      if (response.status === 429) {
        const retryAfter = response.headers.get('Retry-After') || Math.pow(2, attempt);
        console.log(`Rate limited. Retrying after ${retryAfter}s...`);

        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
        continue; // Retry
      }

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      return await response.json();

    } catch (error) {
      if (attempt === maxRetries - 1) {
        console.error('Max retries exceeded:', error);
        throw error;
      }

      // Exponential backoff
      const delay = Math.pow(2, attempt) * 1000;
      console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
```

***

### Service Unavailable (503)

Returned during temporary service outages:

```json theme={null}
{
  "status": 503,
  "error": "Service temporarily unavailable. Please try again later."
}
```

**Solution: Fail Open Strategy**

```javascript theme={null}
async function validateEmailSafe(email) {
  try {
    const response = await fetch(
      `https://api.fraudiant.com/email/${email}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}`
        },
        timeout: 5000 // 5 second timeout
      }
    );

    if (response.status === 503) {
      console.warn('Fraudiant service unavailable. Failing open.');
      return {
        valid: true,
        failedOpen: true,
        message: 'Validation service temporarily unavailable'
      };
    }

    const validation = await response.json();

    return {
      valid: !validation.disposable && !validation.spam && validation.mx,
      data: validation
    };

  } catch (error) {
    console.error('Service error:', error);
    // Fail open - don't block users
    return {
      valid: true,
      failedOpen: true,
      error: error.message
    };
  }
}
```

***

## Network & Timeout Errors

### Timeout Handling

```javascript theme={null}
async function validateWithTimeout(email, timeoutMs = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const response = await fetch(
      `https://api.fraudiant.com/email/${email}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}`
        },
        signal: controller.signal
      }
    );

    clearTimeout(timeoutId);
    return await response.json();

  } catch (error) {
    clearTimeout(timeoutId);

    if (error.name === 'AbortError') {
      console.error('Request timeout after', timeoutMs, 'ms');
      // Fail open
      return { disposable: false, spam: false, mx: true, timeout: true };
    }

    throw error;
  }
}
```

### Connection Errors

```javascript theme={null}
async function validateWithConnectionHandling(email) {
  try {
    const response = await fetch(
      `https://api.fraudiant.com/email/${email}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}`
        }
      }
    );

    return await response.json();

  } catch (error) {
    // Network errors
    if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
      console.error('Cannot reach Fraudiant API. Check network connection.');
      return { valid: true, networkError: true };
    }

    // DNS errors
    if (error.code === 'EAI_AGAIN') {
      console.error('DNS resolution failed. Temporary network issue.');
      return { valid: true, dnsError: true };
    }

    // SSL/TLS errors
    if (error.code === 'CERT_HAS_EXPIRED') {
      console.error('SSL certificate error');
      return { valid: true, sslError: true };
    }

    throw error;
  }
}
```

***

## Error Logging & Monitoring

### Structured Error Logging

```javascript theme={null}
class ErrorLogger {
  static log(context, error, metadata = {}) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      context,
      error: {
        message: error.message,
        stack: error.stack,
        code: error.code
      },
      metadata,
      severity: this.getSeverity(error)
    };

    console.error(JSON.stringify(logEntry));

    // Send to monitoring service (e.g., Sentry, DataDog)
    // this.sendToMonitoring(logEntry);
  }

  static getSeverity(error) {
    if (error.code === 401) return 'critical'; // Auth failure
    if (error.code === 429) return 'warning';  // Rate limit
    if (error.code === 503) return 'warning';  // Service down
    return 'error';
  }
}

// Usage
async function validateWithLogging(email) {
  try {
    const validation = await fetch(
      `https://api.fraudiant.com/email/${email}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}`
        }
      }
    ).then(r => r.json());

    return validation;

  } catch (error) {
    ErrorLogger.log('email_validation', error, { email: email.split('@')[1] });

    // Fail open
    return { valid: true, error: true };
  }
}
```

### Circuit Breaker Pattern

Prevent cascading failures by implementing a circuit breaker:

```javascript theme={null}
class CircuitBreaker {
  constructor(threshold = 5, timeout = 60000) {
    this.failureCount = 0;
    this.threshold = threshold;
    this.timeout = timeout;
    this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
    this.nextAttempt = Date.now();
  }

  async execute(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        throw new Error('Circuit breaker is OPEN');
      }
      this.state = 'HALF_OPEN';
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  onSuccess() {
    this.failureCount = 0;
    this.state = 'CLOSED';
  }

  onFailure() {
    this.failureCount++;
    if (this.failureCount >= this.threshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.timeout;
      console.error('Circuit breaker opened');
    }
  }
}

// Usage
const breaker = new CircuitBreaker(5, 60000);

async function validateWithCircuitBreaker(email) {
  try {
    return await breaker.execute(async () => {
      const response = await fetch(
        `https://api.fraudiant.com/email/${email}`,
        {
          headers: {
            'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}`
          }
        }
      );

      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      return response.json();
    });
  } catch (error) {
    console.error('Validation failed:', error.message);
    // Fail open when circuit is open
    return { valid: true, circuitOpen: true };
  }
}
```

***

## Error Recovery Strategies

### Graceful Degradation

```javascript theme={null}
async function validateWithFallback(email) {
  try {
    // Primary: Full validation
    const validation = await fetch(
      `https://api.fraudiant.com/email/${email}`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}`
        },
        timeout: 3000
      }
    ).then(r => r.json());

    return {
      valid: !validation.disposable && !validation.spam && validation.mx,
      quality: 'full',
      data: validation
    };

  } catch (error) {
    console.warn('Full validation failed, using fallback');

    // Fallback: Basic format check only
    const isValidFormat = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);

    return {
      valid: isValidFormat,
      quality: 'basic',
      fallback: true,
      error: error.message
    };
  }
}
```

### Queue Failed Requests

```javascript theme={null}
class ValidationQueue {
  constructor() {
    this.queue = [];
    this.processing = false;
  }

  async add(email) {
    return new Promise((resolve, reject) => {
      this.queue.push({ email, resolve, reject, retries: 0 });
      this.process();
    });
  }

  async process() {
    if (this.processing || this.queue.length === 0) return;

    this.processing = true;

    while (this.queue.length > 0) {
      const item = this.queue[0];

      try {
        const validation = await fetch(
          `https://api.fraudiant.com/email/${item.email}`,
          {
            headers: {
              'Authorization': `Bearer ${process.env.FRAUDIANT_API_KEY}`
            }
          }
        ).then(r => r.json());

        this.queue.shift(); // Remove from queue
        item.resolve(validation);

      } catch (error) {
        item.retries++;

        if (item.retries >= 3) {
          this.queue.shift(); // Remove after max retries
          item.reject(error);
        } else {
          // Move to back of queue
          this.queue.push(this.queue.shift());
          await new Promise(r => setTimeout(r, 1000 * item.retries));
        }
      }
    }

    this.processing = false;
  }
}
```

***

## Testing Error Scenarios

### Mock Error Responses

```javascript theme={null}
// test-helpers/fraudiant-mock.js
class FraudiantMock {
  constructor(mode = 'success') {
    this.mode = mode;
  }

  async validate(email) {
    switch (this.mode) {
      case 'rate_limit':
        throw { status: 429, error: 'Too many requests' };

      case 'auth_error':
        throw { status: 401, error: 'Unauthorized' };

      case 'service_down':
        throw { status: 503, error: 'Service unavailable' };

      case 'timeout':
        await new Promise(r => setTimeout(r, 10000)); // Simulate timeout
        throw new Error('Timeout');

      case 'network_error':
        throw { code: 'ECONNREFUSED', message: 'Connection refused' };

      default:
        return {
          disposable: email.includes('temp'),
          spam: false,
          mx: true
        };
    }
  }
}

// Usage in tests
describe('Error Handling', () => {
  it('should handle rate limit errors', async () => {
    const mock = new FraudiantMock('rate_limit');
    const result = await validateWithRetry('[email protected]', mock);
    expect(result.valid).toBe(true);
    expect(result.failedOpen).toBe(true);
  });
});
```

***

## Best Practices Summary

<AccordionGroup>
  <Accordion title="Always implement timeout handling">
    Set reasonable timeouts (3-5 seconds) to prevent hanging requests.
  </Accordion>

  <Accordion title="Fail open, not closed">
    When errors occur, allow users to proceed rather than blocking them completely.
  </Accordion>

  <Accordion title="Log errors with context">
    Include request metadata, timestamps, and error details for debugging.
  </Accordion>

  <Accordion title="Implement retry logic">
    Use exponential backoff for transient failures like rate limits and network issues.
  </Accordion>

  <Accordion title="Monitor error rates">
    Track error patterns to identify systemic issues early.
  </Accordion>

  <Accordion title="Test error scenarios">
    Use mocks to test how your application handles various error conditions.
  </Accordion>
</AccordionGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Best Practices" icon="star" href="/best-practices">
    Learn optimization and performance strategies
  </Card>

  <Card title="Rate Limits" icon="gauge" href="/api-reference/rate-limits">
    Understand rate limits and quotas
  </Card>

  <Card title="Integration Examples" icon="code" href="/integration-examples">
    View framework-specific implementations
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    Complete API documentation
  </Card>
</CardGroup>
