Search by

Official PHP SDK for the CompanyEnrich API: company and people enrichment, search, lookalikes, and work emails.

v1.0.0 2026-11-09 23:33 UTC

This package is auto-updated.

Last update: 2026-11-09 23:34:34 UTC


README

Packagist

The official PHP client for the CompanyEnrich API: company and people enrichment, filter, semantic and lookalike search, work emails, and bulk jobs.

It requires PHP 8.1 or newer with the curl and json extensions, and has no Composer dependencies.

Install

composer require companyenrich/sdk

Quick start

Create an API key in your dashboard and keep it secret.

<?php

require 'vendor/autoload.php';

use CompanyEnrich\Client;

// Reads COMPANYENRICH_API_KEY from the environment, or pass new Client('your-api-key').
$client = new Client();

$company = $client->companies->enrich('stripe.com');
echo $company['name'] ?? '', ' ', $company['employees'] ?? '', PHP_EOL;

Responses are plain associative arrays with the API's field names. Optional fields can be absent or null, so read them with ??.

Arguments can be passed by name, as in these examples. Arrays you pass, such as filter, use the API's own field names.

Companies

// Add paid data to a lookup. Each expansion costs extra credits.
$detailed = $client->companies->enrich('stripe.com', expand: ['technologies', 'workforce']);

// Up to 50 domains at once. Check each item's status.
$batch = $client->companies->enrichBatch(['stripe.com', 'adyen.com']);
foreach ($batch['items'] as $item) {
    if ($item['status'] === 'found') {
        echo $item['domain'], ' ', $item['company']['name'] ?? '', PHP_EOL;
    }
}

// Filter search
$page = $client->companies->search(
    filter: ['country' => 'US', 'employees' => ['51-200', '201-500'], 'categories' => 'saas'],
    pageSize: 25,
);
echo count($page['items']), ' ', $page['nextCursor'] ?? 'last page', PHP_EOL;

// Free count of the same query
$total = $client->companies->count(filter: ['country' => 'US']);
echo $total['count'], PHP_EOL;

People

$results = $client->people->search(
    filter: ['domain' => 'stripe.com', 'seniority' => ['c-suite', 'founder']],
    pageSize: 10,
);

$person = $client->people->enrich('jane@stripe.com');

$lookup = $client->people->findEmail($person['id'], domain: 'stripe.com');
if ($lookup['status'] === 'found') {
    echo $lookup['email'], ' ', $lookup['certainty'] ?? '', PHP_EOL;
}

Semantic and lookalike search

Semantic search finds companies or people from a plain-language description. Lookalike search finds companies similar to ones you already know. A company search can use either mode but not both, and either can be combined with filter. searchAll accepts the same arguments as search.

Search Credits per result
Companies by filters 1
Companies by description 2
Lookalike companies 5
People, in any mode 2

Paid expansions cost extra, and an empty result costs one result's base price. To try a query for free, pass the same arguments to companies->preview() or people->preview(), which need a plan with previews.

Companies by description

By default, the API reads attributes such as location and size from your query and applies them. interpretation shows what it inferred.

$results = $client->companies->search(
    semantic: ['query' => 'saas companies in Turkey'],
    pageSize: 25,
);

foreach ($results['items'] as $company) {
    echo $company['domain'], ' ', $company['description'] ?? '', PHP_EOL;
}

print_r($results['interpretation']['filter'] ?? []); // Inferred filters, such as the country
print_r($results['interpretation']['boost'] ?? []); // Inferred ranking boosts, such as the category

Filters you pass take priority over inferred values of the same kind. Set 'autoFilter' => false to turn inference off, or set weight (0 to 1) to control how much semantic similarity affects ranking.

$filtered = $client->companies->search(
    semantic: ['query' => 'AI tools for hospitals', 'autoFilter' => false],
    filter: ['country' => 'US', 'employees' => ['51-200', '201-500']],
    pageSize: 25,
);

Lookalike companies

Pass 1 to 10 seed domains. Use minScore (0 to 1) to require a minimum similarity.

$similar = $client->companies->search(
    lookalike: ['domains' => ['stripe.com', 'adyen.com']],
    filter: ['country' => 'US', 'employees' => ['51-200', '201-500']],
    pageSize: 25,
);

foreach ($similar['items'] as $company) {
    echo $company['domain'], ' ', $similar['scores'][$company['id']] ?? '', PHP_EOL; // Scores are not probabilities
}

Keep results in the order returned rather than sorting them by score.

People by description

The API turns your query into person filters, such as role, seniority and country, plus a description of their current companies. interpretation shows both. A query it can't infer any filters from is rejected with an error, so include something concrete like a role or location.

$people = $client->people->search(
    semantic: ['query' => 'marketing leaders at Series B fintech startups in London'],
    pageSize: 25,
);

foreach ($people['items'] as $person) {
    echo $person['name'] ?? '', ' ', $person['position'] ?? '', PHP_EOL;
}

print_r($people['interpretation'] ?? []); // The inferred filters and company description

People at matching companies

Use company to find people by the kind of company they work at. These searches cover people at the 1,000 best-matching companies.

// Executives at companies matching a description
$executives = $client->people->search(
    filter: ['seniority' => ['c-suite', 'vp']],
    company: ['semantic' => ['query' => 'AI tools for hospitals']],
    pageSize: 25,
);

// Executives at companies similar to your best customers
$similarTeams = $client->people->search(
    filter: ['seniority' => ['c-suite', 'vp']],
    company: ['lookalike' => ['domains' => ['stripe.com', 'adyen.com']]],
    pageSize: 25,
);

Pagination

search() returns one page. The *All methods return a generator that requests pages as you consume them.

foreach ($client->companies->searchAll(filter: ['country' => 'TR'], pageSize: 100) as $company) {
    echo $company['domain'], PHP_EOL;
}

These are available as companies->searchAll(), people->searchAll(), batches->listAll(), and geo->searchCountriesAll(), geo->searchStatesAll() and geo->searchCitiesAll().

Search credits are charged for every result on a fetched page, even if you stop iterating partway through it. Set pageSize close to the number of results you plan to use.

Bulk jobs

Bulk enrichment and search exports run in the background.

$job = $client->companies->bulkEnrichment->create(['stripe.com', 'adyen.com']);

$client->batches->waitForCompletion(
    $job['id'],
    onProgress: fn (array $batch) => print($batch['progress']['succeeded'] . '/' . $batch['progress']['total'] . PHP_EOL),
);

foreach ($client->companies->bulkEnrichment->stream($job['id']) as $record) {
    if ($record['success']) {
        echo $record['domain'], ' ', $record['company']['name'] ?? '', PHP_EOL;
    }
}

companies->bulkEnrichment, companies->searchExport and people->searchExport share the same methods:

Method Returns
create(...) The new batch's status
stream($id) A generator of records, best for large jobs
results($id) One JSON document with every record
csv($id) The CSV as a string
csvChunks($id) A generator of raw bytes, for writing to a file

Errors

Failed requests throw a subclass of CompanyEnrich\Exception\ApiException, which carries status, title, detail, errors (field-level validation messages) and traceId (include it when contacting support).

use CompanyEnrich\Exception\ApiException;
use CompanyEnrich\Exception\NotFoundException;
use CompanyEnrich\Exception\PaymentRequiredException;

try {
    $client->companies->enrich('example.invalid');
} catch (NotFoundException) {
    echo 'No company found', PHP_EOL;
} catch (PaymentRequiredException) {
    echo 'Out of credits, or not included in this plan', PHP_EOL;
} catch (ApiException $error) {
    echo $error->status, ' ', $error->detail, ' ', $error->traceId, PHP_EOL;
}
Status Class
400 BadRequestException
401 AuthenticationException
402 PaymentRequiredException
403 PermissionDeniedException
404 NotFoundException
422 ValidationException
429 RateLimitException
5xx ServerException
Other ApiException

ApiConnectionException and ApiTimeoutException cover requests that never received a complete response. Everything above extends CompanyEnrichException.

Retries and timeouts

Failed requests are retried up to twice with exponential backoff.

Failure GET and free POST endpoints Other POST endpoints
429 rate limit Retried Retried
408, 5xx, connection error, timeout Retried Not retried

POST requests that consume credits or start a job are not repeated after an ambiguous failure, because the first attempt may already have been charged or created a batch. A download that fails partway through is never restarted, so you won't receive duplicate records.

Configuration

$configured = new Client('your-api-key', timeout: 30.0, maxRetries: 4);

// Every method accepts per-request options.
$configured->me(options: ['timeout' => 5.0, 'maxRetries' => 0]);
Option Default Description
apiKey COMPANYENRICH_API_KEY Your API key
baseUrl https://api.companyenrich.com API origin, for proxies
timeout 120.0 Timeout in seconds. null disables it. For downloads, it applies when no data arrives
maxRetries 2 Retries after the first attempt
defaultHeaders none Headers added to every request

API reference

SDK method Endpoint
companies->get($id) GET /v2/companies/{companyId}
companies->enrich($domain) GET /v2/companies/enrich
companies->enrichByProperties(...) POST /v2/companies/enrich
companies->enrichBatch($domains) POST /v2/companies/enrich/batch
companies->autocomplete($query) GET /v2/companies/autocomplete
companies->workforce(...) GET /v2/companies/workforce
companies->search(...) POST /v2/companies/search
companies->count(...) POST /v2/companies/search/count
companies->preview(...) POST /v2/companies/search/preview
companies->bulkEnrichment /v2/companies/enrich/bulk
companies->searchExport /v2/companies/search/export
people->get($id) GET /v2/people/{personId}
people->enrich($email) POST /v2/people/enrich
people->findEmail($id) GET /v2/people/{personId}/email
people->search(...) POST /v2/people/search
people->count(...) POST /v2/people/search/count
people->preview(...) POST /v2/people/search/preview
people->searchExport /v2/people/search/export
batches->list(...) GET /v2/batches
batches->get($id) GET /v2/batches/{id}
batches->cancel($id) POST /v2/batches/{id}/cancel
batches->rawResults($id) GET /v2/batches/{id}/results
batches->waitForCompletion($id) Polls GET /v2/batches/{id}
geo->regions() GET /v2/geo/regions
geo->country($code) GET /v2/geo/countries/{countryCode}
geo->searchCountries(...) POST /v2/geo/countries
geo->searchStates(...) POST /v2/geo/states
geo->searchCities(...) POST /v2/geo/cities
industries->list() GET /v2/industries
keywords->autocomplete($query) GET /v2/keywords/autocomplete
technologies->autocomplete($query) GET /v2/technologies/autocomplete
positions->autocomplete($query) GET /v2/positions/autocomplete
me() GET /v2/me

See the API documentation for parameters and credit costs.

Development

composer update
vendor/bin/phpunit
vendor/bin/phpstan analyse

To release, bump VERSION in src/Version.php and push to main. The release workflow runs the checks, creates the git tag, and tells Packagist about it if the version is new.

License

MIT