> Reverse Contact API documentation. This is the Markdown copy of https://docs.reversecontact.com/guides/migrating-search-companies-to-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

# Migrating company search to organizations

> Move company search calls from /v2/search/companies to /v2/search/organizations.

- Last verified: 2026-10-16

Company search has a new home: `POST /v2/search/organizations`. It returns full organization profiles instead of search hits, paginates with cursors instead of page numbers, accepts three new filters, and ships with a free count endpoint.

The previous endpoint, `POST /v2/search/companies`, is deprecated since **October 16, 2026**. It keeps working for now and no retirement date is announced yet, but it is no longer documented in the endpoint catalog. Once it is retired, calls to `/v2/search/companies` and `/v2/search/companies/available-fields` will return `410 Gone` with the error code `ENDPOINT_GONE`, `error.details.alternative` naming the endpoint to call instead and `error.details.docs` linking this guide. This guide covers everything that changes when you move to `/v2/search/organizations`.

## What changes

| Area | `POST /v2/search/companies` | `POST /v2/search/organizations` |
| ---- | --------------------------- | ------------------------------- |
| Path | `/v2/search/companies` | `/v2/search/organizations` |
| Results | Search hits (name, size, industry, city and country) | Full organization profiles, the same shape as the [Company profile](https://docs.reversecontact.com/endpoints/fetch-company) endpoint |
| Pagination | `page` and `perPage` (default `100`) | `cursor` and `perPage` (default `25`) |
| Metadata | `currentPage`, `pageNumber`, `perPage`, `total` | `perPage`, `count`, `nextCursor` |
| Total matches | `data.metadata.total` on every response | Free [count endpoint](https://docs.reversecontact.com/endpoints/search-organizations-count) |
| Pricing | 1 credit per page | 1 credit per organization delivered |
| Filters | `companyName`, `companyDomain`, `industry`, `employeeCountRange`, `location`, `maxDataAgeDate` | The same, plus `specialities`, `foundedYear` and `followersCount` |
| Available fields | `GET /v2/search/companies/available-fields` | `GET /v2/search/organizations/available-fields` |

## Paths

| Before | After |
| ------ | ----- |
| `POST /v2/search/companies` | `POST /v2/search/organizations` |
| `GET /v2/search/companies/available-fields` | `GET /v2/search/organizations/available-fields` |
| (no equivalent) | `POST /v2/search/organizations/count` |

The method and the `Authorization: Bearer YOUR_API_KEY` header stay the same. Only the path changes.

## Results are full profiles

The previous endpoint returned a light search hit, and the documented flow was to chain into the [Company profile](https://docs.reversecontact.com/endpoints/fetch-company) endpoint for the full data of a hit. Organization search returns complete profiles directly, so the second call is no longer needed.

Before, a hit looked like this:

```json
{
  "id": "com_01jbd5vewyenebxgaz869ffe5t",
  "name": "Acme Corp",
  "publicId": "acme",
  "linkedinId": "12345678",
  "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",
  "industry": "Software Development",
  "companyUpdateDate": "2026-03-18T09:12:44.000Z",
  "location": { "city": "San Francisco", "country": "US" }
}
```

After, each item of `data.data` is a full organization profile. Most fields keep their name (`id`, `publicId`, `linkedinId`, `linkedinUrl`, `employeesCount`, `followersCount`, `employeeCountRange`, `websiteUrl`, `tagline`, `industry`), three change, and the profile adds `description`, `phone`, `specialities`, `logoUrl`, `backgroundUrl`, `foundedOn` and `locations`:

| Search hit | Full profile |
| ---------- | ------------ |
| `name` | `companyName` |
| `location.city`, `location.country` | `headquarter.city`, `headquarter.country` (still an ISO code), next to the street, postal code and region of the headquarters |
| `companyUpdateDate` | Not part of the full profile. Filter on freshness with `maxDataAgeDate`, or read `lastUpdate` from the free [Company profile status](https://docs.reversecontact.com/endpoints/check-company) endpoint |

If you chained Company profile calls after a search, drop them: the data is already in the search response.

## Pagination moves from pages to cursors

The previous endpoint used `page` (1 to 50) and `perPage` (1 to 100, default `100`), and reported `currentPage`, `pageNumber` and `total` in `data.metadata`. Organization search uses cursor pagination:

- `perPage` stays, with a new default of `25` (still 1 to 100).
- `page` is gone, and sending it returns `422` with `UNKNOWN_FIELD`. Send the `nextCursor` value from the previous response as `cursor` to get the next page, with the same filters.
- `nextCursor` is `null` on the last page.
- The cursor is opaque: send it back unchanged, without parsing or altering it.
- The 50-page limit is gone: a cursor reaches every matching organization.

Before:

```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", page: 2, perPage: 100 })
});

const payload = await response.json();
const hits = payload.data.data;
console.log(`Page ${payload.data.metadata.currentPage} of ${payload.data.metadata.pageNumber}, ${payload.data.metadata.total} total`);
```

After:

```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", perPage: 25, cursor })
  });

  return response.json();
};

const page = await searchPage();
console.log(`${page.data.metadata.count} organizations in this page`);
if (page.data.metadata.nextCursor) {
  const nextPage = await searchPage(page.data.metadata.nextCursor);
}
```

## Totals move to the count endpoint

The previous `data.metadata.total` field is gone: `data.metadata.count` is the number of organizations in the current page, not the size of the whole matching set. To size a query, call the free [Organization search count](https://docs.reversecontact.com/endpoints/search-organizations-count) endpoint with the same filters:

```bash
curl -X POST https://api.reversecontact.com/v2/search/organizations/count \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "industry": "Software Development" }'
```

The response is `data.count`, the total number of matching organizations across all pages, and it costs 0 credits.

## Pricing changes

The previous endpoint charged 1 credit per page, whatever the page contained. Organization search charges **1 credit per organization delivered**: a page that returns 25 organizations costs 25 credits, and a page that returns no organization costs 0 credits. Rejected filters still cost 0 credits.

Three consequences for your migration:

- `perPage` is now a credit control. Lower it to cap what a single request can spend.
- A request needs as many credits available as its `perPage` before it runs, otherwise it returns `402` with `INSUFFICIENT_CREDITS`. Only the organizations delivered are then charged.
- Size queries first with the free count endpoint instead of reading `metadata.total` from a billed page.

## Filter changes

Existing filters keep their names and their values: `companyName`, `companyDomain`, `industry` (from the same [industry list](https://docs.reversecontact.com/guides/industries)), `employeeCountRange` (the same headcount brackets), `location` (`country`, `countryCode`, `region` and `city`, matched on the headquarters) and `maxDataAgeDate`. Organization search also accepts filters the previous endpoint did not have:

| Filter | What it does |
| ------ | ------------ |
| `specialities` | Match the specialities listed on the organization profile, one value or a list (OR-matched) |
| `foundedYear` | Keep organizations founded within a year range, from 0 to 2100 |
| `followersCount` | Keep organizations within a follower count range |

Two rules are stricter. A request needs at least one filter, and `maxDataAgeDate` cannot be that only filter: both return `422` with `NO_FILTER_PROVIDED`. Validation is otherwise unchanged in spirit: unknown fields, blank values and values outside a closed list return `422` with suggestions and cost 0 credits. Every filter is documented on the [Organization search](https://docs.reversecontact.com/endpoints/search-organizations#request-body) page.

## Available fields moves too

If you discover filters dynamically, switch `GET /v2/search/companies/available-fields` to `GET /v2/search/organizations/available-fields`. The response keeps its shape, with two differences: `data.entity` is `"organization"`, and `data.fields` lists `cursor` instead of `page`, next to every accepted filter and the `allowedValues` of closed vocabularies such as industries.

## Migration checklist

1. Replace the search path with `POST /v2/search/organizations` and the available-fields path with `GET /v2/search/organizations/available-fields`.
2. Replace `page` with cursor handling: loop while `data.metadata.nextCursor` is not `null`.
3. Review your `perPage`: the default is now `25`, each delivered organization costs 1 credit, and the balance must cover `perPage` before the call.
4. Replace `data.metadata.total` reads with a call to `POST /v2/search/organizations/count`.
5. Map the renamed fields: `name` becomes `companyName`, `location` becomes `headquarter`, and `companyUpdateDate` is gone.
6. Drop the Company profile calls you used to chain after a search: results are already full profiles.
7. Make sure every request carries at least one filter other than `maxDataAgeDate`.

## FAQ

### Does `/v2/search/companies` still work?

Yes, for now. `POST /v2/search/companies` is deprecated since October 16, 2026, has left the documentation and has no retirement date yet. Once it is retired, it will return `410 Gone` with the error code `ENDPOINT_GONE` and name `POST /v2/search/organizations` in `error.details.alternative`, so move to `POST /v2/search/organizations` now.

### How do I get the total number of matching organizations with `/v2/search/organizations`?

`POST /v2/search/organizations` does not return a total: `data.metadata.count` is only the number of organizations in the current page. Call `POST /v2/search/organizations/count` with the same filters to get `data.count`, the total number of matching organizations across all pages, for 0 credits. See [Organization search count](https://docs.reversecontact.com/endpoints/search-organizations-count).

### How many credits does an organization search cost?

`POST /v2/search/organizations` charges 1 credit per organization delivered: a page that returns 25 organizations costs 25 credits, and a page that returns no organization costs 0 credits. Rejected filters also cost 0 credits, and lowering `perPage` caps what a single request can spend.

### Which result fields change name in `/v2/search/organizations`?

In `POST /v2/search/organizations` results, `name` becomes `companyName` and `location` becomes `headquarter`, whose `country` is still an ISO code. `companyUpdateDate` is not part of the full profile: filter with `maxDataAgeDate`, or read `lastUpdate` from the free Company profile status endpoint.

### Do I still need to call the Company profile endpoint after an organization search?

No. Each item of `data.data` in a `POST /v2/search/organizations` response is already a full organization profile, with `description`, `specialities`, `headquarter`, `locations`, `foundedOn` and the other Company profile fields, so the Company profile calls you chained after a search can be dropped. See [Company profile](https://docs.reversecontact.com/endpoints/fetch-company).
