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

# Developer Experience

> Comprehensive guide to LeadMagic API's developer-friendly features

LeadMagic is built with developers in mind. Every response includes rich metadata, standardized headers, and detailed error messages to make debugging and monitoring effortless.

## Why We Build This Way

> [!NOTE]
>
> **Our Philosophy:** APIs should be predictable, transparent, and never surprise you. Every decision in our API design prioritizes developer happiness and operational confidence.

### Design Principles

> [card] Standards-First
>
> We follow **RFC 9457** for errors, **IETF draft-ietf-httpapi-ratelimit-headers** for rate limits, and **RFC 7807** patterns throughout. No proprietary formats to learn.

> [card] Observable by Default
>
> Every response includes 15+ headers giving you complete visibility into credits, rate limits, concurrency, and usage - without extra API calls.

> [card] Graceful Degradation
>
> Soft-mode rate limiting, automatic retries, and fallback mechanisms mean our limits guide you rather than break you.

> [card] Context-Rich Errors
>
> Errors include actionable suggestions, documentation links, and machine-readable context so you can handle them programmatically.

### Why Response Headers Matter

Traditional APIs force you to make extra calls to check your balance or limits. LeadMagic embeds everything in response headers:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
# One request = Full operational context
curl -I 'https://api.leadmagic.io/v1/people/email-validation' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"email": "test@example.com"}'
```

**Every response includes:**

* Your remaining credits (`X-Credits-Remaining`)
* Rate limit status (`RateLimit-Remaining`, `RateLimit-Reset`)
* Daily quota (`X-RateLimit-Remaining-Daily`)
* Usage percentages (`X-RateLimit-Daily-Usage-Percent`)
* Soft mode status (`X-RateLimit-Soft-Mode`)

This means you can build dashboards, set up alerts, and implement smart rate limiting without polling separate endpoints.

***

## Response Headers

Every API response includes comprehensive headers for monitoring, debugging, and rate limit management.

> [tab] Rate Limits
>
> ### IETF Standard Headers (Recommended)
>
> Following the [IETF draft-ietf-httpapi-ratelimit-headers](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/) standard:
>
> | Header | Description | Example |
> | - | - | - |
> | `RateLimit-Limit` | Max requests/min | `300` |
> | `RateLimit-Remaining` | Remaining this minute | `2847` |
> | `RateLimit-Reset` | Seconds until reset | `42` |
>
> ### Legacy X- Headers (Backwards Compatible)
>
> | Header | Description | Example |
> | - | - | - |
> | `X-RateLimit-Limit` | Max requests/min | `300` |
> | `X-RateLimit-Remaining` | Remaining this minute | `2847` |
> | `X-RateLimit-Reset` | Unix timestamp reset | `1706745600` |
>
> ### Daily Limit Headers
>
> | Header | Description | Example |
> | - | - | - |
> | `X-RateLimit-Limit-Daily` | Max requests/day | `500000` |
> | `X-RateLimit-Remaining-Daily` | Remaining today | `485000` |
> | `X-RateLimit-Reset-Daily` | Unix timestamp reset | `1706832000` |

> [tab] Credits & Usage
>
> ### Credit Tracking
>
> | Header | Description | Example |
> | - | - | - |
> | `X-Credits-Remaining` | Current credit balance | `15432.50` |
> | `X-Credits-Cost` | Credits consumed | `1` |
>
> ### Usage Percentages
>
> | Header | Description | Example |
> | - | - | - |
> | `X-RateLimit-Daily-Usage-Percent` | Daily usage % | `15` |
> | `X-RateLimit-RPM-Usage-Percent` | Current minute usage % | `5` |
>
> > [!TIP]
> >
> > Use these headers to build dashboards, set up alerts at 50%/80%/95% thresholds, or implement automatic top-ups when credits run low.

> [tab] Soft Mode & Concurrency
>
> ### Soft Mode
>
> LeadMagic uses "soft mode" rate limiting by default — we warn but don't block:
>
> | Header | Description | Example |
> | - | - | - |
> | `X-RateLimit-Soft-Mode` | Soft mode enabled | `true` |
> | `X-RateLimit-Soft-Exceeded` | Limit exceeded but allowed | `true` |
>
> > [!NOTE]
> >
> > **Soft Mode** means we log rate limit violations but allow requests through. This prevents unexpected failures while giving you visibility into usage patterns.
>
> ### Concurrency
>
> | Header | Description | Example |
> | - | - | - |
> | `X-Concurrent-Requests` | Current active requests | `12` |
> | `X-Active-Reservations` | Credit reservations in progress | `5` |
> | `X-Peak-Concurrency` | Peak concurrent this minute | `45` |

### Complete Header Reference

Here's a real-world example showing all headers in a typical response:

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
HTTP/2 200 OK
Content-Type: application/json

# IETF Standard Rate Limit Headers (Use these!)
RateLimit-Limit: 300
RateLimit-Remaining: 287
RateLimit-Reset: 42

# Legacy Rate Limit Headers (Backwards compatible)
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1706745600

# Daily Limits
X-RateLimit-Limit-Daily: 500000
X-RateLimit-Remaining-Daily: 485000
X-RateLimit-Reset-Daily: 1706832000

# Credits Tracking
X-Credits-Remaining: 15432.50
X-Credits-Cost: 0.25

# Usage Metrics (percentages for easy alerting)
X-RateLimit-Daily-Usage-Percent: 3
X-RateLimit-RPM-Usage-Percent: 5

# Soft Mode (we warn but don't block)
X-RateLimit-Soft-Mode: true

# Concurrency
X-Concurrent-Requests: 12
X-Active-Reservations: 0
X-Peak-Concurrency: 45

# Request Tracking
X-Request-ID: req_abc123def456
```

> [!TIP]
>
> **Pro Tip:** Log these headers after every request. When debugging issues, you'll have complete visibility into your API state at that exact moment.

***

## Request Headers

### Required Headers

| Header | Description | Example |
| - | - | - |
| `X-API-Key` | Your API key (case-insensitive) | `lm_live_abc123...` |
| `Content-Type` | Always `application/json` for POST | `application/json` |

> [!NOTE]
>
> **Case-Insensitive Authentication:** The `X-API-Key` header accepts any case variation: `X-API-Key`, `X-API-KEY`, `x-api-key` all work identically.

### Optional Headers

| Header | Description | Use Case |
| - | - | - |
| `User-Agent` | Identifies your application | Used for timeout optimization (Clay integrations get longer timeouts) |
| `X-Request-ID` | Your request tracking ID | Returned in error responses for debugging |
| `Accept` | Response format | `application/json` (default) |

***

## Success Messages

Every endpoint returns a human-readable `message` field that tells you exactly what happened. These are designed to be user-friendly and can be displayed directly in your UI.

> [tab] Email Validation
>
> | Message | Meaning |
> | - | - |
> | `Email is valid.` | Email verified as deliverable |
> | `Email is invalid.` | Email doesn't exist or bounces |
> | `Unable to determine email validity.` | Verification inconclusive (free) |

> [tab] Email Finder
>
> | Message | Meaning |
> | - | - |
> | `Valid email found.` | Email found and verified (1 credit) |
> | `No email found for this person at this company.` | No email found (free) |

> [tab] Mobile Finder
>
> | Message | Meaning |
> | - | - |
> | `Mobile number found.` | Phone number successfully retrieved |
> | `No mobile number found for this contact.` | No phone data available |

> [tab] Profile Search
>
> | Message | Meaning |
> | - | - |
> | `Profile found.` | Full profile data returned |
> | `Profile not found or not accessible.` | Profile doesn't exist or is private |

> [tab] Company Search
>
> | Message | Meaning |
> | - | - |
> | `Company found` | Company data returned |
> | `Company not found` | No matching company |

> [tab] Role Finder
>
> | Message | Meaning |
> | - | - |
> | `Role Found` | Person with matching role found |
> | `No matching role found at this company.` | No matching role exists |

> [tab] People Search
>
> | Message | Meaning |
> | - | - |
> | `People found` | People list returned |
> | `No people found` | No matching people for the requested filters |

> [tab] Job Change Detector
>
> | Message | Meaning |
> | - | - |
> | `No job change detected. Still employed at expected company.` | Person still at company |
> | `Job change detected. Person has moved to a new company.` | Person changed jobs |
> | `Never worked at the expected company.` | No employment record found |

> [!NOTE]
>
> **Why Messages Matter:** These aren't just for humans - they're also machine-readable. Check the `message` field to determine success vs. not-found scenarios, especially when `credits_consumed` might still be > 0 for verified "not found" results.

## Analytics Endpoints

Monitor your API usage programmatically with our comprehensive analytics suite.

> [!NOTE]
>
> **All analytics endpoints are FREE** - they don't consume any credits and are exempt from rate limiting.

### Dashboard Overview

Get a real-time snapshot of your account status:

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl 'https://api.leadmagic.io/v1/analytics/dashboard' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```javascript Node.js theme={"theme":{"light":"github-light","dark":"github-dark"}}
const response = await fetch('https://api.leadmagic.io/v1/analytics/dashboard', {
  headers: { 'X-API-Key': 'YOUR_API_KEY' }
});
const dashboard = await response.json();
console.log(`Credits: ${dashboard.credits.current}`);
console.log(`RPM Used: ${dashboard.rate_limit.minute.used}/${dashboard.rate_limit.minute.limit}`);
```

```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import requests

response = requests.get(
    'https://api.leadmagic.io/v1/analytics/dashboard',
    headers={'X-API-Key': 'YOUR_API_KEY'}
)
dashboard = response.json()
print(f"Credits: {dashboard['credits']['current']}")
print(f"Daily Used: {dashboard['rate_limit']['daily']['used']}")
```

**Response:**

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "user": {
    "id": "user_abc123",
    "email": "developer@company.com"
  },
  "credits": {
    "current": 15432.50,
    "formatted": "$154.33"
  },
  "rate_limit": {
    "minute": {
      "limit": 300,
      "used": 45,
      "remaining": 255,
      "utilization": 15.0
    },
    "daily": {
      "limit": 500000,
      "used": 12500,
      "remaining": 487500,
      "utilization": 2.5
    }
  },
  "concurrency": {
    "current": 0,
    "peak": 23,
    "active_reservations": 0
  },
  "stats": {
    "today": {
      "requests": 1250,
      "credits": 875.50,
      "chargeable_requests": 1100,
      "chargeable_rate": 88.0,
      "unique_products": 5
    },
    "this_week": {
      "requests": 8500,
      "credits": 5200.25,
      "chargeable_requests": 7200,
      "chargeable_rate": 84.7,
      "unique_products": 8
    },
    "this_month": {
      "requests": 45000,
      "credits": 28500.00,
      "chargeable_requests": 38000,
      "chargeable_rate": 84.4,
      "unique_products": 12
    }
  }
}
```

### Available Analytics Endpoints

> [card] GET /v1/analytics/dashboard
>
> Real-time dashboard with credits, rate limits, and usage stats for today, this week, and this month.

> [card] GET /v1/analytics/usage
>
> Daily usage summary with total requests, credits consumed, and chargeable rates.
>
> **Query params:** `?days=30` (1-90)

> [card] GET /v1/analytics/products
>
> Per-product breakdown showing requests, credits, success rates, and average costs.
>
> **Query params:** `?days=30` (1-90)

> [card] GET /v1/analytics/credits
>
> Credit consumption history with daily breakdown and chargeable request analysis.
>
> **Query params:** `?days=30` (1-90)

> [card] GET /v1/analytics/summary
>
> All-time statistics including total requests, credits consumed, success rates, and first/last request timestamps.
>
> **Query params:** `?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD`

> [card] GET /v1/analytics/daily
>
> Detailed daily metrics with latency percentiles, error rates, and performance data.
>
> **Query params:** `?days=30` (1-90)

> [card] GET /v1/analytics/day/:date
>
> Per-product breakdown for a specific day. Top 15 products plus aggregated "other" category.
>
> **Path param:** `:date` in YYYY-MM-DD format

### Usage Example: Daily Breakdown

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl 'https://api.leadmagic.io/v1/analytics/usage?days=7' \
  -H 'X-API-Key: YOUR_API_KEY'
```

**Response:**

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "period": {
    "start": "2026-10-25",
    "end": "2026-11-01",
    "days": 7
  },
  "summary": {
    "total_requests": 8500,
    "chargeable_requests": 7200,
    "total_credits": 5200.25,
    "avg_credits_per_request": 0.61,
    "chargeable_rate": 84.7,
    "unique_products": 8
  },
  "daily": [
    {
      "date": "2026-11-01",
      "total_requests": 1250,
      "chargeable_requests": 1100,
      "total_credits": 875.50,
      "unique_products_used": 5
    }
    // ... more days
  ]
}
```

## Credits Endpoints

### Check Your Balance

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl 'https://api.leadmagic.io/v1/credits' \
  -H 'X-API-Key: YOUR_API_KEY'
```

```javascript Node.js theme={"theme":{"light":"github-light","dark":"github-dark"}}
const response = await fetch('https://api.leadmagic.io/v1/credits', {
  headers: { 'X-API-Key': 'YOUR_API_KEY' }
});
const { credits } = await response.json();
console.log(`Available credits: ${credits}`);
```

```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import requests

response = requests.get(
    'https://api.leadmagic.io/v1/credits',
    headers={'X-API-Key': 'YOUR_API_KEY'}
)
print(f"Credits: {response.json()['credits']}")
```

**Response:**

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "credits": 15432.50
}
```

### Additional Credits Endpoints

| Endpoint | Method | Description |
| - | - | - |
| `GET /v1/credits` | GET | Get current credit balance |
| `POST /v1/credits/refresh` | POST | Force refresh credits from database |
| `GET /v1/credits/health` | GET | Validate API key and check authentication status |

## Error Handling

LeadMagic follows [RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457) for standardized, machine-readable error responses.

### Error Response Format

```json {3,4,5,6,7,8} theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "success": false,
  "errors": [
    {
      "type": "https://api.leadmagic.io/errors/insufficient_credits",
      "title": "Insufficient credits: need 5, have 2.50. Add credits to continue.",
      "status": 402,
      "detail": "This request requires 5 credit(s) but your account only has 2.50 credits remaining.",
      "instance": "/v1/people/mobile-finder#req_abc123",
      "code": "insufficient_credits",
      "action": "Add credits to your account at https://app.leadmagic.io/billing",
      "docs": "https://leadmagic.io/docs/v1/credits",
      "context": {
        "credits_required": 5,
        "credits_available": 2.50,
        "credits_needed": 2.50
      }
    }
  ],
  "meta": {
    "request_id": "req_abc123",
    "timestamp": "2026-10-01T12:00:00.000Z",
    "environment": "production"
  }
}
```

### Response Fields Explained

| Field | Description |
| - | - |
| `type` | URI identifying the error type (machine-readable) |
| `title` | Human-readable summary (safe to display to users) |
| `status` | HTTP status code |
| `detail` | Specific explanation of what went wrong |
| `code` | Short error code for programmatic handling |
| `action` | Suggested fix or next steps |
| `docs` | Link to relevant documentation |
| `context` | Machine-readable details (varies by error type) |

### HTTP Status Codes

> [card] 200
>
> **Success** - Request completed

> [card] 400
>
> **Bad Request** - Invalid input

> [card] 401
>
> **Unauthorized** - Invalid API key

> [card] 402
>
> **Payment Required** - Low credits

> [card] 429
>
> **Too Many Requests** - Rate limited

> [card] 500
>
> **Server Error** - Our fault

### Error Types

> [tab] 401 Authentication
>
> **HTTP 401 - Unauthorized**
>
> | Code | Description |
> | - | - |
> | `missing_authentication` | No API key provided in `X-API-Key` header |
> | `invalid_api_key` | API key doesn't exist or is incorrect |
> | `insufficient_permissions` | API key lacks access to this resource |
>
> ```json {4,5} theme={"theme":{"light":"github-light","dark":"github-dark"}}
> {
>   "errors": [{
>     "type": "https://api.leadmagic.io/errors/invalid_api_key",
>     "title": "Invalid API key. The key does not exist or is incorrect.",
>     "status": 401,
>     "code": "invalid_api_key",
>     "docs": "https://leadmagic.io/docs/v1/authentication"
>   }]
> }
> ```

> [tab] 400 Validation
>
> **HTTP 400 - Bad Request**
>
> | Code | Description |
> | - | - |
> | `validation_error` | Request validation failed |
> | `missing_required_field` | Required field not provided |
> | `invalid_json` | Request body is not valid JSON |
> | `invalid_parameter` | Parameter value is invalid |
>
> ```json {4,5,7} theme={"theme":{"light":"github-light","dark":"github-dark"}}
> {
>   "errors": [{
>     "type": "https://api.leadmagic.io/errors/validation_error",
>     "title": "Request validation failed. Check your input parameters.",
>     "status": 400,
>     "code": "validation_error",
>     "param": ["email"],
>     "docs": "https://leadmagic.io/docs/v1/making-api-calls"
>   }]
> }
> ```

> [tab] 429 Rate Limits
>
> **HTTP 429 - Too Many Requests**
>
> | Code | Description |
> | - | - |
> | `rate_limit_exceeded` | Exceeded per-minute or daily limit |
>
> > [!NOTE]
> >
> > Includes `Retry-After` header with seconds until reset.
>
> ```json {4,5,8,9} theme={"theme":{"light":"github-light","dark":"github-dark"}}
> {
>   "errors": [{
>     "type": "https://api.leadmagic.io/errors/rate_limit_exceeded",
>     "title": "Rate limit exceeded: 300 requests per 1 minute.",
>     "status": 429,
>     "code": "rate_limit_exceeded",
>     "detail": "You have exceeded the maximum allowed request rate.",
>     "action": "Wait 42 seconds before retrying.",
>     "context": {
>       "limit": 300,
>       "window": "1 minute",
>       "remaining": 0,
>       "reset_at": 1706745642,
>       "retry_after_seconds": 42
>     }
>   }]
> }
> ```

> [tab] 402 Credits
>
> **HTTP 402 - Payment Required**
>
> | Code | Description |
> | - | - |
> | `insufficient_credits` | Not enough credits for this request |
>
> ```json {4,5,8-11} theme={"theme":{"light":"github-light","dark":"github-dark"}}
> {
>   "errors": [{
>     "type": "https://api.leadmagic.io/errors/insufficient_credits",
>     "title": "Insufficient credits: need 5, have 2.50.",
>     "status": 402,
>     "code": "insufficient_credits",
>     "context": {
>       "credits_required": 5,
>       "credits_available": 2.50,
>       "credits_needed": 2.50
>     }
>   }]
> }
> ```

> [tab] 5xx Server
>
> **HTTP 5xx - Server Errors**
>
> | Code | Status | Description |
> | - | - | - |
> | `internal_error` | 500 | Unexpected server error |
> | `external_service_error` | 502 | Upstream service failed |
> | `service_unavailable` | 503 | Service temporarily down |
>
> ```json {4,5,7} theme={"theme":{"light":"github-light","dark":"github-dark"}}
> {
>   "errors": [{
>     "type": "https://api.leadmagic.io/errors/internal_error",
>     "title": "Something went wrong on our end.",
>     "status": 500,
>     "code": "INTERNAL_ERROR",
>     "action": "Wait 30 seconds and retry. Contact support@leadmagic.io if persistent."
>   }]
> }
> ```

## Idempotency

Every request can include a unique identifier for tracking and debugging:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST 'https://api.leadmagic.io/v1/people/email-validation' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'X-Request-ID: my-unique-request-id-123' \
  -H 'Content-Type: application/json' \
  -d '{"email": "test@example.com"}'
```

The `X-Request-ID` is returned in error responses and logged for debugging:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "errors": [{
    "instance": "/v1/people/email-validation#my-unique-request-id-123"
  }],
  "meta": {
    "request_id": "my-unique-request-id-123"
  }
}
```

## Graceful Degradation

LeadMagic is designed to handle failures gracefully:

| Scenario | Behavior |
| - | - |
| **DO Timeout** | Falls back to direct database query |
| **Rate limit in soft mode** | Logs warning but allows request |
| **External service failure** | Returns 502 with retry guidance |
| **Database unavailable** | Returns 503 with retry timing |

> [!NOTE]
>
> **High Availability:** Our Durable Objects provide sub-millisecond rate limiting with automatic failover to PostgreSQL when needed.

## Best Practices

> [accordion] Implement Exponential Backoff
>
> When you receive a 429 error, use the `Retry-After` header:
>
> ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> async function callWithRetry(fn, maxRetries = 3) {
>   for (let i = 0; i < maxRetries; i++) {
>     const response = await fn();
>
>     if (response.status === 429) {
>       const retryAfter = response.headers.get('Retry-After') || Math.pow(2, i);
>       await sleep(retryAfter * 1000);
>       continue;
>     }
>
>     return response;
>   }
>   throw new Error('Max retries exceeded');
> }
> ```

> [accordion] Monitor Rate Limit Headers
>
> Build proactive monitoring using response headers:
>
> ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> async function monitoredRequest(url, options) {
>   const response = await fetch(url, options);
>
>   const dailyUsage = response.headers.get('X-RateLimit-Daily-Usage-Percent');
>   const creditsRemaining = response.headers.get('X-Credits-Remaining');
>
>   if (parseInt(dailyUsage) > 80) {
>     alert('Approaching daily rate limit: ' + dailyUsage + '%');
>   }
>
>   if (parseFloat(creditsRemaining) < 1000) {
>     alert('Low credits: ' + creditsRemaining);
>   }
>
>   return response;
> }
> ```

> [accordion] Use Request IDs for Debugging
>
> Always include a request ID for easier debugging:
>
> ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
>
> const response = await fetch(url, {
>   headers: {
>     'X-API-Key': apiKey,
>     'X-Request-ID': requestId,
>     'Content-Type': 'application/json'
>   },
>   body: JSON.stringify(data)
> });
>
> // Log for debugging
> console.log(`Request ${requestId}: ${response.status}`);
> ```

> [accordion] Cache Credits Check
>
> Don't call `/v1/credits` before every request. Cache and refresh periodically:
>
> ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let cachedCredits = null;
> let lastFetch = 0;
> const CACHE_TTL = 60000; // 1 minute
>
> async function getCredits() {
>   if (cachedCredits && Date.now() - lastFetch < CACHE_TTL) {
>     return cachedCredits;
>   }
>
>   const response = await fetch('https://api.leadmagic.io/v1/credits', {
>     headers: { 'X-API-Key': apiKey }
>   });
>
>   const { credits } = await response.json();
>   cachedCredits = credits;
>   lastFetch = Date.now();
>   return credits;
> }
> ```

## SDK Support

While we don't have official SDKs yet, our REST API works seamlessly with any HTTP client:

> [card] Node.js
>
> Use native `fetch` or `axios` for HTTP requests.

> [card] Python
>
> Use `requests` or `httpx` for async support.

> [card] Any Language
>
> Any HTTP client works - just send JSON with your API key.

***

## Why We Built It This Way

> [accordion] RFC 9457 Problem Details for Errors
>
> Traditional APIs return cryptic errors like `{"error": "Bad request"}`. This leaves developers guessing what went wrong and how to fix it.
>
> **Our approach:** Every error follows [RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457), a standard designed by the IETF specifically for HTTP API errors. This means:
>
> * **`type`**: A URI identifying the error type (bookmarkable, consistent)
> * **`title`**: Human-readable summary you can show users
> * **`status`**: HTTP status code (redundant but useful)
> * **`detail`**: Specific explanation of what went wrong
> * **`action`**: Suggested fix (unique to LeadMagic)
> * **`docs`**: Link to relevant documentation
> * **`context`**: Machine-readable details (credits needed, rate limits, etc.)
>
> This isn't just for show - your code can programmatically handle errors based on `type` while showing users the `title` and `action`.

> [accordion] IETF Rate Limit Headers
>
> We implement the [IETF draft-ietf-httpapi-ratelimit-headers](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/) standard for rate limit headers:
>
> ```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
> RateLimit-Limit: 300
> RateLimit-Remaining: 287
> RateLimit-Reset: 42
> ```
>
> **Why this matters:**
>
> * These headers are on track to become an official standard
> * Libraries already support them (many HTTP clients auto-parse)
> * Consistent across all modern APIs that implement the spec
> * No need to learn LeadMagic-specific header names
>
> We also include legacy `X-RateLimit-*` headers for backwards compatibility.

> [accordion] Soft Mode Rate Limiting
>
> Most APIs hard-block you when you hit limits. This causes:
>
> * Unexpected failures in production
> * Data loss if you're mid-batch
> * Frustrated developers
>
> **Our approach:** Soft mode warns but doesn't block. When you exceed limits, we:
>
> 1. Log the violation
> 2. Set `X-RateLimit-Soft-Exceeded: true`
> 3. **Still process your request**
>
> This gives you time to fix your implementation without breaking production. Watch for the header in logs and fix before we have to enforce limits.

> [accordion] Credits in Every Response
>
> Checking your balance shouldn't require extra API calls. Every enrichment response includes:
>
> ```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
> X-Credits-Remaining: 15432.50
> X-Credits-Cost: 1
> ```
>
> **Benefits:**
>
> * Build credit alerts without polling
> * Log spend per-request for cost attribution
> * Pause automatically when credits run low
> * Never wonder "how much did that cost?"

> [accordion] Meaningful Success Messages
>
> Our `message` field isn't just "success" - it tells you what actually happened:
>
> * `"Email is valid."` - Great, use it!
> * `"Email is invalid."` - Do not email
> * `"Unable to determine email validity."` - Unknown result (free)
>
> This lets you:
>
> * Display messages directly to end users
> * Make programmatic decisions based on outcomes
> * Distinguish between "not found" and "error"

> [accordion] Zero-Cost Analytics
>
> All analytics endpoints are **FREE** - no credits consumed, no rate limits. This means you can:
>
> * Poll `/v1/analytics/dashboard` every minute
> * Build real-time dashboards
> * Set up alerting without worrying about cost
> * Audit your usage as often as needed
>
> We want you to have complete visibility into your API usage.

## Next Steps

> [card] [Authentication](https://leadmagic.io/docs/v1/authentication.md)
>
> Deep dive into API key management and security best practices.

> [card] [Credits Guide](https://leadmagic.io/docs/v1/credits.md)
>
> Understand credit costs, billing, and optimization strategies.

> [card] [API Reference](https://leadmagic.io/docs/v1/reference/email-validation.md)
>
> Explore all available endpoints in the interactive playground.

> [card] [Integrations](https://leadmagic.io/docs/v1/integrations.md)
>
> Connect LeadMagic with Clay, Make, Zapier, and more.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.