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

# Error Handling

> Best practices for handling API errors

# Error Handling Guide

Learn how to properly handle errors from the GTMAPIs validation API.

## Error Response Format

All errors return an appropriate HTTP status code with JSON body:

```json theme={null}
{
  "error": "Error Type",
  "message": "Detailed error message"
}
```

## HTTP Status Codes

| Status | Error                 | Description                          |
| ------ | --------------------- | ------------------------------------ |
| 200    | Success               | Request processed successfully       |
| 400    | Bad Request           | Invalid request format or parameters |
| 401    | Unauthorized          | Missing or invalid API key           |
| 429    | Too Many Requests     | Rate limit exceeded                  |
| 500    | Internal Server Error | Unexpected server error              |
| 503    | Service Unavailable   | Service temporarily unavailable      |

## Common Errors

### 400 Bad Request

**Cause**: Invalid request format or missing required fields

#### Missing Email Field

```json theme={null}
{
  "error": "Bad Request",
  "message": "Email field is required"
}
```

**Solution**: Include `email` field in request body

```javascript theme={null}
// ❌ Wrong
fetch('https://api.gtmapis.com/v1/validate', {
  body: JSON.stringify({})
});

// ✅ Correct
fetch('https://api.gtmapis.com/v1/validate', {
  body: JSON.stringify({ email: 'john@company.com' })
});
```

#### Invalid Email Format

```json theme={null}
{
  "error": "Bad Request",
  "message": "Invalid email format"
}
```

**Solution**: Validate email format before sending

```javascript theme={null}
function isValidEmailFormat(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

if (!isValidEmailFormat(email)) {
  throw new Error('Invalid email format');
}
```

#### Too Many Emails in Bulk Request

```json theme={null}
{
  "error": "Bad Request",
  "message": "Maximum 100 emails per request. Received: 150"
}
```

**Solution**: Split into batches of 100

```javascript theme={null}
function chunkArray(array, size) {
  const chunks = [];
  for (let i = 0; i < array.length; i += size) {
    chunks.push(array.slice(i, i + size));
  }
  return chunks;
}

const batches = chunkArray(emails, 100);
for (const batch of batches) {
  await validateBulk(batch);
}
```

### 401 Unauthorized

**Cause**: Missing or invalid API key

#### Missing API Key Header

```json theme={null}
{
  "error": "Unauthorized",
  "message": "X-API-Key header is required"
}
```

**Solution**: Always include `X-API-Key` header

```javascript theme={null}
// ❌ Wrong
fetch('https://api.gtmapis.com/v1/validate', {
  headers: {
    'Content-Type': 'application/json'
  }
});

// ✅ Correct
fetch('https://api.gtmapis.com/v1/validate', {
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.GTMAPIS_API_KEY
  }
});
```

#### Invalid API Key

```json theme={null}
{
  "error": "Unauthorized",
  "message": "Invalid API key"
}
```

**Solution**: Verify API key format and validity

```javascript theme={null}
// Check API key format
if (!apiKey || !apiKey.match(/^gtm_(test|live)_[a-f0-9]{32}$/)) {
  throw new Error('Invalid API key format');
}
```

#### Revoked API Key

```json theme={null}
{
  "error": "Unauthorized",
  "message": "API key has been revoked"
}
```

**Solution**: Generate a new API key from the dashboard

### 429 Too Many Requests

**Cause**: Rate limit exceeded (10,000 requests/minute per API key or 1,000 requests/minute per IP)

```json theme={null}
{
  "error": "Rate limit exceeded",
  "message": "You have exceeded the rate limit of 10000 requests per minute",
  "retry_after": 60
}
```

**Solution**: Implement retry logic with exponential backoff

```javascript theme={null}
async function validateWithRetry(email, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('https://api.gtmapis.com/v1/validate', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-API-Key': process.env.GTMAPIS_API_KEY
        },
        body: JSON.stringify({ email })
      });

      if (response.status === 429) {
        const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
        console.log(`Rate limited. Waiting ${retryAfter}s...`);
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
        continue;
      }

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

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

      // Exponential backoff
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
```

### 500 Internal Server Error

**Cause**: Unexpected server error

```json theme={null}
{
  "error": "Internal Server Error",
  "message": "An unexpected error occurred"
}
```

**Solution**: Retry request or contact support

```javascript theme={null}
async function validateWithErrorHandling(email) {
  try {
    return await validateEmail(email);
  } catch (error) {
    if (error.status === 500) {
      // Log for investigation
      console.error('Server error:', error);

      // Retry once after delay
      await new Promise(resolve => setTimeout(resolve, 5000));
      return await validateEmail(email);
    }
    throw error;
  }
}
```

### 503 Service Unavailable

**Cause**: Service temporarily unavailable (maintenance or overload)

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

**Solution**: Implement circuit breaker pattern

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

// Usage
const breaker = new CircuitBreaker();

async function validateWithCircuitBreaker(email) {
  return await breaker.execute(() => validateEmail(email));
}
```

## Validation Result Errors

Even with HTTP 200, emails can have validation issues:

### Invalid Email

```json theme={null}
{
  "email": "notreal@fakeemail123.com",
  "result": "invalid",
  "reason": "Domain has no MX records",
  "validation_layer": "dns"
}
```

**Handling**:

```javascript theme={null}
if (result.result === 'invalid') {
  console.log(`Email ${result.email} is invalid: ${result.reason}`);
  // Remove from list
  removeFromList(result.email);
}
```

### Risky Email

```json theme={null}
{
  "email": "anyone@catchall.com",
  "result": "risky",
  "reason": "Catch-all domain detected"
}
```

**Handling**:

```javascript theme={null}
if (result.result === 'risky') {
  console.log(`Email ${result.email} is risky: ${result.reason}`);
  // Mark as low priority or exclude
  markAsLowPriority(result.email);
}
```

### Unknown Verification

```json theme={null}
{
  "email": "john@restrictive.com",
  "result": "unknown",
  "reason": "SMTP verification not permitted"
}
```

**Handling**:

```javascript theme={null}
if (result.result === 'unknown') {
  console.log(`Cannot verify ${result.email}: ${result.reason}`);
  // Test with small batch first
  addToTestBatch(result.email);
}
```

## Error Handling Patterns

### Comprehensive Error Handler

```javascript theme={null}
async function validateEmailSafely(email) {
  try {
    const response = await fetch('https://api.gtmapis.com/v1/validate', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': process.env.GTMAPIS_API_KEY
      },
      body: JSON.stringify({ email })
    });

    // Handle HTTP errors
    if (!response.ok) {
      switch (response.status) {
        case 400:
          throw new Error('Invalid request format');
        case 401:
          throw new Error('Invalid API key - check your credentials');
        case 429:
          const retryAfter = response.headers.get('Retry-After') || 60;
          throw new Error(`Rate limited - retry after ${retryAfter}s`);
        case 500:
          throw new Error('Server error - please retry');
        case 503:
          throw new Error('Service unavailable - try again later');
        default:
          throw new Error(`HTTP ${response.status}`);
      }
    }

    const result = await response.json();

    // Handle validation results
    switch (result.result) {
      case 'valid':
        return { status: 'success', data: result };

      case 'valid_role_based':
        return {
          status: 'warning',
          message: 'Role-based email (info@, support@)',
          data: result
        };

      case 'risky':
        return {
          status: 'warning',
          message: 'Catch-all domain - cannot verify mailbox',
          data: result
        };

      case 'invalid':
        return {
          status: 'error',
          message: result.reason || 'Invalid email',
          data: result
        };

      case 'unknown':
        return {
          status: 'warning',
          message: 'Cannot verify - server restrictions',
          data: result
        };

      default:
        return {
          status: 'error',
          message: 'Unexpected result type',
          data: result
        };
    }
  } catch (error) {
    // Network or parsing errors
    console.error('Validation error:', error);
    return {
      status: 'error',
      message: error.message,
      data: null
    };
  }
}

// Usage
const result = await validateEmailSafely('john@company.com');

if (result.status === 'success') {
  // Email is valid
  console.log('Valid email:', result.data.email);
} else if (result.status === 'warning') {
  // Email has issues but may be usable
  console.warn(result.message, result.data);
} else {
  // Email is invalid or error occurred
  console.error(result.message);
}
```

### Bulk Validation Error Handling

```javascript theme={null}
async function validateBulkWithErrorHandling(emails) {
  const results = [];
  const errors = [];

  for (let i = 0; i < emails.length; i += 100) {
    const batch = emails.slice(i, i + 100);

    try {
      const response = await fetch('https://api.gtmapis.com/v1/validate/bulk', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-API-Key': process.env.GTMAPIS_API_KEY
        },
        body: JSON.stringify({ emails: batch })
      });

      if (!response.ok) {
        // Log error but continue with next batch
        errors.push({
          batch: i / 100 + 1,
          emails: batch,
          error: `HTTP ${response.status}`
        });
        continue;
      }

      const batchResults = await response.json();
      results.push(...batchResults.results);

    } catch (error) {
      // Network error - log and continue
      errors.push({
        batch: i / 100 + 1,
        emails: batch,
        error: error.message
      });
    }

    // Rate limit delay
    await new Promise(resolve => setTimeout(resolve, 500));
  }

  return {
    results,
    errors,
    summary: {
      total: emails.length,
      validated: results.length,
      failed: errors.reduce((sum, e) => sum + e.emails.length, 0)
    }
  };
}
```

## Monitoring and Logging

### Log Errors for Analysis

```javascript theme={null}
function logValidationError(error, context) {
  console.error('Validation Error:', {
    timestamp: new Date().toISOString(),
    error: error.message,
    stack: error.stack,
    context: {
      email: context.email,
      apiKey: context.apiKey?.substring(0, 15) + '...',  // Partial key only
      endpoint: context.endpoint
    }
  });

  // Send to error tracking service
  if (process.env.SENTRY_DSN) {
    Sentry.captureException(error, {
      contexts: { validation: context }
    });
  }
}
```

### Track Error Rates

```javascript theme={null}
class ErrorTracker {
  constructor() {
    this.errors = new Map();
  }

  track(errorType) {
    const count = this.errors.get(errorType) || 0;
    this.errors.set(errorType, count + 1);
  }

  getStats() {
    return Object.fromEntries(this.errors);
  }

  reset() {
    this.errors.clear();
  }
}

const errorTracker = new ErrorTracker();

// Track errors
try {
  await validateEmail(email);
} catch (error) {
  if (error.status === 429) {
    errorTracker.track('rate_limit');
  } else if (error.status === 500) {
    errorTracker.track('server_error');
  }
  throw error;
}

// Review stats periodically
setInterval(() => {
  console.log('Error stats:', errorTracker.getStats());
  errorTracker.reset();
}, 60000);
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Rate Limits" icon="gauge" href="/guides/rate-limits">
    Understand rate limiting strategies
  </Card>

  <Card title="API Integration" icon="code" href="/guides/api-integration">
    Full integration patterns
  </Card>
</CardGroup>
