> Reverse Contact API documentation. This is the Markdown copy of https://docs.reversecontact.com/endpoints/search-organizations
> Every page is listed at https://docs.reversecontact.com/llms.txt, every endpoint with its credit cost and status at https://docs.reversecontact.com/catalog.json

# Organization search

> Search the database for organizations with filters and get full organization profiles back with cursor pagination.

- Endpoint: `POST https://api.reversecontact.com/v2/search/organizations`
- Credits: 1 credit per organization delivered. A page with no organization is free.
- Answer: synchronous.
- Last verified: 2026-10-16

## Introduction

The Organization Search endpoint returns full organization profiles matching a combination of filters. Filters are combined with **AND** logic. Results are drawn from our cache-based search index. This endpoint never triggers a live fetch.

Every result is a complete profile, the same shape the [Company profile](https://docs.reversecontact.com/endpoints/fetch-company) endpoint returns, so you do not need a second call to get the full data of a hit. Use the free [Organization search count](https://docs.reversecontact.com/endpoints/search-organizations-count) endpoint to check how many organizations match before you spend credits.

> Coming from `POST /v2/search/companies`? See [Migrating company search to organizations](https://docs.reversecontact.com/guides/migrating-search-companies-to-organizations).

## 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

Every filter is **optional**, but a request needs at least one, and filters are combined with **AND** logic. Scalar filters live in the table below; the nested-object filters (`employeeCountRange`, `foundedYear`, `followersCount`, `location`) are documented as expandable field items right after, and pagination (`cursor`, `perPage`) closes the section.

| Name             | Type               | Required | Description                                                                |
| ---------------- | ------------------ | -------- | -------------------------------------------------------------------------- |
| `companyName`    | string             | No       | Filter by organization name                                                |
| `companyDomain`  | string             | No       | Filter by website domain (e.g. `acme.com`). Disposable domains are rejected with `422` |
| `industry`       | string \| string[] | No       | Industry, or array of industries: an organization matches if its industry is any of them. See [Industry values](https://docs.reversecontact.com/guides/industries) for the full list |
| `specialities`   | string \| string[] | No       | Speciality, or array of specialities: an organization matches if it lists any of them. See [Specialities](#specialities) |
| `maxDataAgeDate` | string             | No       | ISO 8601 datetime. Only returns organizations refreshed after this date. Needs at least one other filter |
| `cursor`         | string             | No       | Opaque cursor returned as `metadata.nextCursor`; request the next page     |
| `perPage`        | number             | No       | Organizations per page (1 to 100, default `25`)                            |

> **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. Each list filter accepts at most 20 values. See [Search filter validation](https://docs.reversecontact.com/guides/errors-retries#search-filter-validation).

> **At least one filter:** a body with only `cursor` and `perPage` returns HTTP `422` with `NO_FILTER_PROVIDED`. `maxDataAgeDate` narrows a search but selects nothing on its own, so a request whose only filter is `maxDataAgeDate` returns `NO_FILTER_PROVIDED` too. `followersCount` sent as the only filter needs both `min` and `max`, otherwise the request returns `INCOMPLETE_RANGE_FILTER`. None of these requests costs credits.

> **Note:** `industry` is matched exactly against the [industry list](https://docs.reversecontact.com/guides/industries). Case, surrounding spaces, accents and `&` versus `and` are normalized for you, and any other unlisted value returns HTTP `422` with `INVALID_FILTER_VALUE` and up to three suggested values, never an empty page.

**employeeCountRange** (RangeFilter): Filter by employee count. Both bounds are optional and inclusive, from 0 to 1,000,000,000. 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.data` array.

| 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.

**foundedYear** (RangeFilter): Filter by founding year. Both bounds are optional and inclusive, from 0 to 2100.

| Field | Type   | Description                          |
| ----- | ------ | ------------------------------------ |
| `min` | number | Earliest founding year, inclusive    |
| `max` | number | Latest founding year, inclusive      |

**followersCount** (RangeFilter): Filter by follower count. Both bounds are optional and inclusive, from 0 to 1,000,000,000.

| Field | Type   | Description              |
| ----- | ------ | ------------------------ |
| `min` | number | Minimum value, inclusive |
| `max` | number | Maximum value, inclusive |

> **Ranges:** bounds are whole numbers, `min` must not be greater than `max`, and a bound outside the accepted interval (for example a `foundedYear.max` of `2200`) returns HTTP `422` with `INVALID_RANGE` and the accepted maximum in `error.details`. The request costs 0 credits.

**location** (LocationFilter): Headquarters location filter. Each sub-field is optional and accepts a single string or an array of strings (OR matching), with a maximum of 20 values per sub-field. Beyond that, the request returns HTTP `422` with `FILTER_VALUES_LIMIT_EXCEEDED` and costs 0 credits: split it into several searches. There is no `area` field here (unlike the person location filter).

| Field         | Type               | Description                                                       |
| ------------- | ------------------ | ----------------------------------------------------------------- |
| `country`     | string \| string[] | Country name in English (e.g. `"Germany"`). An ISO code sent here is rejected with `422`: put codes in `countryCode` |
| `countryCode` | string \| string[] | ISO 3166-1 alpha-2 country code (e.g. `"DE"`). Values outside the list are rejected with `422` |
| `region`      | string \| string[] | State or region                                                   |
| `city`        | string \| string[] | City, in its local spelling (e.g. `"München"`)                    |

## Specialities

`specialities` matches the specialities an organization lists on its profile, such as `"SaaS"` or `"Machine Learning"`. Matching is word-based, not exact: case and accents are ignored, and every word of a value must appear among the organization's specialities, so `"SaaS"` also matches an organization that lists `"B2B SaaS"`. A list is OR-matched: an organization matches when it lists at least one of the requested specialities.

**Example**

```bash [cURL]
curl -X POST https://api.reversecontact.com/v2/search/organizations \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "specialities": ["SaaS", "Machine Learning"],
    "foundedYear": { "min": 2015 },
    "location": { "countryCode": "DE", "city": "München" },
    "perPage": 25
  }'
```

> **Tip:** a very broad query, such as one common industry on its own, has to rank a very large set of organizations and can be slow or time out with HTTP `504`, which costs 0 credits. Pair it with an employee count range, a founding year or a precise location to keep the search fast.

## Pagination and credits

Organization search uses **cursor pagination**. Each response returns up to `perPage` organizations (1 to 100, default `25`) and a `metadata.nextCursor` value. Pass that value back as `cursor` in the next request, with the same filters, to get the following page; `nextCursor` is `null` on the last page. The cursor is opaque: send it back unchanged, without parsing or altering it. `perPage` may change from one page to the next.

There is no `page` parameter. A request that sends `page` returns HTTP `422` with `error.code` `UNKNOWN_FIELD`, and the error names `cursor` as its replacement.

The cursor does not expire, and a long pause between two pages is fine: the next page picks up where the previous one stopped. If organizations were refreshed in the meantime, one of them can occasionally repeat or be skipped at the page boundary. When the last page is exactly full, its `nextCursor` is not `null` yet, and the request it leads to returns an empty page, which costs 0 credits.

An invalid cursor returns HTTP `400` with the message `Invalid or expired cursor`. This happens when a filter is changed between two pages (keep the filters identical and only add the `cursor`), or when the cursor is malformed (truncated or altered).

`perPage` above `100` returns HTTP `422` with `error.code` `PAGINATION_LIMIT_EXCEEDED`, and the `error.details` object identifies the field and maximum, for example `{ "field": "perPage", "max": 100 }`. A `perPage` that is not a whole number returns HTTP `422` with `error.code` `INVALID_PAGINATION`.

Organization search costs **1 credit per organization delivered**. Organizations removed for compliance reasons before the response are not billed. A page that returns 25 organizations costs 25 credits, and a page that returns no organization costs 0 credits. Before it runs, a request needs as many credits available as its `perPage`: with fewer, it returns HTTP `402` with `INSUFFICIENT_CREDITS` and costs nothing. Check the size of a query first with the free [Organization search count](https://docs.reversecontact.com/endpoints/search-organizations-count) endpoint, and lower `perPage` to control how many credits a single request can spend.

## 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** (CompanyFull[]): Full organization profiles returned for this page, wrapped under `data.data` in the envelope. Empty array if no match. Each item is the same full profile shape as the [Company profile](https://docs.reversecontact.com/endpoints/fetch-company) endpoint; the table below lists its fields.

| Field                | Type                       | Description                                                                |
| -------------------- | -------------------------- | -------------------------------------------------------------------------- |
| `id`                 | string                     | Reverse Contact company id (`com_...`). Use it as the `id` parameter of the [Company profile](https://docs.reversecontact.com/endpoints/fetch-company) endpoint |
| `companyName`        | string                     | Organization name                                                          |
| `linkedinId`         | string                     | Social company identifier                                                  |
| `publicId`           | string                     | Universal name / slug (the `/company/xxx` part of the URL)                 |
| `linkedinUrl`        | string \| null             | Full Social company URL                                                    |
| `employeesCount`     | number \| null             | Exact number of employees                                                  |
| `followersCount`     | number \| null             | Number of Social followers                                                 |
| `employeeCountRange` | EmployeeCountRange \| null | Employee count bracket                                                     |
| `websiteUrl`         | string \| null             | Official website URL                                                       |
| `tagline`            | string \| null             | Tagline / slogan                                                           |
| `description`        | string \| null             | Full description                                                           |
| `industry`           | string \| null             | Industry (e.g. `"Software Development"`)                                   |
| `phone`              | string \| null             | Phone number                                                               |
| `specialities`       | string[]                   | Specialities listed on the profile                                         |
| `logoUrl`            | string \| null             | Logo image URL                                                             |
| `foundedOn`          | FoundedOn \| null          | Founding year wrapper                                                      |
| `locations`          | Location[]                 | All office locations registered on the profile (can be empty)              |
| `backgroundUrl`      | string \| null             | Background / banner image URL                                              |

**headquarter** (CompanyHeadquarter): Headquarters location (`null` if unknown). The `location` filter matches these fields.

| Field            | Type           | Description                                  |
| ---------------- | -------------- | -------------------------------------------- |
| `street1`        | string \| null | Primary street address line                  |
| `street2`        | string \| null | Secondary street address line                |
| `postalCode`     | string \| null | Postal code                                  |
| `city`           | string \| null | City                                         |
| `geographicArea` | string \| null | State or region                              |
| `country`        | string \| null | Country ISO 3166 code (e.g. `"US"`, `"FR"`)  |

**employeeCountRange** (EmployeeCountRange): Employee count bracket (`null` if unknown). Note the `start` / `end` field names (different from the request `RangeFilter`, which uses `min` / `max`). For the top bracket (`10,001+`) there is no upper bound, and `end` is `1` as a sentinel: read any range where `end < start` as `start+` (unbounded). Use `employeesCount` 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 bracket is unbounded (`start+`, i.e. `10,001+`) |

**foundedOn** (FoundedOn): Founding year (`null` if unknown)

| Field  | Type   | Description     |
| ------ | ------ | --------------- |
| `year` | number | Four-digit year |

**locations** (Location[]): All office locations registered on the profile (can be empty)

| Field        | Type           | Description                                                                    |
| ------------ | -------------- | ------------------------------------------------------------------------------ |
| `street1`    | string \| null | Street address line 1                                                          |
| `street2`    | string \| null | Street address line 2                                                          |
| `postalCode` | string \| null | Postal / ZIP code                                                              |
| `city`       | string \| null | City                                                                           |
| `state`      | string \| null | State or region (named `state` here, not `geographicArea` as in `headquarter`) |
| `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                                                            |
| ------------ | -------------- | ---------------------------------------------------------------------- |
| `perPage`    | number         | Organizations requested per page                                       |
| `count`      | number         | Organizations returned in this page. Use [Organization search count](https://docs.reversecontact.com/endpoints/search-organizations-count) for the total matching set |
| `nextCursor` | string \| null | Opaque cursor for the next page, `null` on the last page               |

**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/organizations \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "industry": ["Software Development", "Technology, Information and Internet"],
    "employeeCountRange": { "min": 51, "max": 500 },
    "foundedYear": { "min": 2015 },
    "location": {
      "country": "United States",
      "city": ["San Francisco", "Austin"]
    },
    "perPage": 25
  }'
```
```javascript [JavaScript]
const searchPage = async (cursor) => {
  const response = await fetch("https://api.reversecontact.com/v2/search/organizations", {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      industry: ["Software Development", "Technology, Information and Internet"],
      employeeCountRange: { min: 51, max: 500 },
      foundedYear: { min: 2015 },
      location: { country: "United States", city: ["San Francisco", "Austin"] },
      perPage: 25,
      cursor
    })
  });

  return response.json();
};

const firstPage = await searchPage();
const organizations = firstPage.data.data;
const { count, nextCursor } = firstPage.data.metadata;
console.log(`Got ${count} organizations, credits used: ${firstPage.quotas.creditsConsumed}`);

if (nextCursor) {
  const secondPage = await searchPage(nextCursor);
  organizations.push(...secondPage.data.data);
}
```
```python [Python]
import requests

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

payload = response.json()
organizations = payload["data"]["data"]
metadata = payload["data"]["metadata"]
print(f"Got {metadata['count']} organizations, next cursor: {metadata['nextCursor']}")
```

## Example response

**Example Response**

```json [200 - Success]
{
  "success": true,
  "data": {
    "data": [
      {
        "id": "com_01jbd5vewyenebxgaz869ffe5t",
        "companyName": "Acme Corp",
        "linkedinId": "12345678",
        "publicId": "acme",
        "linkedinUrl": "https://www.social.com/company/acme",
        "employeesCount": 452,
        "followersCount": 28512,
        "employeeCountRange": {
          "start": 201,
          "end": 500
        },
        "websiteUrl": "https://www.acmecorp.com",
        "tagline": "Building the future of work",
        "description": "Acme Corp builds collaboration software for enterprise teams. Founded in 2016, it serves over 10,000 customers worldwide.",
        "industry": "Software Development",
        "phone": "+1 (415) 555-0123",
        "specialities": ["SaaS", "Enterprise Software", "Collaboration", "API Integration"],
        "headquarter": {
          "street1": "1 Market Street",
          "street2": "Suite 400",
          "postalCode": "94105",
          "city": "San Francisco",
          "geographicArea": "California",
          "country": "US"
        },
        "logoUrl": "https://media.licdn.com/dms/image/v2/C4D0BAQ.../logo.jpg",
        "foundedOn": {
          "year": 2016
        },
        "locations": [
          {
            "street1": "1 Market Street",
            "street2": "Suite 400",
            "postalCode": "94105",
            "city": "San Francisco",
            "state": "California",
            "country": "US"
          },
          {
            "street1": "500 Congress Avenue",
            "street2": null,
            "postalCode": "78701",
            "city": "Austin",
            "state": "Texas",
            "country": "US"
          }
        ],
        "backgroundUrl": "https://media.licdn.com/dms/image/v2/D5616AQ.../background.jpg"
      }
    ],
    "metadata": {
      "perPage": 25,
      "count": 1,
      "nextCursor": null
    }
  },
  "error": null,
  "metadata": {
    "requestId": "req_o1r2g3s4e5a6",
    "executionTimeMs": 320
  },
  "quotas": {
    "creditsConsumed": 1,
    "workspace": {
      "id": "ws_abc123",
      "credits": {
        "total": 10000,
        "used": 4522,
        "left": 5478
      },
      "dailyLimit": null,
      "minuteRateLimit": {
        "limit": 60,
        "used": 5,
        "left": 55,
        "nextReset": "2026-10-16T14:16:00.000Z"
      },
      "hasUnlimitedCredits": false,
      "allowDailyOvercost": false
    },
    "key": {
      "id": "key_abc123",
      "dailyLimit": null,
      "minuteRateLimit": {
        "limit": 60,
        "used": 5,
        "left": 55,
        "nextReset": "2026-10-16T14:16:00.000Z"
      }
    }
  }
}
```
