# Clearbit Combined API Replacement

This guide provides step-by-step instructions for migrating from Clearbit's Combined API (Person + Company) to Tomba's `/combined/find` endpoint.

---

## API Comparison

| Feature                | Clearbit Combined API                  | Tomba Combined API              |
| ---------------------- | -------------------------------------- | ------------------------------- |
| **Endpoint**           | `person.clearbit.com/v2/combined/find` | `api.tomba.io/v1/combined/find` |
| **Input**              | Email                                  | Email                           |
| **Authentication**     | Bearer token                           | API Key + Secret headers        |
| **Rate Limit**         | 600 req/min                            | 300 req/min                     |
| **Person Data**        | ✅                                     | ✅ Enhanced                     |
| **Company Data**       | ✅                                     | ✅ Enhanced                     |
| **Email Verification** | ❌                                     | ✅ FREE                         |
| **Phone Numbers**      | Limited                                | ✅ Validated                    |
| **Technology Stack**   | Basic                                  | ✅ 6000+ technologies           |
| **Similar Companies**  | ❌                                     | ✅                              |
| **API Calls**          | 1                                      | 1 (same efficiency)             |
| **Cost**               | $999/10k                               | $119/10k (88% savings)          |
| **Free Trial**         | 50 calls                               | 50 searches + 500 verifications |

---

## Endpoint Migration

### Clearbit Combined API

```http
GET https://person.clearbit.com/v2/combined/find?email=john@stripe.com
Authorization: Bearer sk_clearbit_xxx
```

### Tomba Combined API

```http
GET https://api.tomba.io/v1/combined/find?email=john@stripe.com
X-Tomba-Key: your-api-key
X-Tomba-Secret: your-secret-key
```

---

## Why Use Combined API?

The Combined API returns both person and company data in a single request, giving you:

✅ **Reduced API calls** - 1 call instead of 2  
✅ **Lower latency** - Get both datasets instantly  
✅ **Cost savings** - Pay for 1 request, not 2  
✅ **Simplified code** - One endpoint, one response

---

## Response Format Comparison

### Clearbit Response

```json
{
    "person": {
        "id": "d54c54ad-40be-4305-8a34-0ab44710b90d",
        "name": {
            "fullName": "John Doe",
            "givenName": "John",
            "familyName": "Doe"
        },
        "email": "john@stripe.com",
        "location": "San Francisco, CA, US",
        "employment": {
            "domain": "stripe.com",
            "name": "Stripe",
            "title": "Senior Software Engineer",
            "role": "engineering",
            "seniority": "senior"
        },
        "linkedin": {
            "handle": "in/johndoe"
        },
        "twitter": {
            "handle": "johndoe",
            "followers": 1000
        }
    },
    "company": {
        "id": "027b0d40-016c-40ea-8925-a076fa640992",
        "name": "Stripe",
        "domain": "stripe.com",
        "description": "Online payment processing for internet businesses",
        "location": "San Francisco, CA, US",
        "logo": "https://logo.clearbit.com/stripe.com",
        "metrics": {
            "employees": 7000,
            "employeesRange": "1001-5000",
            "estimatedAnnualRevenue": "$1B-$10B"
        },
        "category": {
            "industry": "Financial Services"
        }
    }
}
```

### Tomba Response

```json
{
    "data": {
        "person": {
            "email": "john@stripe.com",
            "first_name": "John",
            "last_name": "Doe",
            "full_name": "John Doe",
            "gender": null,
            "phone_number": "+1-415-555-0123",
            "position": "Senior Software Engineer",
            "department": "Engineering",
            "seniority": "senior",
            "twitter": "johndoe",
            "linkedin": "https://www.linkedin.com/in/johndoe",
            "location": {
                "city": "San Francisco",
                "state": "California",
                "country": "United States",
                "country_code": "US"
            },
            "verification": {
                "status": "valid",
                "result": "deliverable",
                "score": 98,
                "smtp_check": true,
                "disposable": false
            }
        },
        "company": {
            "organization": "Stripe",
            "domain": "stripe.com",
            "website": "https://stripe.com",
            "email_count": 347,
            "phone_number": "+1-888-926-2289",
            "country": "US",
            "state": "California",
            "city": "San Francisco",
            "postal_code": "94103",
            "street": "510 Townsend Street",
            "revenue": "$1B-$10B",
            "employees": "1001-5000",
            "founded": 2010,
            "industry": "Financial Services",
            "description": "Online payment processing for internet businesses",
            "logo": "https://d12h6n0d1mbsct.cloudfront.net/logos/stripe-com.jpg",
            "twitter": "stripe",
            "linkedin": "stripe",
            "facebook": "stripe",
            "technologies": [
                {
                    "name": "Stripe",
                    "category": "Payment Processing"
                },
                {
                    "name": "AWS",
                    "category": "Cloud Infrastructure"
                }
            ],
            "similar_domains": ["square.com", "paypal.com", "adyen.com"]
        }
    }
}
```

---

## Field Mapping

### Person Fields

| Clearbit Field                | Tomba Field           | Notes                       |
| ----------------------------- | --------------------- | --------------------------- |
| `person.name.fullName`        | `person.full_name`    | Full name                   |
| `person.name.givenName`       | `person.first_name`   | First name                  |
| `person.name.familyName`      | `person.last_name`    | Last name                   |
| `person.email`                | `person.email`        | Email address               |
| `person.employment.title`     | `person.position`     | Job title                   |
| `person.employment.role`      | `person.department`   | Department                  |
| `person.employment.seniority` | `person.seniority`    | Seniority                   |
| `person.location`             | `person.location.*`   | Location object             |
| `person.linkedin.handle`      | `person.linkedin`     | LinkedIn URL                |
| `person.twitter.handle`       | `person.twitter`      | Twitter handle              |
| N/A                           | `person.phone_number` | **New: Phone number**       |
| N/A                           | `person.verification` | **New: Email verification** |

### Company Fields

| Clearbit Field                           | Tomba Field                                        | Notes                     |
| ---------------------------------------- | -------------------------------------------------- | ------------------------- |
| `company.name`                           | `company.organization`                             | Company name              |
| `company.domain`                         | `company.domain`                                   | Domain                    |
| `company.description`                    | `company.description`                              | Description               |
| `company.location`                       | `company.city`, `company.state`, `company.country` | Location fields           |
| `company.logo`                           | `company.logo`                                     | Logo URL                  |
| `company.metrics.employees`              | `company.employees`                                | Employee count            |
| `company.metrics.estimatedAnnualRevenue` | `company.revenue`                                  | Revenue estimate          |
| `company.category.industry`              | `company.industry`                                 | Industry                  |
| N/A                                      | `company.email_count`                              | **New: Available emails** |
| N/A                                      | `company.phone_number`                             | **New: Company phone**    |
| N/A                                      | `company.technologies`                             | **New: Tech stack**       |
| N/A                                      | `company.similar_domains`                          | **New: Competitors**      |

---

## Code Examples

### Node.js / JavaScript

**Before (Clearbit):**

```javascript
const clearbit = require("clearbit")("sk_clearbit_xxx");

async function getCombined(email) {
    try {
        const combined = await clearbit.Combined.find({ email });
        return {
            person: {
                name: combined.person?.name?.fullName,
                position: combined.person?.employment?.title,
                location: combined.person?.location,
            },
            company: {
                name: combined.company?.name,
                industry: combined.company?.category?.industry,
                employees: combined.company?.metrics?.employees,
            },
        };
    } catch (error) {
        console.error("Clearbit error:", error);
        throw error;
    }
}
```

**After (Tomba):**

```javascript
const axios = require("axios");

async function getCombined(email) {
    try {
        const response = await axios.get(
            "https://api.tomba.io/v1/combined/find",
            {
                params: { email },
                headers: {
                    "X-Tomba-Key": process.env.TOMBA_API_KEY,
                    "X-Tomba-Secret": process.env.TOMBA_API_SECRET,
                },
            },
        );

        const data = response.data.data;
        return {
            person: {
                name: data.person.full_name,
                position: data.person.position,
                location: `${data.person.location?.city}, ${data.person.location?.country}`,
                // Bonus fields
                phone: data.person.phone_number,
                emailValid: data.person.verification?.status === "valid",
                emailScore: data.person.verification?.score,
            },
            company: {
                name: data.company.organization,
                industry: data.company.industry,
                employees: data.company.employees,
                // Bonus fields
                revenue: data.company.revenue,
                emailCount: data.company.email_count,
                technologies: data.company.technologies?.map((t) => t.name),
                competitors: data.company.similar_domains,
            },
        };
    } catch (error) {
        console.error("Tomba error:", error);
        throw error;
    }
}

// Usage
getCombined("john@stripe.com").then(console.log);
```

### Python

**Before (Clearbit):**

```python
import clearbit

clearbit.key = 'sk_clearbit_xxx'

def get_combined(email):
    try:
        combined = clearbit.Combined.find(email=email)
        return {
            'person': {
                'name': combined.get('person', {}).get('name', {}).get('fullName'),
                'position': combined.get('person', {}).get('employment', {}).get('title'),
                'location': combined.get('person', {}).get('location')
            },
            'company': {
                'name': combined.get('company', {}).get('name'),
                'industry': combined.get('company', {}).get('category', {}).get('industry'),
                'employees': combined.get('company', {}).get('metrics', {}).get('employees')
            }
        }
    except clearbit.errors.NotFoundError:
        return None
```

**After (Tomba):**

```python
import requests
import os

def get_combined(email):
    try:
        response = requests.get(
            'https://api.tomba.io/v1/combined/find',
            params={'email': email},
            headers={
                'X-Tomba-Key': os.getenv('TOMBA_API_KEY'),
                'X-Tomba-Secret': os.getenv('TOMBA_API_SECRET')
            }
        )
        response.raise_for_status()

        data = response.json()['data']
        return {
            'person': {
                'name': data['person'].get('full_name'),
                'position': data['person'].get('position'),
                'location': f"{data['person'].get('location', {}).get('city')}, {data['person'].get('location', {}).get('country')}",
                # Bonus fields
                'phone': data['person'].get('phone_number'),
                'email_valid': data['person'].get('verification', {}).get('status') == 'valid',
                'email_score': data['person'].get('verification', {}).get('score')
            },
            'company': {
                'name': data['company'].get('organization'),
                'industry': data['company'].get('industry'),
                'employees': data['company'].get('employees'),
                # Bonus fields
                'revenue': data['company'].get('revenue'),
                'email_count': data['company'].get('email_count'),
                'technologies': [t['name'] for t in data['company'].get('technologies', [])],
                'competitors': data['company'].get('similar_domains', [])
            }
        }
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 404:
            return None
        raise

# Usage
combined = get_combined('john@stripe.com')
print(combined)
```

### PHP

**Before (Clearbit):**

```php
<?php
require 'vendor/autoload.php';

use Clearbit\Clearbit;

Clearbit::setApiKey('sk_clearbit_xxx');

function getCombined($email) {
    try {
        $combined = Clearbit::combined()->find(['email' => $email]);

        return [
            'person' => [
                'name' => $combined->person->name->fullName ?? null,
                'position' => $combined->person->employment->title ?? null,
                'location' => $combined->person->location ?? null
            ],
            'company' => [
                'name' => $combined->company->name ?? null,
                'industry' => $combined->company->category->industry ?? null,
                'employees' => $combined->company->metrics->employees ?? null
            ]
        ];
    } catch (\Clearbit\Errors\NotFoundError $e) {
        return null;
    }
}
```

**After (Tomba):**

```php
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;

function getCombined($email) {
    $client = new Client();

    try {
        $response = $client->get('https://api.tomba.io/v1/combined/find', [
            'query' => ['email' => $email],
            'headers' => [
                'X-Tomba-Key' => getenv('TOMBA_API_KEY'),
                'X-Tomba-Secret' => getenv('TOMBA_API_SECRET')
            ]
        ]);

        $data = json_decode($response->getBody())->data;

        return [
            'person' => [
                'name' => $data->person->full_name ?? null,
                'position' => $data->person->position ?? null,
                'location' => "{$data->person->location->city}, {$data->person->location->country}",
                // Bonus fields
                'phone' => $data->person->phone_number ?? null,
                'email_valid' => ($data->person->verification->status ?? '') === 'valid',
                'email_score' => $data->person->verification->score ?? 0
            ],
            'company' => [
                'name' => $data->company->organization ?? null,
                'industry' => $data->company->industry ?? null,
                'employees' => $data->company->employees ?? null,
                // Bonus fields
                'revenue' => $data->company->revenue ?? null,
                'email_count' => $data->company->email_count ?? 0,
                'technologies' => array_map(fn($t) => $t->name, $data->company->technologies ?? []),
                'competitors' => $data->company->similar_domains ?? []
            ]
        ];
    } catch (\GuzzleHttp\Exception\ClientException $e) {
        if ($e->getResponse()->getStatusCode() === 404) {
            return null;
        }
        throw $e;
    }
}

// Usage
$combined = getCombined('john@stripe.com');
print_r($combined);
```

---

## Use Cases

### Sales Intelligence

Get everything you need about a prospect in one call:

```javascript
const prospect = await getCombined("decision.maker@target-company.com");

console.log("Prospect Profile:");
console.log(`Name: ${prospect.person.name}`);
console.log(`Position: ${prospect.person.position}`);
console.log(`Phone: ${prospect.person.phone}`);
console.log(`Email Valid: ${prospect.person.emailValid ? "Yes" : "No"}`);

console.log("\nCompany Profile:");
console.log(`Company: ${prospect.company.name}`);
console.log(`Industry: ${prospect.company.industry}`);
console.log(`Size: ${prospect.company.employees} employees`);
console.log(`Revenue: ${prospect.company.revenue}`);
console.log(`Technologies: ${prospect.company.technologies.join(", ")}`);
console.log(`Competitors: ${prospect.company.competitors.join(", ")}`);
```

### Lead Enrichment

Enrich leads in your CRM:

```javascript
async function enrichLead(email) {
    const data = await getCombined(email);

    if (!data) {
        return { status: "not_found" };
    }

    return {
        status: "enriched",
        contact: {
            first_name: data.person.name.split(" ")[0],
            last_name: data.person.name.split(" ").slice(1).join(" "),
            title: data.person.position,
            phone: data.person.phone,
            email_verified: data.person.emailValid,
            linkedin_url: data.person.linkedin,
        },
        company: {
            name: data.company.name,
            domain: data.company.domain,
            industry: data.company.industry,
            employee_count: data.company.employees,
            annual_revenue: data.company.revenue,
            tech_stack: data.company.technologies,
            competitors: data.company.competitors,
        },
    };
}
```

### Account-Based Marketing

Build comprehensive account profiles:

```javascript
async function buildAccountProfile(emails) {
    const profiles = await Promise.all(
        emails.map((email) => getCombined(email)),
    );

    const validProfiles = profiles.filter((p) => p !== null);

    if (validProfiles.length === 0) {
        return null;
    }

    // Aggregate company data (same company for all contacts)
    const company = validProfiles[0].company;

    // Collect all contacts
    const contacts = validProfiles.map((p) => ({
        name: p.person.name,
        position: p.person.position,
        phone: p.person.phone,
        email_score: p.person.emailScore,
        seniority: p.person.seniority,
    }));

    return {
        company: {
            name: company.name,
            size: company.employees,
            revenue: company.revenue,
            industry: company.industry,
            technologies: company.technologies,
            competitors: company.competitors,
            total_contacts_available: company.emailCount,
        },
        contacts: contacts,
        decision_makers: contacts.filter(
            (c) =>
                c.seniority === "executive" ||
                c.position.includes("VP") ||
                c.position.includes("Director"),
        ),
    };
}
```

---

## Performance Optimization

### Batch Processing

```javascript
async function enrichLeadsBatch(emails, batchSize = 10) {
    const results = [];

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

        const batchResults = await Promise.allSettled(
            batch.map((email) => getCombined(email)),
        );

        results.push(
            ...batchResults.map((r, idx) => ({
                email: batch[idx],
                data: r.status === "fulfilled" ? r.value : null,
                error: r.status === "rejected" ? r.reason : null,
            })),
        );

        // Respect rate limits
        if (i + batchSize < emails.length) {
            await new Promise((resolve) => setTimeout(resolve, 200));
        }
    }

    return results;
}

// Usage
const emails = [
    "contact1@company.com",
    "contact2@company.com",
    "contact3@company.com",
];

const enriched = await enrichLeadsBatch(emails);
console.log(
    `Enriched ${enriched.filter((r) => r.data).length}/${emails.length} leads`,
);
```

---

## Error Handling

```javascript
async function getCombinedSafe(email) {
    try {
        const response = await axios.get(
            "https://api.tomba.io/v1/combined/find",
            {
                params: { email },
                headers: {
                    "X-Tomba-Key": process.env.TOMBA_API_KEY,
                    "X-Tomba-Secret": process.env.TOMBA_API_SECRET,
                },
            },
        );

        return {
            success: true,
            data: response.data.data,
        };
    } catch (error) {
        if (error.response?.status === 404) {
            return {
                success: false,
                error: "NOT_FOUND",
                message: "Email or company not found in database",
            };
        } else if (error.response?.status === 429) {
            return {
                success: false,
                error: "RATE_LIMIT",
                message: "Rate limit exceeded. Please retry later.",
            };
        } else if (error.response?.status === 400) {
            return {
                success: false,
                error: "INVALID_EMAIL",
                message: "Invalid email format",
            };
        } else if (error.response?.status === 401) {
            return {
                success: false,
                error: "AUTH_FAILED",
                message: "Invalid API credentials",
            };
        } else {
            return {
                success: false,
                error: "UNKNOWN",
                message: error.message,
            };
        }
    }
}
```

---

## Migration Checklist

- [ ] Sign up for Tomba account
- [ ] Generate API keys
- [ ] Update authentication headers
- [ ] Change base URL to `api.tomba.io/v1`
- [ ] Update field mappings for person data
- [ ] Update field mappings for company data
- [ ] Add handling for new fields (verification, technologies, etc.)
- [ ] Update error handling
- [ ] Test with sample emails
- [ ] Update type definitions/interfaces
- [ ] Test batch operations
- [ ] Deploy to staging
- [ ] Monitor API responses
- [ ] Deploy to production
- [ ] Remove Clearbit dependencies

---

## Testing

### Comprehensive Test Suite

```javascript
const assert = require("assert");

describe("Combined API Migration", () => {
    it("should return combined data for valid email", async () => {
        const data = await getCombined("john@stripe.com");

        // Person assertions
        assert(data.person.name);
        assert(data.person.position);
        assert(data.person.emailValid !== undefined);

        // Company assertions
        assert(data.company.name);
        assert(data.company.industry);
        assert(data.company.emailCount > 0);
    });

    it("should return null for non-existent email", async () => {
        const data = await getCombined("notexist@example12345.com");
        assert.strictEqual(data, null);
    });

    it("should include email verification", async () => {
        const data = await getCombined("valid@stripe.com");
        assert(data.person.emailValid === true);
        assert(data.person.emailScore > 90);
    });

    it("should include technology stack", async () => {
        const data = await getCombined("john@shopify.com");
        assert(Array.isArray(data.company.technologies));
        assert(data.company.technologies.length > 0);
    });

    it("should include similar companies", async () => {
        const data = await getCombined("john@salesforce.com");
        assert(Array.isArray(data.company.competitors));
    });

    it("should handle batch processing", async () => {
        const emails = [
            "john@stripe.com",
            "jane@shopify.com",
            "bob@salesforce.com",
        ];

        const results = await enrichLeadsBatch(emails);
        assert.equal(results.length, emails.length);
        assert(results.some((r) => r.data !== null));
    });
});
```

---

## Next Steps

- [Company API Migration](/migration/clearbit/company-api) - Company-only enrichment
- [Person API Migration](/migration/clearbit/person-api) - Person-only enrichment
- [Migration Overview](/migration/clearbit) - Full Clearbit replacement guide
- [Full API Documentation](https://docs.tomba.io)

---

**Ready to migrate from Clearbit?**

Get started with a free Tomba account - includes 50 free searches and 500 free verifications!

[Start Migration →](https://app.tomba.io/auth/register)
