# Add up to 100 contacts to a list

Source: https://docs.emelia.io/docs/lists/add-contacts-in-bulk

Guide: [Lists & contacts](https://docs.emelia.io/docs/lists.md)

`POST https://api.emelia.io/lists/list/{listId}/contacts/batch`

Adds 1 to 100 contacts in one call. Each item of `contacts` is the same flat object as in `POST /lists/list/{listId}/contacts`, with the same key resolution, custom variable creation, duplicate rules and `updateIfExists` behaviour (read that endpoint first).

Rows are processed one after the other and independently: a failing row is reported in `results` with its `index` and an `error` message and does not stop the others. A row repeating an email or LinkedIn URL already seen earlier in the same batch is counted as a duplicate of that earlier row and is never updated, even with `updateIfExists=true`.

Counters: `created + duplicates + failed = total`, and `updated` is a subset of `duplicates`. The body must be an object with a `contacts` array, not a bare array. Split larger loads into several calls of 100.

## Authentication

Send your API key in the `Authorization` header: `Authorization: <your API key>`.

## Path parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `listId` | string | yes | List id. |

## Query parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `updateIfExists` | enum: `true`, `false` | no | Only the exact string `true` enables it. Applies to every row. Default: `"false"`. |

## Request body

Content type: `application/json`, required.

| Field | Type | Required | Description |
|---|---|---|---|
| `contacts` | array of object | yes | Max 100 items. |

```json
{
  "contacts": [
    {
      "firstName": "John",
      "lastName": "Doe",
      "email": "john.doe@acme.com",
      "companyName": "Acme"
    },
    {
      "firstName": "Jane",
      "lastName": "Roe",
      "email": "jane.roe@acme.com",
      "companyName": "Acme",
      "icebreaker": "Congrats on the new office"
    }
  ]
}
```

## Example request

**cURL**

```bash
curl -X POST "https://api.emelia.io/lists/list/65f1a2b3c4d5e6f760f68df4/contacts/batch" \
  -H "Authorization: $EMELIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "contacts": [
    {
      "firstName": "John",
      "lastName": "Doe",
      "email": "john.doe@acme.com",
      "companyName": "Acme"
    },
    {
      "firstName": "Jane",
      "lastName": "Roe",
      "email": "jane.roe@acme.com",
      "companyName": "Acme",
      "icebreaker": "Congrats on the new office"
    }
  ]
}'
```

**JavaScript**

```js
const response = await fetch('https://api.emelia.io/lists/list/65f1a2b3c4d5e6f760f68df4/contacts/batch', {
  method: 'POST',
  headers: { Authorization: process.env.EMELIA_API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    "contacts": [
      {
        "firstName": "John",
        "lastName": "Doe",
        "email": "john.doe@acme.com",
        "companyName": "Acme"
      },
      {
        "firstName": "Jane",
        "lastName": "Roe",
        "email": "jane.roe@acme.com",
        "companyName": "Acme",
        "icebreaker": "Congrats on the new office"
      }
    ]
  }),
});
console.log(await response.json());
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://api.emelia.io/lists/list/65f1a2b3c4d5e6f760f68df4/contacts/batch",
    headers={"Authorization": os.environ["EMELIA_API_KEY"]},
    json={
        "contacts": [
            {
                "firstName": "John",
                "lastName": "Doe",
                "email": "john.doe@acme.com",
                "companyName": "Acme"
            },
            {
                "firstName": "Jane",
                "lastName": "Roe",
                "email": "jane.roe@acme.com",
                "companyName": "Acme",
                "icebreaker": "Congrats on the new office"
            }
        ]
    },
)
print(response.json())
```

## Responses

### 200: Batch processed. Read `results` for the outcome of each row.

| Field | Type | Required | Description |
|---|---|---|---|
| `success` | boolean | yes | True even when some rows failed. |
| `total` | integer | yes |  |
| `created` | integer | yes |  |
| `duplicates` | integer | yes | Includes the updated duplicates. |
| `updated` | integer | yes |  |
| `failed` | integer | yes |  |
| `results` | array of object | yes | One entry per input row, in input order. |
| `results[].index` | integer | yes | Position of the row in `contacts`, starting at 0. |
| `results[].leadId` | string | no |  |
| `results[].companyId` | string or null | no |  |
| `results[].duplicate` | boolean | no |  |
| `results[].updated` | boolean | no |  |
| `results[].error` | string | no |  |
| `createdCustomVariables` | array of object | yes |  |
| `createdCustomVariables[].technicalName` | string | yes |  |
| `createdCustomVariables[].displayName` | string | yes | The key as sent. |
| `createdCustomVariables[].schema` | enum: `lead`, `company` | yes | Always `lead` for variables created by the contact routes. |

```json
{
  "success": true,
  "total": 3,
  "created": 1,
  "duplicates": 1,
  "updated": 0,
  "failed": 1,
  "results": [
    {
      "index": 0,
      "leadId": "66f2a1b2c3d4e5f6a7b8c9d0",
      "companyId": "66f2a1b2c3d4e5f6a7b8c9e0",
      "duplicate": true,
      "updated": false
    },
    {
      "index": 1,
      "leadId": "66f2a1b2c3d4e5f6a7b8c9d1",
      "companyId": "66f2a1b2c3d4e5f6a7b8c9e1",
      "duplicate": false,
      "updated": false
    },
    {
      "index": 2,
      "error": "Field \"tags\" must be a string, number or boolean"
    }
  ],
  "createdCustomVariables": []
}
```

### 400: The list id is not a valid ObjectId.

| Field | Type | Required | Description |
|---|---|---|---|
| `message` | string | yes | English, human-readable message. |
| `code` | string | no | Stable error code, when available (for example `PAYLOAD_TOO_LARGE`, `INVALID_JSON`, `export_too_large`, `companies_only`, `enrichment_running`). |
| `params` | object | no |  |
| `error` | string | no | Raw internal message (unexpected errors only). |

```json
{
  "message": "Invalid list id"
}
```

### 401: Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.

| Field | Type | Required | Description |
|---|---|---|---|
| `error` | string | yes |  |
| `code` | enum: `AUTH_REQUIRED`, `ACCOUNT_NOT_FOUND` | yes |  |

```json
{
  "error": "string",
  "code": "AUTH_REQUIRED"
}
```

### 404: The list does not exist or belongs to another account.

| Field | Type | Required | Description |
|---|---|---|---|
| `message` | string | yes | English, human-readable message. |
| `code` | string | no | Stable error code, when available (for example `PAYLOAD_TOO_LARGE`, `INVALID_JSON`, `export_too_large`, `companies_only`, `enrichment_running`). |
| `params` | object | no |  |
| `error` | string | no | Raw internal message (unexpected errors only). |

```json
{
  "message": "List not found"
}
```

### 413: JSON body larger than 5 MB.

| Field | Type | Required | Description |
|---|---|---|---|
| `message` | string | yes | English, human-readable message. |
| `code` | string | no | Stable error code, when available (for example `PAYLOAD_TOO_LARGE`, `INVALID_JSON`, `export_too_large`, `companies_only`, `enrichment_running`). |
| `params` | object | no |  |
| `error` | string | no | Raw internal message (unexpected errors only). |

```json
{
  "message": "Request body too large: the limit is 5mb",
  "code": "PAYLOAD_TOO_LARGE",
  "params": {
    "limit": "5mb"
  }
}
```

### 422: `contacts` missing, not an array, empty or longer than 100.

| Field | Type | Required | Description |
|---|---|---|---|
| `message` | string | yes | English, human-readable message. |
| `code` | string | no | Stable error code, when available (for example `PAYLOAD_TOO_LARGE`, `INVALID_JSON`, `export_too_large`, `companies_only`, `enrichment_running`). |
| `params` | object | no |  |
| `error` | string | no | Raw internal message (unexpected errors only). |

```json
{
  "message": "string",
  "code": "string",
  "params": {},
  "error": "string"
}
```

### 500: Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.

| Field | Type | Required | Description |
|---|---|---|---|
| `message` | string | yes | English, human-readable message. |
| `code` | string | no | Stable error code, when available (for example `PAYLOAD_TOO_LARGE`, `INVALID_JSON`, `export_too_large`, `companies_only`, `enrichment_running`). |
| `params` | object | no |  |
| `error` | string | no | Raw internal message (unexpected errors only). |

```json
{
  "message": "Hmm... Something went wrong. Please try again later."
}
```
