# List your LinkedIn scrapers

Source: https://docs.emelia.io/docs/linkedin-scraper/list-scrapers

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

`GET https://api.emelia.io/linkedin-scrappers`

Returns the scrapers of your workspace, newest first, 20 per page. Deleted scrapers are not returned.

Use it to show a history or to find a scraper id. To follow one extraction, poll `GET /linkedin-scrappers/{id}` instead: it is lighter and returns the same object.

For running segmented scrapers (large searches split into chunks, Grow and Scale plans), two computed fields are added: `nextRunningSegment` (when the next chunk is scheduled) and `betweenSegment` (true when no chunk is being extracted right now).

Pitfall: each object also contains internal fields not described here (cursors, legacy ids). Do not rely on them.

## Authentication

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

## Query parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `page` | integer | no | Page number, starting at 0. 20 scrapers per page. Missing, non numeric or negative values mean 0. An empty `scraps` array means you went past the last page. Default: `0`. Range: 0 to .... |

## Example request

**cURL**

```bash
curl -X GET "https://api.emelia.io/linkedin-scrappers" \
  -H "Authorization: $EMELIA_API_KEY"
```

**JavaScript**

```js
const response = await fetch('https://api.emelia.io/linkedin-scrappers', {
  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",
    headers={"Authorization": os.environ["EMELIA_API_KEY"]},
)
print(response.json())
```

## Responses

### 200: One page of scrapers.

| Field | Type | Required | Description |
|---|---|---|---|
| `success` | boolean | yes |  |
| `scraps` | array of object | yes |  |
| `scraps[]._id` | string | no | Scrapper id. |
| `scraps[].userId` | string | no | Workspace id. Only returned by `GET /linkedin-scrappers/{id}`. |
| `scraps[].name` | string | no |  |
| `scraps[].url` | string | no | Search URL given at creation. |
| `scraps[].scrapType` | enum: `people`, `account`, `classic` | no | `people`: Sales Navigator people search or lead list. `account`: Sales Navigator account (company) search; detected when the extraction starts, so it reads `people` before. `classic`: classic LinkedIn people search. |
| `scraps[].status` | enum: `running`, `planified`, `paused`, `enriching`, `done`, `error` | no | Life cycle of a scraper: - `planified`: waiting for `plannedStart`. - `running`: extraction in progress. For a segmented extraction, also between two chunks (see `betweenSegment`). - `paused`: paused by you (segmented extractions only) or by Emelia when credits ran out. Restart with resume. - `done`: extraction finished; also the state after an enrichment ends. - `enriching`: email enrichment in progress (`POST /{id}/enrich`). - `error`: stopped, the reason is in `error`. |
| `scraps[].error` | enum: `INVALID_COOKIE`, `2FA_REQUIRED`, `BAD_SALES_NAV_URL`, `NEED_SUBSCRIPTION`, `TOO_MUCH_RESULT`, `RATE_LIMIT`, `SEGMENTATION_ERROR`, `PROCESS_ERROR`, `NO_AUTH_FOUND` or null | no | Error code when `status` is `error`. |
| `scraps[].errorDetail` | object | no | Internal diagnostic written by the scraper (free text in French). Not a stable contract: use `error`. |
| `scraps[].totalContact` | integer | no | Contacts to extract: 0 until LinkedIn reports the result count, then capped by the plan rules and `maxContacts`. Final count once `done`. Reduced by a split or a filter. |
| `scraps[].scrapIndex` | integer | no | Contacts extracted so far. |
| `scraps[].maxContacts` | integer | no | Value of `max_contacts` at creation, 0 = no limit. |
| `scraps[].segmented` | boolean | no | True when the search was split into chunks (Grow and Scale, 3,000 results or more). |
| `scraps[].segments` | array of object | no |  |
| `scraps[].estimatedEnd` | string (date-time) | no | Estimated end of a segmented extraction. |
| `scraps[].nextRunningSegment` | string or null (date-time) | no | Computed for running segmented scrapers: when the next chunk is scheduled. |
| `scraps[].betweenSegment` | boolean | no | Computed for running segmented scrapers: true when no chunk is being extracted right now. |
| `scraps[].plannedStart` | string (date-time) | no | Scheduled start, when given at creation. |
| `scraps[].date` | string (date-time) | no | Creation date. |
| `scraps[].lastUpdate` | string (date-time) | no | Last time the scraper wrote progress. |
| `scraps[].enrichEnabled` | boolean | no | True once an email enrichment was started. |
| `scraps[].startEnrichDate` | string (date-time) | no | Start of the email enrichment. |
| `scraps[].enrichIndex` | integer | no | Contacts processed by the enrichment. Only set at the end. |
| `scraps[].enrichFound` | integer | no | Emails found. Only set at the end of the enrichment. |
| `scraps[].enrichPhoneEnabled` | boolean | no | True when a phone enrichment was started from the app. |
| `scraps[].enrichPhoneIndex` | integer | no |  |
| `scraps[].enrichPhoneFound` | integer | no |  |
| `scraps[].processing` | boolean | no | True while `POST /{id}/filter` runs. |
| `scraps[].webhooks` | array of object | no |  |
| `scraps[].authes` | array of string | no | LinkedIn account ids used by the scraper. |
| `scraps[].auth` | string | no | Legacy single account id (old scrapers only). |
| `scraps[].convertedListId` | string or null | no | Id of the Emelia list created automatically from the finished scraper, `null` before. |

```json
{
  "success": true,
  "scraps": [
    {
      "_id": "66f1a2b3c4d5e6f7a8b9c0d1",
      "authes": [
        "66e0f1a2b3c4d5e6f7a8b9c0",
        "66e0f1a2b3c4d5e6f7a8b9c1"
      ],
      "name": "Acme CTOs France",
      "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))))",
      "scrapType": "people",
      "status": "running",
      "totalContact": 7400,
      "scrapIndex": 5000,
      "maxContacts": 0,
      "segmented": true,
      "segments": [
        {
          "_id": "66f1a2b3c4d5e6f7a8b9c101",
          "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))%2C(type%3ACOMPANY_HEADCOUNT%2Cvalues%3AList((id%3AB%2Ctext%3A1-10%2CselectionType%3AINCLUDED)))))",
          "scrapIndex": 2500,
          "count": 2500,
          "complete": true
        },
        {
          "_id": "66f1a2b3c4d5e6f7a8b9c102",
          "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))%2C(type%3ACOMPANY_HEADCOUNT%2Cvalues%3AList((id%3AC%2Ctext%3A11-50%2CselectionType%3AINCLUDED)))))",
          "scrapIndex": 2500,
          "count": 2500,
          "complete": true
        },
        {
          "_id": "66f1a2b3c4d5e6f7a8b9c103",
          "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))%2C(type%3ACOMPANY_HEADCOUNT%2Cvalues%3AList((id%3AD%2Ctext%3A51-200%2CselectionType%3AINCLUDED)))))",
          "scrapIndex": 0,
          "count": 2400
        }
      ],
      "estimatedEnd": "2026-10-12T13:40:00.000Z",
      "nextRunningSegment": "2026-10-12T09:11:00.000Z",
      "betweenSegment": true,
      "date": "2026-10-11T08:02:41.512Z",
      "lastUpdate": "2026-10-11T11:14:03.220Z",
      "enrichEnabled": false,
      "enrichIndex": 0,
      "enrichFound": 0,
      "enrichPhoneEnabled": false,
      "enrichPhoneIndex": 0,
      "enrichPhoneFound": 0,
      "webhooks": [
        {
          "_id": "66f1a2b3c4d5e6f7a8b9c0f1",
          "webhookUrl": "https://hooks.acme.example/emelia/scrapper",
          "events": [
            "FINISHED",
            "ERROR"
          ]
        }
      ],
      "convertedListId": null
    },
    {
      "_id": "66fd0e1f2a3b4c5d6e7f8a90",
      "authes": [
        "66e0f1a2b3c4d5e6f7a8b9c0"
      ],
      "name": "Acme marketing leads",
      "url": "https://www.linkedin.com/sales/lists/people/7212345678901234567?sortCriteria=CREATED_TIME&sortOrder=DESCENDING",
      "scrapType": "people",
      "status": "done",
      "totalContact": 1840,
      "scrapIndex": 1840,
      "maxContacts": 0,
      "segmented": false,
      "segments": [],
      "date": "2026-10-02T14:05:09.000Z",
      "lastUpdate": "2026-10-02T14:41:55.000Z",
      "enrichEnabled": true,
      "startEnrichDate": "2026-10-02T15:20:11.000Z",
      "enrichIndex": 1840,
      "enrichFound": 1022,
      "enrichPhoneEnabled": false,
      "enrichPhoneIndex": 0,
      "enrichPhoneFound": 0,
      "webhooks": [],
      "convertedListId": "66f2b3c4d5e6f7a8b9c0d1e2"
    }
  ]
}
```

### 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"
}
```
