# Company search

> Search the database for company profiles using multiple filters.

## Introduction

The Company Search endpoint returns paginated company profiles matching a combination of filters. All filters are optional and combined with **AND** logic. Results are drawn from our cache-based search index. This endpoint never triggers a live fetch.

Use this endpoint to discover companies that match criteria like industry, size, or headquarters location, then chain into [Profile](https://docs.reversecontact.com/endpoints/fetch-company) for the complete data of any hit.

## Authorization

| Name     | Type   | Required | Description                                                                                         |
| -------- | ------ | -------- | --------------------------------------------------------------------------------------------------- |
| `apikey` | string | Yes      | Your API key from the developer dashboard. Pass it via `Authorization: Bearer YOUR_API_KEY` header. |

## Request body

All filters are **optional** and combined with **AND** logic. Scalar filters live in the table below; the two nested-object filters (`employeeCountRange`, `location`) are documented as expandable field items right after.

| Name             | Type               | Required | Description                                                                |
| ---------------- | ------------------ | -------- | -------------------------------------------------------------------------- |
| `companyName`    | string             | No       | Filter by company name                                                     |
| `companyDomain`  | string             | No       | Filter by company website domain. Disposable domains are rejected with `422` |
| `industry`       | string \| string[] | No       | Industry name or array of industries. See [Industry values](https://docs.reversecontact.com/guides/industries) for the full list |
| `maxDataAgeDate` | string             | No       | ISO 8601 datetime. Only returns companies refreshed after this date        |
| `page`           | number             | No       | Page number (1 to 50, default `1`)                                        |
| `perPage`        | number             | No       | Results per page (1 to 100, default `100`)                                |

> **Filter validation:** filters are validated before the query runs. Unknown field names, blank values, values outside a closed list (`industry`, `location.countryCode`) and inconsistent ranges return HTTP `422` with the offending field, the reason and suggested values, and **cost 0 credits**. Unknown fields are rejected rather than ignored, because an ignored filter would return unfiltered results. See [Search filter validation](https://docs.reversecontact.com/guides/errors-retries#search-filter-validation).

> **Pagination limits:** `page` must be between `1` and `50`, and `perPage` must be between `1` and `100`, inclusive, so pagination reaches the first 5,000 matching records. Requests above either limit return HTTP `422` with `error.code` `PAGINATION_LIMIT_EXCEEDED`. The `error.details` object identifies the field and maximum, for example `{ "field": "page", "max": 50 }` or `{ "field": "perPage", "max": 100 }`. `pageNumber` and `total` still describe the whole matching set, so a search can report more than 50 pages while only the first 50 are retrievable. Narrow your filters or request fewer results per page.

**employeeCountRange** (RangeFilter): Filter by employee count range. Both bounds are optional and inclusive. Headcount is stored as fixed size brackets (`1`, `2-10`, `11-50`, `51-200`, `201-500`, `501-1000`, `1001-5000`, `5001-10000`, `10001+`), not as an exact number, and a range only matches a bracket it fully contains. Send bracket boundaries, otherwise the range contains no bracket, matches nothing and returns an empty `data` array. The request is still billed, so check your bounds against the list below.

| Field | Type   | Description              |
| ----- | ------ | ------------------------ |
| `min` | number | Lower bound, inclusive. Must be a bracket start: `1`, `2`, `11`, `51`, `201`, `501`, `1001`, `5001`, `10001` |
| `max` | number | Upper bound, inclusive. Must be a bracket end: `1`, `10`, `50`, `200`, `500`, `1000`, `5000`, `10000`. Omit it to include `10001+` |

> **Examples:** `{ "min": 11, "max": 50 }` matches the `11-50` bracket. `{ "min": 11, "max": 200 }` spans `11-50` and `51-200`. `{ "min": 10001 }` matches `10001+`. `{ "min": 11, "max": 20 }` returns an empty array, because no whole bracket fits between 11 and 20.

**location** (LocationFilter): Multi-field location filter. Each sub-field is optional and accepts a single string or an array of strings (OR matching). Note: there is no `area` field here (unlike the person location filter).

| Field         | Type               | Description                             |
| ------------- | ------------------ | --------------------------------------- |
| `country`     | string \| string[] | Country name (e.g. `"United States"`)   |
| `countryCode` | string \| string[] | ISO 3166 country code (e.g. `"US"`)     |
| `region`      | string \| string[] | State or region                         |
| `city`        | string \| string[] | City                                    |

## Response structure

> **Note:** This endpoint uses a **nested envelope**. The top-level V2 `data` field wraps an inner search payload that contains the actual results array (`data.data`) and the pagination info (`data.metadata`). See the example response for the exact shape.

| Field     | Type    | Description                                      |
| --------- | ------- | ------------------------------------------------ |
| `success` | boolean | `true` if the request was processed successfully |
| `error`   | `null`  | Always `null` on success                         |

**data.data** (CompanySearchResult[]): Array of matching company profiles wrapped under `data.data` in the envelope. Empty array if no match.

| Field                | Type                       | Description                                                                |
| -------------------- | -------------------------- | -------------------------------------------------------------------------- |
| `id`                 | string                     | ReverseContact company id (`com_...`). Use it as the `id` parameter of the [Company Profile endpoint](https://docs.reversecontact.com/endpoints/fetch-company) |
| `name`               | string                     | Company name                                                               |
| `publicId`           | string                     | Universal name / slug (the `/company/xxx` part of the URL)                 |
| `linkedinId`         | string                     | Social company identifier                                                  |
| `linkedinUrl`        | string \| null             | Full Social company URL                                                    |
| `websiteUrl`         | string \| null             | Official company website URL                                               |
| `industry`           | string \| null             | Industry (e.g. `"Software Development"`)                                   |
| `tagline`            | string \| null             | Tagline / slogan                                                           |
| `employeesCount`     | number                     | Exact number of employees                                                  |
| `followersCount`     | number                     | Number of Social followers                                                 |
| `employeeCountRange` | EmployeeCountRange \| null | Employee count bracket. `null` if unknown                                  |
| `companyUpdateDate`  | string \| null             | When ReverseContact last refreshed this record (ISO 8601)                  |

**employeeCountRange** (EmployeeCountRange): Employee count bracket (`null` if unknown). Note the `start` / `end` field names (different from the request `RangeFilter` which uses `min` / `max`). Buckets mirror LinkedIn's native `staffCountRange`: `1`, `2–10`, `11–50`, `51–200`, `201–500`, `501–1,000`, `1,001–5,000`, `5,001–10,000`, `10,001+`. For the top bucket (`10,001+`), LinkedIn has no upper bound and returns `end: 1` as a sentinel: interpret any range where `end < start` as `start+` (unbounded). Use `employeeCount` for the exact headcount when available.

| Field   | Type   | Description                                                                              |
| ------- | ------ | ---------------------------------------------------------------------------------------- |
| `start` | number | Lower bound of the range                                                                 |
| `end`   | number | Upper bound of the range. If `end < start`, the bucket is unbounded (`start+`, i.e. `10,001+`) |

> **Example (10,001+ bucket):** `{ "start": 10001, "end": 1 }`. Since `end` (1) < `start` (10001), this means the company has 10,001 or more employees. No upper bound is available for this bucket.

**location** (CompanySearchLocation): Primary location of the company (always present, fields may be `null`)

| Field     | Type           | Description                              |
| --------- | -------------- | ---------------------------------------- |
| `city`    | string \| null | City                                     |
| `country` | string \| null | Country ISO 3166 code (e.g. `"US"`, `"FR"`) |

**data.metadata** (SearchPagination): Pagination metadata wrapped under `data.metadata` in the envelope.

| Field         | Type   | Description                                         |
| ------------- | ------ | --------------------------------------------------- |
| `currentPage` | number | Current page number (1-indexed)                     |
| `pageNumber`  | number | Total number of pages available                     |
| `perPage`     | number | Results per page                                    |
| `total`       | number | Total number of matching records across all pages   |

**quotas** (V2QuotaInfo): Credits and rate limit usage after this request

| Field             | Type   | Description                      |
| ----------------- | ------ | -------------------------------- |
| `creditsConsumed` | number | Credits consumed by this request |

**workspace** (Workspace): Workspace-level limits

| Field                 | Type    | Description                                    |
| --------------------- | ------- | ---------------------------------------------- |
| `id`                  | string  | Your workspace identifier                      |
| `hasUnlimitedCredits` | boolean | Whether workspace credits are unlimited        |
| `allowDailyOvercost`  | boolean | Whether the workspace may exceed its daily cap |

**credits** (Credits): Credit balance

| Field   | Type   | Description                |
| ------- | ------ | -------------------------- |
| `total` | number | Total credits in your plan |
| `used`  | number | Credits consumed so far    |
| `left`  | number | Remaining credits          |

**dailyLimit** (DailyLimit): Daily request limit (`null` if not configured)

| Field       | Type   | Description                             |
| ----------- | ------ | --------------------------------------- |
| `limit`     | number | Maximum requests per day                |
| `used`      | number | Requests made today                     |
| `left`      | number | Remaining requests today                |
| `nextReset` | string | When the daily window resets (ISO 8601) |

**minuteRateLimit** (MinuteRateLimit): Per-minute rate limit

| Field       | Type   | Description                              |
| ----------- | ------ | ---------------------------------------- |
| `limit`     | number | Maximum requests per minute              |
| `used`      | number | Requests made in the current minute      |
| `left`      | number | Remaining requests in the current minute |
| `nextReset` | string | When the minute window resets (ISO 8601) |

**key** (ApiKey): API key-level limits (`null` when request is from dashboard)

| Field | Type   | Description        |
| ----- | ------ | ------------------ |
| `id`  | string | API key identifier |

**dailyLimit** (KeyDailyLimit): Daily limit for this key (`null` if not configured)

| Field       | Type   | Description                             |
| ----------- | ------ | --------------------------------------- |
| `limit`     | number | Maximum requests per day                |
| `used`      | number | Requests made today                     |
| `left`      | number | Remaining requests today                |
| `nextReset` | string | When the daily window resets (ISO 8601) |

**minuteRateLimit** (KeyMinuteRateLimit): Minute rate limit for this key

| Field       | Type   | Description                              |
| ----------- | ------ | ---------------------------------------- |
| `limit`     | number | Maximum requests per minute              |
| `used`      | number | Requests made in the current minute      |
| `left`      | number | Remaining requests in the current minute |
| `nextReset` | string | When the minute window resets (ISO 8601) |

**metadata** (V2ResponseMetadata): Request tracking information (not to be confused with `data.metadata` which is pagination)

| Field             | Type   | Description                          |
| ----------------- | ------ | ------------------------------------ |
| `requestId`       | string | Unique identifier for this request   |
| `executionTimeMs` | number | Total execution time in milliseconds |

## Code examples

**Code Examples**

```bash [cURL]
curl -X POST https://api.reversecontact.com/v2/search/companies \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "industry": ["Software Development", "Technology, Information and Internet"],
    "employeeCountRange": { "min": 50, "max": 500 },
    "location": {
      "country": "United States",
      "city": ["San Francisco", "Austin"]
    },
    "page": 1,
    "perPage": 100
  }'
```
```javascript [JavaScript]
const response = await fetch("https://api.reversecontact.com/v2/search/companies", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    industry: ["Software Development", "Technology, Information and Internet"],
    employeeCountRange: { min: 50, max: 500 },
    location: {
      country: "United States",
      city: ["San Francisco", "Austin"]
    },
    page: 1,
    perPage: 100
  })
});

const payload = await response.json();
const results = payload.data.data;
const pagination = payload.data.metadata;
console.log(`Found ${pagination.total} companies, showing ${results.length} on page ${pagination.currentPage}`);
```
```python [Python]
import requests

response = requests.post(
    "https://api.reversecontact.com/v2/search/companies",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json"
    },
    json={
        "industry": ["Software Development", "Technology, Information and Internet"],
        "employeeCountRange": {"min": 50, "max": 500},
        "location": {
            "country": "United States",
            "city": ["San Francisco", "Austin"]
        },
        "page": 1,
        "perPage": 100
    }
)

payload = response.json()
results = payload["data"]["data"]
pagination = payload["data"]["metadata"]
print(f"Found {pagination['total']} companies, showing {len(results)} on page {pagination['currentPage']}")
```

## Example response

**Example Response**

```json [200 - Success]
{
  "success": true,
  "data": {
    "data": [
      {
        "id": "com_01jbd5vewyenebxgaz869ffe5t",
        "name": "Acme Corp",
        "publicId": "acme-corp",
        "linkedinId": "12345678",
        "linkedinUrl": "https://www.social.com/company/acme-corp",
        "employeesCount": 452,
        "followersCount": 28512,
        "employeeCountRange": {
          "start": 201,
          "end": 500
        },
        "websiteUrl": "https://www.acmecorp.com",
        "tagline": "Building the future of work",
        "industry": "Software Development",
        "companyUpdateDate": "2026-03-18T09:12:44.000Z",
        "location": {
          "city": "San Francisco",
          "country": "US"
        }
      },
      {
        "id": "com_01k2f8aq3xvznebxgaz869ffe5t",
        "name": "Globex Inc.",
        "publicId": "globex-inc",
        "linkedinId": "98765432",
        "linkedinUrl": "https://www.social.com/company/globex-inc",
        "employeesCount": 187,
        "followersCount": 12340,
        "employeeCountRange": {
          "start": 51,
          "end": 200
        },
        "websiteUrl": "https://www.globex.com",
        "tagline": "Ship faster, ship smarter",
        "industry": "Software Development",
        "companyUpdateDate": "2026-02-27T14:40:05.000Z",
        "location": {
          "city": "Austin",
          "country": "US"
        }
      }
    ],
    "metadata": {
      "currentPage": 1,
      "pageNumber": 2,
      "perPage": 100,
      "total": 184
    }
  },
  "error": null,
  "metadata": {
    "requestId": "req_c1o2m3p4a5n6",
    "executionTimeMs": 280
  },
  "quotas": {
    "creditsConsumed": 1,
    "workspace": {
      "id": "ws_abc123",
      "credits": {
        "total": 10000,
        "used": 4523,
        "left": 5477
      },
      "dailyLimit": null,
      "minuteRateLimit": {
        "limit": 60,
        "used": 6,
        "left": 54,
        "nextReset": "2025-01-20T14:16:00.000Z"
      },
      "hasUnlimitedCredits": false,
      "allowDailyOvercost": false
    },
    "key": {
      "id": "key_abc123",
      "dailyLimit": null,
      "minuteRateLimit": {
        "limit": 60,
        "used": 6,
        "left": 54,
        "nextReset": "2025-01-20T14:16:00.000Z"
      }
    }
  }
}
```
