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

# Bulk Create / Update Contacts

> Upsert up to 1,000 contacts in a single call, processed asynchronously.

Requires the `people:write` scope. This endpoint accepts up to **1,000** contacts per call and processes them asynchronously — it returns immediately with an `operation_id` you can poll for completion. Like the single [Create a Contact](/api-reference/people/create) endpoint, contacts are keyed by email: any email that already exists is **merged** with the supplied data (there is no skip-existing mode).

### Body

<ParamField body="people" type="array" required>
  The contacts to create or merge (at least one, at most 1,000).

  <Expandable title="properties">
    <ParamField body="email" type="string" required>
      The contact's email address.
    </ParamField>

    <ParamField body="tags" type="array[string]">
      A list of tag names to apply. Tags that don't exist are created.
    </ParamField>

    <ParamField body="notes" type="string">
      Notes associated with this contact.
    </ParamField>

    <ParamField body="extra_data" type="object">
      Custom field values, keyed by each field's `data_name` (the same shape used by the single-contact endpoints).
    </ParamField>
  </Expandable>
</ParamField>

### Response

Returns `202 Accepted`.

<ResponseField name="operation_id" type="string">
  The id of the async operation.
</ResponseField>

<ResponseField name="operation_url" type="string">
  The full URL of the [operation](/api-reference/operations/get) — poll it to watch the batch progress from `pending` → `processing` → `succeeded`.
</ResponseField>

<ResponseField name="status" type="string">
  The operation's initial status, `pending`. Poll the operation to see it move as the batch is processed.
</ResponseField>

<ResponseField name="accepted" type="number">
  The number of contacts accepted for processing.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl --location --request POST 'https://api.audienceful.com/v2/people/bulk' \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <your-api-key>' \
  --data-raw '{
      "people": [
          { "email": "a@example.com", "tags": ["import"], "notes": "", "extra_data": { "plan": "pro" } },
          { "email": "b@example.com" }
      ]
  }'
  ```

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

  url = "https://api.audienceful.com/v2/people/bulk"
  headers = {
      "Content-Type": "application/json",
      "X-Api-Key": "<your-api-key>",
  }
  payload = {
      "people": [
          {
              "email": "a@example.com",
              "tags": ["import"],
              "notes": "",
              "extra_data": {
                  "plan": "pro",
              },
          },
          {
              "email": "b@example.com",
          },
      ],
  }

  response = requests.post(url, headers=headers, json=payload)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.audienceful.com/v2/people/bulk", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": "<your-api-key>",
    },
    body: JSON.stringify({
      "people": [
        {
          "email": "a@example.com",
          "tags": [
            "import"
          ],
          "notes": "",
          "extra_data": {
            "plan": "pro"
          }
        },
        {
          "email": "b@example.com"
        }
      ]
    }),
  });
  const data = await response.json();
  console.log(data);
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "operation_id": "task-abc123",
    "operation_url": "https://api.audienceful.com/v2/operations/task-abc123",
    "status": "pending",
    "accepted": 2
  }
  ```
</ResponseExample>
