# Download the contacts of a scraper (CSV)

Source: https://docs.emelia.io/docs/linkedin-scraper/download-csv

Guide: [LinkedIn scraper](https://docs.emelia.io/docs/linkedin-scraper.md)

`GET https://api.emelia.io/linkedin-scrappers/{id}/download`

Returns the CSV of a scraper as a string in `data` (a JSON response, not a file). Separator `;`, first line = column names, UTF-8 without BOM, values quoted only when needed. Large extractions give large responses (several MB).

It can be called while the extraction runs: you get what has been collected so far.

`format`: `1` = only contacts with an email (requires a finished enrichment with at least one email), `2` = only contacts without an email (all contacts if the scraper was never enriched), `3` = all contacts.

**People columns** (Sales Navigator and classic searches): id, firstName, lastName, fullName, location, isPremium, openProfile, linkedinUrlProfile, summary, title, company, companyId, companyLocation, companyDescription, companySize, estimatedMinRevenue, estimatedMaxRevenue, industry, description, companyPage, companyWebsite, type. `id` is the Sales Navigator profile key and can contain commas. Fields that LinkedIn does not provide stay empty (classic searches fill fewer columns).

**After an email enrichment** the file is rewritten in this order: id, email_emelia, qualification_emelia, gender_emelia, firstName, lastName, fullName, location, isPremium, openProfile, linkedinUrlProfile, phone_number_emelia, phone_qualification_emelia, phone_country_emelia, summary, title, company, companyId, companyLocation, companyDescription, companySize, estimatedMaxRevenue, estimatedMinRevenue, industry, description, companyPage, companyWebsite, domain_emelia, type.

**Account search columns** (companies): id, companyName, companyPage, companyDescription, companySize, employeeCountRange, industry, companyWebsite, companyLocation, yearFounded, type, estimatedMinRevenue, estimatedMaxRevenue.

Once a scraper is `done`, its contacts are also imported automatically into an Emelia list (`convertedListId`), that you can manage with the Lists endpoints.

## Authentication

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

## Path parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters. |

## Query parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `format` | enum: `1`, `2`, `3` | yes | 1 = contacts with an email only, 2 = contacts without an email only, 3 = all contacts. |

## Example request

**cURL**

```bash
curl -X GET "https://api.emelia.io/linkedin-scrappers/66f1a2b3c4d5e6f7a8b9c0d1/download?format=3" \
  -H "Authorization: $EMELIA_API_KEY"
```

**JavaScript**

```js
const response = await fetch('https://api.emelia.io/linkedin-scrappers/66f1a2b3c4d5e6f7a8b9c0d1/download?format=3', {
  method: 'GET',
  headers: { Authorization: process.env.EMELIA_API_KEY },
});
console.log(await response.json());
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://api.emelia.io/linkedin-scrappers/66f1a2b3c4d5e6f7a8b9c0d1/download?format=3",
    headers={"Authorization": os.environ["EMELIA_API_KEY"]},
)
print(response.json())
```

## Responses

### 200: CSV content in `data`.

| Field | Type | Required | Description |
|---|---|---|---|
| `success` | boolean | yes |  |
| `data` | string | yes | CSV content: `;` separator, header line first, UTF-8 without BOM. |

```json
{
  "success": true,
  "data": "string"
}
```

### 400: Invalid format, unknown scraper or nothing to export.

| Field | Type | Required | Description |
|---|---|---|---|
| `error` | string or object | yes | Human readable message. A few legacy routes return an empty object `{}` here instead of a string. |
| `code` | enum: `INVALID_REQUEST`, `INVALID_PARAMETERS`, `SUBSCRIPTION_REQUIRED`, `CAMPAIGN_NOT_FOUND`, `CAMPAIGN_ALREADY_RUNNING`, `SCHEDULE_MISSING`, `STEPS_MISSING`, `NO_RECIPIENTS`, `TOO_MANY_RECIPIENTS`, `PLANNED_START_INVALID`, `PLANNED_START_IN_PAST`, `EMAIL_PROVIDER_MISSING`, `EMAIL_PROVIDER_DISCONNECTED`, `EMAIL_PROVIDER_EXPIRED`, `ONE_EMAIL_PROVIDER_EXPIRED`, `EMAIL_ADDRESS_DISCONNECTED`, `TRACKING_DOMAINS_REQUIRED`, `LINKEDIN_ACCOUNT_MISSING`, `LINKEDIN_COOKIE_EXPIRED`, `LINKEDIN_SLOT_EXPIRED`, `WHATSAPP_ACCOUNT_EXPIRED`, `WHATSAPP_SLOT_EXPIRED`, `NO_IDENTITY_SET`, `IDENTITY_FOREIGN_ACCOUNT`, `IDENTITY_EMAIL_PROVIDER_MISSING`, `LINKEDIN_ACCOUNT_AMBIGUOUS`, `WHATSAPP_ACCOUNT_AMBIGUOUS`, `LINKEDIN_AUDIO_MISSING`, `EMAIL_STEP_NO_ACTIVE_VERSION`, `EMAIL_VERSION_NO_CONTENT`, `EMAIL_VERSION_NO_SUBJECT`, `RECIPIENTS_NO_EMAIL`, `RECIPIENTS_NO_LINKEDIN`, `MC_LINKEDIN_DAILY_LIMIT_CONNECTION`, `MC_LINKEDIN_DAILY_LIMIT`, `DAILY_CONTACT_TOO_LOW`, `LINKEDIN_DAILY_CONTACT_LIMIT`, `VERIFY_EMAIL_TOKEN_INVALID`, `VERIFY_EMAIL_TOKEN_EXPIRED`, `FREE_CREDITS_DOMAIN_ALREADY_CLAIMED`, `FREE_CREDITS_ALREADY_GRANTED`, `FREE_CREDITS_NOT_ELIGIBLE`, `RATE_LIMITED`, `STATS_COMPUTING`, `WARMUP_PROVIDER_UNSUPPORTED`, `ACCOUNT_DISCONNECTED` | no | Stable machine readable error code. Present only on catalogued errors (mainly the campaign start checks), on validation errors (`INVALID_REQUEST`) and on some LinkedIn provider errors (`ACCOUNT_DISCONNECTED`, `RATE_LIMITED`). Most other errors have no code: rely on the HTTP status and on `error`. Codes are never renamed once deployed. |
| `params` | object | no | Values interpolated in the message, for example `{"max": 30000}`. |

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

### 401: Missing or invalid API key (`Authorization` header).

| Field | Type | Required | Description |
|---|---|---|---|
| `error` | string or object | yes | Human readable message. A few legacy routes return an empty object `{}` here instead of a string. |
| `code` | enum: `INVALID_REQUEST`, `INVALID_PARAMETERS`, `SUBSCRIPTION_REQUIRED`, `CAMPAIGN_NOT_FOUND`, `CAMPAIGN_ALREADY_RUNNING`, `SCHEDULE_MISSING`, `STEPS_MISSING`, `NO_RECIPIENTS`, `TOO_MANY_RECIPIENTS`, `PLANNED_START_INVALID`, `PLANNED_START_IN_PAST`, `EMAIL_PROVIDER_MISSING`, `EMAIL_PROVIDER_DISCONNECTED`, `EMAIL_PROVIDER_EXPIRED`, `ONE_EMAIL_PROVIDER_EXPIRED`, `EMAIL_ADDRESS_DISCONNECTED`, `TRACKING_DOMAINS_REQUIRED`, `LINKEDIN_ACCOUNT_MISSING`, `LINKEDIN_COOKIE_EXPIRED`, `LINKEDIN_SLOT_EXPIRED`, `WHATSAPP_ACCOUNT_EXPIRED`, `WHATSAPP_SLOT_EXPIRED`, `NO_IDENTITY_SET`, `IDENTITY_FOREIGN_ACCOUNT`, `IDENTITY_EMAIL_PROVIDER_MISSING`, `LINKEDIN_ACCOUNT_AMBIGUOUS`, `WHATSAPP_ACCOUNT_AMBIGUOUS`, `LINKEDIN_AUDIO_MISSING`, `EMAIL_STEP_NO_ACTIVE_VERSION`, `EMAIL_VERSION_NO_CONTENT`, `EMAIL_VERSION_NO_SUBJECT`, `RECIPIENTS_NO_EMAIL`, `RECIPIENTS_NO_LINKEDIN`, `MC_LINKEDIN_DAILY_LIMIT_CONNECTION`, `MC_LINKEDIN_DAILY_LIMIT`, `DAILY_CONTACT_TOO_LOW`, `LINKEDIN_DAILY_CONTACT_LIMIT`, `VERIFY_EMAIL_TOKEN_INVALID`, `VERIFY_EMAIL_TOKEN_EXPIRED`, `FREE_CREDITS_DOMAIN_ALREADY_CLAIMED`, `FREE_CREDITS_ALREADY_GRANTED`, `FREE_CREDITS_NOT_ELIGIBLE`, `RATE_LIMITED`, `STATS_COMPUTING`, `WARMUP_PROVIDER_UNSUPPORTED`, `ACCOUNT_DISCONNECTED` | no | Stable machine readable error code. Present only on catalogued errors (mainly the campaign start checks), on validation errors (`INVALID_REQUEST`) and on some LinkedIn provider errors (`ACCOUNT_DISCONNECTED`, `RATE_LIMITED`). Most other errors have no code: rely on the HTTP status and on `error`. Codes are never renamed once deployed. |
| `params` | object | no | Values interpolated in the message, for example `{"max": 30000}`. |

```json
{
  "error": "Authentication required"
}
```

### 500: Unexpected server error.

Current behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.

| Field | Type | Required | Description |
|---|---|---|---|
| `error` | string or object | yes | Human readable message. A few legacy routes return an empty object `{}` here instead of a string. |
| `code` | enum: `INVALID_REQUEST`, `INVALID_PARAMETERS`, `SUBSCRIPTION_REQUIRED`, `CAMPAIGN_NOT_FOUND`, `CAMPAIGN_ALREADY_RUNNING`, `SCHEDULE_MISSING`, `STEPS_MISSING`, `NO_RECIPIENTS`, `TOO_MANY_RECIPIENTS`, `PLANNED_START_INVALID`, `PLANNED_START_IN_PAST`, `EMAIL_PROVIDER_MISSING`, `EMAIL_PROVIDER_DISCONNECTED`, `EMAIL_PROVIDER_EXPIRED`, `ONE_EMAIL_PROVIDER_EXPIRED`, `EMAIL_ADDRESS_DISCONNECTED`, `TRACKING_DOMAINS_REQUIRED`, `LINKEDIN_ACCOUNT_MISSING`, `LINKEDIN_COOKIE_EXPIRED`, `LINKEDIN_SLOT_EXPIRED`, `WHATSAPP_ACCOUNT_EXPIRED`, `WHATSAPP_SLOT_EXPIRED`, `NO_IDENTITY_SET`, `IDENTITY_FOREIGN_ACCOUNT`, `IDENTITY_EMAIL_PROVIDER_MISSING`, `LINKEDIN_ACCOUNT_AMBIGUOUS`, `WHATSAPP_ACCOUNT_AMBIGUOUS`, `LINKEDIN_AUDIO_MISSING`, `EMAIL_STEP_NO_ACTIVE_VERSION`, `EMAIL_VERSION_NO_CONTENT`, `EMAIL_VERSION_NO_SUBJECT`, `RECIPIENTS_NO_EMAIL`, `RECIPIENTS_NO_LINKEDIN`, `MC_LINKEDIN_DAILY_LIMIT_CONNECTION`, `MC_LINKEDIN_DAILY_LIMIT`, `DAILY_CONTACT_TOO_LOW`, `LINKEDIN_DAILY_CONTACT_LIMIT`, `VERIFY_EMAIL_TOKEN_INVALID`, `VERIFY_EMAIL_TOKEN_EXPIRED`, `FREE_CREDITS_DOMAIN_ALREADY_CLAIMED`, `FREE_CREDITS_ALREADY_GRANTED`, `FREE_CREDITS_NOT_ELIGIBLE`, `RATE_LIMITED`, `STATS_COMPUTING`, `WARMUP_PROVIDER_UNSUPPORTED`, `ACCOUNT_DISCONNECTED` | no | Stable machine readable error code. Present only on catalogued errors (mainly the campaign start checks), on validation errors (`INVALID_REQUEST`) and on some LinkedIn provider errors (`ACCOUNT_DISCONNECTED`, `RATE_LIMITED`). Most other errors have no code: rely on the HTTP status and on `error`. Codes are never renamed once deployed. |
| `params` | object | no | Values interpolated in the message, for example `{"max": 30000}`. |

```json
{
  "error": "Internal server error"
}
```
