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

# Bulk Import Leads

> Import up to 1000 leads in a single call with automatic soft-delete restoration and a structured dedup report.

Use this endpoint instead of looping [Create Lead](/api-reference/leads/create-lead) when you have a list of leads to ingest. You get back a single response with counts of `created`, `restored`, `duplicates`, and `errors` — no per-row 409s, no manual retry logic.

## Request Body

<ParamField body="leads" type="array" required>
  Array of lead objects. Each item supports the same fields as [Create Lead](/api-reference/leads/create-lead) — at minimum `email`, plus any of `firstName`, `lastName`, `company`, `title`, `phone`, `linkedinUrl`, `website`, `notes`, `source`, `status`, `customFields`, `tags`. Maximum **1000 leads** per request.
</ParamField>

<ParamField body="restoreSoftDeleted" type="boolean" default="true">
  When `true` (default), leads with the same email as a previously soft-deleted record are **restored** (their `deletedAt` is cleared and the existing fields are merged with the request payload). When `false`, those rows are counted as duplicates instead.
</ParamField>

## Response

```json theme={null}
{
  "data": {
    "created":        142,
    "restored":         8,
    "duplicates":      12,
    "errors":           1,
    "duplicateEmails": ["existing@example.com", "..."],
    "errorDetails":    [{ "email": "", "reason": "empty email" }]
  }
}
```

| Field             | Description                                                                                                                    |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `created`         | Net-new lead rows inserted.                                                                                                    |
| `restored`        | Previously soft-deleted rows whose `deletedAt` was cleared and whose campaign membership counts were re-incremented.           |
| `duplicates`      | Rows skipped because an active lead with that email already exists, or because the same email appeared earlier in the request. |
| `errors`          | Rows skipped due to validation issues (empty email, etc.).                                                                     |
| `duplicateEmails` | First 100 duplicate emails for debugging.                                                                                      |
| `errorDetails`    | First 100 invalid rows with reasons.                                                                                           |

In-request duplicate emails are caught — the first occurrence wins, subsequent rows with the same email are counted as duplicates.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.foxreach.io/api/v1/leads/import" \
    -H "X-API-Key: otr_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "restoreSoftDeleted": true,
      "leads": [
        {
          "email": "alex@acme.com",
          "firstName": "Alex",
          "company": "Acme",
          "tags": ["enterprise", "q1-outreach"],
          "customFields": { "industry": "fintech" }
        },
        {
          "email": "sam@example.com",
          "firstName": "Sam",
          "title": "Head of RevOps"
        }
      ]
    }'
  ```

  ```python Python SDK theme={null}
  from foxreach import FoxReach, LeadCreate

  client = FoxReach(api_key="otr_your_key")

  result = client.leads.bulk_import(
      leads=[
          LeadCreate(
              email="alex@acme.com",
              first_name="Alex",
              company="Acme",
              tags=["enterprise", "q1-outreach"],
              custom_fields={"industry": "fintech"},
          ),
          LeadCreate(
              email="sam@example.com",
              first_name="Sam",
              title="Head of RevOps",
          ),
      ],
      restore_soft_deleted=True,
  )

  print(result.created, result.restored, result.duplicates, result.errors)
  ```

  ```typescript TypeScript SDK theme={null}
  import { FoxReach } from "foxreach";

  const client = new FoxReach({ apiKey: "otr_your_key" });

  const result = await client.leads.bulkImport(
    [
      {
        email: "alex@acme.com",
        firstName: "Alex",
        company: "Acme",
        tags: ["enterprise", "q1-outreach"],
        customFields: { industry: "fintech" },
      },
      { email: "sam@example.com", firstName: "Sam", title: "Head of RevOps" },
    ],
    { restoreSoftDeleted: true },
  );

  console.log(result.created, result.restored, result.duplicates, result.errors);
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "data": {
      "created": 2,
      "restored": 0,
      "duplicates": 0,
      "errors": 0,
      "duplicateEmails": [],
      "errorDetails": []
    }
  }
  ```
</ResponseExample>

## Errors

| Status | Description                                                   |
| ------ | ------------------------------------------------------------- |
| `400`  | More than 1000 leads in `leads`, or empty array               |
| `402`  | Workspace plan contact limit would be exceeded by the request |
| `422`  | Invalid request body (malformed JSON, bad field types)        |
| `429`  | Rate limit exceeded — see Rate Limit section below            |

## Rate Limit

* **100 requests per minute, per API key** (the same v1 limit as every other endpoint).
* A bulk import of 1000 leads counts as **1 request**, not 1000. This is the whole point of the endpoint — your effective ingestion rate goes from 100 leads/minute (looping single create) to **100,000 leads/minute** (looping this endpoint).
* Response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` headers.

For client-side retry/backoff patterns, see [Rate Limiting](/rate-limiting).

## When to use this vs. Create Lead

| Use [Create Lead](/api-reference/leads/create-lead) when... | Use Bulk Import when...                                           |
| ----------------------------------------------------------- | ----------------------------------------------------------------- |
| You have a single lead from a form or webhook               | You're ingesting from a CSV, spreadsheet, or external data source |
| You need the full lead row in the response                  | You need a structured dedup report                                |
| You're inserting fewer than \~10 rows                       | You have anywhere from 10 to 1000 rows per call                   |
| You want a 409 conflict surfaced per row                    | You want to keep going past dups and restore soft-deleted         |
