Enrich contacts, validate emails, and search your database programmatically. All endpoints use a simple API key for authentication and a credit-based billing model.
Log in to the XMagnet Dashboard and navigate to Settings → API Keys. Click Generate New Key, give it a label, and copy the key. You won’t be able to see the full key again.
Use the key in the X-API-Key header to enrich a contact:
curl -X POST https://api.xmagnet.ai/v1/enrich/single \
-H "Content-Type: application/json" \
-H "X-API-Key: xm_live_YOUR_KEY_HERE" \
-d '{
"email": "jane@example.com"
}'
A successful response returns enriched contact data:
{
"status": "success",
"data": {
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"title": "VP of Marketing",
"company": "Acme Corp",
"linkedin_url": "https://linkedin.com/in/janedoe",
"location": "San Francisco, CA",
"industry": "Technology"
},
"credits_used": 1,
"credits_remaining": 4999
}
Every request must include your API key. You can pass it in either of two headers:
| Header | Format | Example |
|---|---|---|
X-API-Key |
Raw key value | X-API-Key: xm_live_abc123... |
Authorization |
Bearer token | Authorization: Bearer xm_live_abc123... |
| Prefix | Environment | Description |
|---|---|---|
xm_live_... |
Production | Consumes credits and returns real data. Use in production applications. |
xm_test_... |
Test | Returns mock data and does not consume credits. Use during development. |
Enrich a single contact by email address, LinkedIn URL, or name + company combination.
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | One of email, linkedin_url, or name+company required | Contact’s email address |
linkedin_url |
string | One of email, linkedin_url, or name+company required | Full LinkedIn profile URL |
first_name |
string | Required with company | Contact’s first name |
last_name |
string | Required with company | Contact’s last name |
company |
string | Required with name | Company name or domain |
import requests
url = "https://api.xmagnet.ai/v1/enrich/single"
headers = {
"Content-Type": "application/json",
"X-API-Key": "xm_live_YOUR_KEY_HERE"
}
payload = {
"email": "jane@example.com"
}
response = requests.post(url, json=payload, headers=headers)
data = response.json()
print(data["data"]["title"]) # "VP of Marketing"
const response = await fetch("https://api.xmagnet.ai/v1/enrich/single", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "xm_live_YOUR_KEY_HERE"
},
body: JSON.stringify({
email: "jane@example.com"
})
});
const data = await response.json();
console.log(data.data.title); // "VP of Marketing"
{
"status": "success",
"data": {
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"title": "VP of Marketing",
"company": "Acme Corp",
"company_domain": "acmecorp.com",
"linkedin_url": "https://linkedin.com/in/janedoe",
"location": "San Francisco, CA",
"industry": "Technology",
"company_size": "201-500",
"phone": "+1-555-0123"
},
"credits_used": 1,
"credits_remaining": 4999
}
Enrich up to 100 contacts in a single request. Each contact in the array follows the same field rules as the single enrichment endpoint.
{
"contacts": [
{ "email": "jane@example.com" },
{ "email": "john@acmecorp.com" },
{ "linkedin_url": "https://linkedin.com/in/sarahsmith" },
{ "first_name": "Mike", "last_name": "Chen", "company": "globex.com" }
]
}
{
"status": "success",
"results": [
{
"input": { "email": "jane@example.com" },
"status": "found",
"data": {
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"title": "VP of Marketing",
"company": "Acme Corp"
}
},
{
"input": { "email": "john@acmecorp.com" },
"status": "found",
"data": { ... }
},
{
"input": { "linkedin_url": "https://linkedin.com/in/sarahsmith" },
"status": "not_found",
"data": null
},
{
"input": { "first_name": "Mike", "last_name": "Chen", "company": "globex.com" },
"status": "found",
"data": { ... }
}
],
"summary": {
"total": 4,
"found": 3,
"not_found": 1,
"credits_used": 3
},
"credits_remaining": 4996
}
summary object tells you exactly how many credits were used.
Validate a single email address. Returns deliverability status, risk score, and detailed checks.
{
"email": "jane@example.com"
}
{
"status": "success",
"data": {
"email": "jane@example.com",
"result": "deliverable",
"score": 98,
"is_free_provider": false,
"is_disposable": false,
"is_role_based": false,
"is_catch_all": false,
"mx_found": true,
"smtp_check": true,
"suggestion": null
},
"credits_used": 1,
"credits_remaining": 4998
}
| Value | Description |
|---|---|
deliverable |
The email address is valid and accepting mail. |
undeliverable |
The email address does not exist or is rejecting mail. |
risky |
The email may be a catch-all, disposable, or role-based address. Use with caution. |
unknown |
Unable to determine deliverability (e.g., server timeout). No credits are charged. |
Validate up to 500 email addresses in a single request.
{
"emails": [
"jane@example.com",
"bouncy@invalid.xyz",
"info@acmecorp.com"
]
}
{
"status": "success",
"results": [
{
"email": "jane@example.com",
"result": "deliverable",
"score": 98
},
{
"email": "bouncy@invalid.xyz",
"result": "undeliverable",
"score": 12
},
{
"email": "info@acmecorp.com",
"result": "risky",
"score": 55
}
],
"summary": {
"total": 3,
"deliverable": 1,
"undeliverable": 1,
"risky": 1,
"unknown": 0,
"credits_used": 3
},
"credits_remaining": 4995
}
Check your current credit balance and usage.
{
"status": "success",
"data": {
"plan": "Growth",
"credits_total": 10000,
"credits_used": 5004,
"credits_remaining": 4996,
"resets_at": "2026-05-01T00:00:00Z"
}
}
Rate limits are applied per API key and vary by key type:
| Key Type | Requests / Second | Requests / Minute | Bulk Max Items |
|---|---|---|---|
xm_test_... |
5 | 60 | 10 |
xm_live_... (Starter) |
10 | 300 | 100 |
xm_live_... (Growth) |
25 | 1,000 | 250 |
xm_live_... (Enterprise) |
100 | 5,000 | 500 |
When you exceed a rate limit, the API returns a 429 status with retry information:
{
"status": "error",
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please retry after 2 seconds.",
"retry_after": 2
}
}
retry_after field tells you exactly how long to wait.
XMagnet automatically caches enrichment and validation results for 30 days. If you request data for a contact that has been enriched within the last 30 days, the cached result is returned instantly and no credits are consumed.
To force a fresh lookup, pass "force_refresh": true in the request body. This will consume credits normally.
{
"email": "jane@example.com",
"force_refresh": true
}
All errors follow a consistent format:
{
"status": "error",
"error": {
"code": "INVALID_REQUEST",
"message": "The 'email' field must be a valid email address.",
"field": "email"
}
}
| Code | Meaning | Description |
|---|---|---|
200 |
OK | Request succeeded. |
400 |
Bad Request | Invalid or missing parameters. Check the message field for details. |
401 |
Unauthorized | Missing or invalid API key. |
402 |
Payment Required | Insufficient credits. Top up your account to continue. |
403 |
Forbidden | Your API key does not have the required scope for this endpoint. |
404 |
Not Found | The requested endpoint does not exist. |
429 |
Too Many Requests | Rate limit exceeded. Wait and retry using the retry_after value. |
500 |
Internal Server Error | Something went wrong on our end. Retry or contact support. |
Keep your API keys safe. Follow these best practices:
.env files or your platform’s secret manager — never hard-code them into source files.xm_test_...) return mock data and don’t consume credits.enrich:read only).import requests
import os
API_KEY = os.environ.get("XMAGNET_API_KEY")
BASE_URL = "https://api.xmagnet.ai/v1"
def enrich_contact(email=None, linkedin_url=None, first_name=None, last_name=None, company=None):
"""Enrich a single contact using the XMagnet API."""
payload = {}
if email:
payload["email"] = email
if linkedin_url:
payload["linkedin_url"] = linkedin_url
if first_name:
payload["first_name"] = first_name
if last_name:
payload["last_name"] = last_name
if company:
payload["company"] = company
response = requests.post(
f"{BASE_URL}/enrich/single",
json=payload,
headers={
"Content-Type": "application/json",
"X-API-Key": API_KEY
}
)
response.raise_for_status()
return response.json()
# Usage
result = enrich_contact(email="jane@example.com")
print(f"Name: {result['data']['first_name']} {result['data']['last_name']}")
print(f"Title: {result['data']['title']}")
print(f"Company: {result['data']['company']}")
print(f"Credits remaining: {result['credits_remaining']}")
const API_KEY = process.env.XMAGNET_API_KEY;
const BASE_URL = "https://api.xmagnet.ai/v1";
async function validateEmails(emails) {
const response = await fetch(`${BASE_URL}/validate/bulk`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": API_KEY
},
body: JSON.stringify({ emails })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error.message);
}
return response.json();
}
// Usage
const emails = [
"jane@example.com",
"bounce@invalid.xyz",
"info@acmecorp.com"
];
const result = await validateEmails(emails);
result.results.forEach(r => {
console.log(`${r.email}: ${r.result} (score: ${r.score})`);
});
console.log(`\nSummary: ${result.summary.deliverable} deliverable, ` +
`${result.summary.undeliverable} undeliverable, ` +
`${result.summary.risky} risky`);
curl -X GET https://api.xmagnet.ai/v1/credits \
-H "X-API-Key: xm_live_YOUR_KEY_HERE"
| Limit | Value |
|---|---|
| Max contacts per bulk enrichment | 100 (Starter), 250 (Growth), 500 (Enterprise) |
| Max emails per bulk validation | 500 |
| Max API keys per account | 10 |
| Cache TTL | 30 days |
| Max request body size | 1 MB |
| Request timeout | 30 seconds |
We’re here to help you integrate successfully: