> ## Documentation Index
> Fetch the complete documentation index at: https://docs.signalhire.com/llms.txt
> Use this file to discover all available pages before exploring further.

# People Profiles & Contact Information

> Retrieve detailed profile and contact information for a specific individual by LinkedIn URL, email, phone, or profile UID.

## Overview

SignalHire's database contains detailed professional profiles for hundreds of millions of people worldwide. The Person API allows looking up individuals using known identifiers — a LinkedIn profile URL, email address, phone number, or SignalHire profile UID — and retrieving their full profile data including contact information.

Up to **100 identifiers** can be submitted in a single request, and identifier types can be mixed freely within the same batch.

<Info>
  This endpoint consumes **standard SignalHire credits** for each successfully matched profile. Credits are shared across the website, browser extension, and API. See [API Pricing](https://www.signalhire.com/pricing) for details.
</Info>

The API works in two modes:

* **Async mode** (default) — submit a request with a `callbackUrl`, receive a `requestId` immediately, and get results POSTed to your URL once ready. Suitable when maximum contact coverage is needed.
* **Sync mode** (Without Waterfall) — submit a request without a `callbackUrl` and receive results immediately in the response. Faster, but may return fewer or slightly older contacts. See [Without Waterfall mode](/person-api/without-waterfall).

## Before You Start

Make sure you have:

* A valid API key — see [Authentication](/authentication)
* A publicly accessible callback URL ready to receive POST requests (for async mode)
* Sufficient credits — see [Get Remaining Credits](/person-api/credits)

***

**Endpoint:** `POST https://www.signalhire.com/api/v1/candidate/search`

## Request Parameters

**In Request Headers**

<ParamField header="apikey" type="string" required>
  Your secret API key. See [Authentication](/authentication).
</ParamField>

**In Request Body**

<ParamField body="items" type="array" required>
  Array of identifiers to look up. Each item can be a LinkedIn profile URL, email address, phone number, or 32-character SignalHire profile UID. Maximum 100 items per request. Types can be mixed freely within a single request.
</ParamField>

<ParamField body="callbackUrl" type="string">
  URL on your server where results will be POSTed once processing is complete. Required unless using `withoutWaterfall`. See [Callback Delivery](#callback-delivery).
</ParamField>

<ParamField body="withoutWaterfall" type="boolean">
  If `true`, returns results synchronously in the response body instead of via callback. Cannot be combined with `callbackUrl`. See [Without Waterfall mode](/person-api/without-waterfall).
</ParamField>

<ParamField body="withoutContacts" type="boolean">
  If `true`, returns profile data without contact details (emails, phones). Requires a separate credit type. See [Without Contacts](/person-api/without-contacts).
</ParamField>

## How It Works (Async Mode)

1. Submit a POST request with an `items` array and a `callbackUrl`
2. SignalHire immediately returns a `requestId` with HTTP `201`
3. The server queries internal and external sources for each item in parallel
4. Once complete, results are POSTed to the `callbackUrl`

<Info>
  For a simpler synchronous flow without setting up a callback server, use [Without Waterfall mode](/person-api/without-waterfall).
</Info>

## Request Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://www.signalhire.com/api/v1/candidate/search \
  -H 'apikey: your_secret_api_key' \
  --data '{
  "items": [
  "https://www.linkedin.com/in/profile1",
  "email@example.com",
  "+44 0 123 456 789",
  "10000000000000000000000000000001"
  ],
  "callbackUrl": "https://www.yourdomain.com/yourCallbackUrl"
  }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
  "https://www.signalhire.com/api/v1/candidate/search",
  headers={"apikey": "your_secret_api_key"},
  json={
  "items": [
  "https://www.linkedin.com/in/profile1",
  "email@example.com",
  "+44 0 123 456 789",
  "10000000000000000000000000000001"
  ],
  "callbackUrl": "https://www.yourdomain.com/yourCallbackUrl"
  }
  )
  ```

  ```javascript Node.js theme={null}
  const axios = require('axios');

  const response = await axios.post(
  'https://www.signalhire.com/api/v1/candidate/search',
  {
      items: [
      'https://www.linkedin.com/in/profile1',
      'email@example.com',
      '+44 0 123 456 789',
      '10000000000000000000000000000001'
      ],
      callbackUrl: 'https://www.yourdomain.com/yourCallbackUrl'
  },
  {
      headers: { apikey: 'your_secret_api_key' }
  }
  );
  ```

  ```java Java theme={null}
  import java.net.http.*;
  import java.net.URI;

  HttpClient client = HttpClient.newHttpClient();

  String body = """
  {
      "items": [
      "https://www.linkedin.com/in/profile1",
      "email@example.com",
      "+44 0 123 456 789",
      "10000000000000000000000000000001"
      ],
      "callbackUrl": "https://www.yourdomain.com/yourCallbackUrl"
  }
  """;

  HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://www.signalhire.com/api/v1/candidate/search"))
  .header("apikey", "your_secret_api_key")
  .header("Content-Type", "application/json")
  .POST(HttpRequest.BodyPublishers.ofString(body))
  .build();

  HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'json'

  uri = URI('https://www.signalhire.com/api/v1/candidate/search')
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  request = Net::HTTP::Post.new(uri)
  request['apikey'] = 'your_secret_api_key'
  request['Content-Type'] = 'application/json'
  request.body = {
      items: [
      'https://www.linkedin.com/in/profile1',
      'email@example.com',
      '+44 0 123 456 789',
      '10000000000000000000000000000001'
      ],
      callbackUrl: 'https://www.yourdomain.com/yourCallbackUrl'
  }.to_json

  response = http.request(request)
  ```
</CodeGroup>

## Initial Response (HTTP 201)

```http theme={null}
HTTP/2 201
Content-Type: application/json
X-Credits-Left: 243
```

```json theme={null}
{
  "requestId": 1748
}
```

HTTP `201` means the request was accepted and processing has started.
The `requestId` uniquely identifies this request — it will be included in the `Request-Id` header of the callback payload, allowing incoming results to be matched to the original request.
For other possible response codes — such as `401`, `402`, `429` — see [Response Codes](/response-codes).

## Callback Format

Once processing is complete, SignalHire POSTs an array to your `callbackUrl`. Each element corresponds to one item from the request and contains:

* **`item`** — the original identifier submitted (LinkedIn URL, email, phone, or UID)
* **`status`** — result of processing for this item (see status values below)
* **`candidate`** — full profile object, present only when `status` is `success`. See [Candidate Object](/person-api/candidate-object) for a complete field reference.

```json theme={null}
[
  {
    "item": "https://www.linkedin.com/in/profile1",
    "status": "success",
    "candidate": { "fullName": "Profile1", "contacts": [...], ... }
  },
  {
    "item": "email@email.com",
    "status": "failed"
  },
  {
    "item": "+44 0 808 189 3171",
    "status": "success",
    "candidate": { "fullName": "Profile2", ... }
  },
  {
    "item": "10000000000000000000000000000001",
    "status": "credits_are_over"
  }
]
```

### Callback Status Values

| Status             | Description                                             |
| ------------------ | ------------------------------------------------------- |
| `success`          | Profile found and returned in the `candidate` field.    |
| `failed`           | Item not found or could not be processed.               |
| `credits_are_over` | Ran out of credits while processing this item.          |
| `timeout_exceeded` | Processing timed out (10-second limit).                 |
| `duplicate_query`  | Same request was submitted again within a short period. |

## Callback Delivery

Setting up a reliable callback endpoint is the responsibility of the API client. The endpoint must be publicly accessible, accept POST requests, and respond with HTTP `200` within 10 seconds. SignalHire has no way to re-initiate processing — if a callback is lost, a new API request must be submitted.

**Delivery flow:**

* Delivery is considered successful when the callback endpoint responds with HTTP `200`
* If the connection fails or times out (10 seconds), SignalHire retries **3 times** sequentially
* If all retries fail, the callback is discarded and the results are lost
* The `Request-Id` header in the callback payload contains the original request ID for tracking

<Warning>
  The callback endpoint must be publicly accessible and respond within 10 seconds. Heavy processing should not be performed inside the callback handler — acknowledge with `200` immediately and process the data asynchronously.
</Warning>

**Delivery timing:**

Callback delivery time varies per request because SignalHire queries external APIs in real time to enrich contact data. Most requests complete within a few seconds, though some may take 30–60 seconds or more depending on external API response times. There is no fixed maximum — the callback is always sent once processing completes, regardless of how long it takes. There is no server-side timeout that would cause a callback to be silently dropped.

### Failed Delivery Notifications

If callbacks could not be delivered, SignalHire sends an **email notification once per hour** listing the affected request IDs and error details. Example:

```
We weren't able to deliver 2 responses to your callback URL due to the
network error on your end.

  cURL error 28: Operation timed out after 10001 milliseconds with 0 out
  of -1 bytes received.

Kindly check the request IDs below and make the required adjustments.

  132725817  132725819

You need to initiate new API calls to receive these responses.
```

Upon receiving such a notification, the issue with the callback endpoint should be resolved and new API requests submitted for the listed request IDs.

## Rate Limits

For information on request limits and throttling, see [Rate Limits](/person-api/rate-limits).
