# Reveal Emails

> Look up work emails for up to 100 people. Spends reveal credits.

Requires an API key with the `write` scope.

Takes person objects from a search result. Each newly charged person costs one reveal credit, taken from the workspace's credit buckets with the nearest expiry first. People already revealed in this workspace are reused at no cost, and people the provider cannot look up are not charged. It needs a paid plan.

The lookup runs in the background: the response reports what was requested and charged, and the emails arrive on the batch. Poll [Get Reveal Batch](/api-reference/lead-finder/get-reveal-batch) with the returned `batchId`.

## Body Parameters

<ParamField body="people" type="array" required>
  Between 1 and 100 person objects. Each needs `id` (up to 512 characters) and may carry `firstName`, `lastName`, `title`, `company`, `companyDomain`, `linkedinUrl`, `location`, `industry` and `companySize`. Pass the fields from the search result: a person can only be looked up with a first or last name plus a `company` or `companyDomain`.
</ParamField>

<ParamField body="campaignId" type="string">
  A campaign in this workspace. Each person whose email is found becomes a lead and is added to this campaign. People revealed earlier are added right away.
</ParamField>

## Response

<ResponseField name="data" type="object">
  What was requested.

  <Expandable title="data">
    <ResponseField name="batchId" type="string">Identifier to poll with Get Reveal Batch.</ResponseField>
    <ResponseField name="requested" type="integer">Distinct people in the request.</ResponseField>
    <ResponseField name="charged" type="integer">Credits spent on new lookups.</ResponseField>
    <ResponseField name="reused" type="integer">People already revealed in this workspace.</ResponseField>
    <ResponseField name="inProgress" type="integer">People whose reveal is still pending from an earlier call.</ResponseField>
    <ResponseField name="unsearchable" type="integer">People with no name, or no company or company domain, who cannot be looked up.</ResponseField>
    <ResponseField name="balance" type="integer">Credits left after this call.</ResponseField>
    <ResponseField name="addedToCampaign" type="integer">People already revealed who were added to `campaignId` immediately.</ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
```bash cURL
curl -X POST "https://api.foxreach.io/api/v1/lead-finder/reveal" \
  -H "X-API-Key: otr_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "people": [
      {"id": "per_abc123", "firstName": "Jane", "lastName": "Doe", "company": "Acme Inc", "companyDomain": "acme.com"}
    ]
  }'
```

```python Python SDK (beta)
from foxreach import FoxReach

client = FoxReach(api_key="otr_your_key")
result = client.lead_finder.reveal([
    {"id": "per_abc123", "firstName": "Jane", "lastName": "Doe", "company": "Acme Inc", "companyDomain": "acme.com"},
])
```

```typescript TypeScript SDK (beta)
import { FoxReach } from "foxreach";

const client = new FoxReach({ apiKey: "otr_your_key" });
const result = await client.leadFinder.reveal({
  people: [{ id: "per_abc123", firstName: "Jane", lastName: "Doe", company: "Acme Inc", companyDomain: "acme.com" }],
});
```
</RequestExample>

<ResponseExample>
```json
{
  "data": {
    "batchId": "bat_abc123",
    "requested": 1,
    "charged": 1,
    "reused": 0,
    "inProgress": 0,
    "unsearchable": 0,
    "balance": 99,
    "addedToCampaign": 0
  }
}
```
</ResponseExample>

## Errors

| Status | Description |
|--------|-------------|
| `401` | Missing, invalid, revoked or expired API key |
| `403` | API key does not have the required scope: `write` |
| `402` | `insufficient_lead_finder_credits`: `{"detail": {"code": "insufficient_lead_finder_credits", "message": "...", "balance": 0, "required": 1}}` |
| `403` | `upgrade_required` on the Free plan: `{"detail": {"code": "upgrade_required", "limit_type": "lead_finder", "message": "..."}}`. A missing scope also returns 403 with a plain `detail` string |
| `404` | `Lead Finder is not enabled` while Lead Finder is switched off on this environment |
| `404` | `Campaign not found` when `campaignId` is not in this workspace |
| `422` | `people` is empty or longer than 100, or a person has no `id` |
| `503` | `Lead Finder is not configured` when it is enabled but has no data provider key |

<Note>
  Revealing does not add anyone to Leads. A found email is stored on the reveal and the credit is kept. Put people into Leads with [Add to Leads](/api-reference/lead-finder/add-to-leads), or pass `campaignId` to reveal straight into a campaign, which creates the lead when the email is found. If the email is already an active lead, or was already revealed in this workspace, the credit is returned (`failureReason` `already_in_leads` or `already_revealed`).
</Note>

## Rate Limit

- **100 requests per minute, per API key.** Fixed 60-second window.
- Every response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (unix epoch seconds).
- A `429` response includes a `Retry-After` header (seconds until the bucket resets).

For client-side patterns (`Retry-After`, exponential backoff, monitoring `X-RateLimit-Remaining`), see [Rate Limiting](/rate-limiting).
