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

# Export a list

> Download a list as a CSV with an async export job: start it, poll it, fetch the file

Exports are asynchronous: start a job, poll it, download the file. A synchronous endpoint would have to build and stream an arbitrarily large CSV inside a single request window; a job keeps working for as long as the export takes, and each poll is a light request against your [rate limits](/concepts/rate-limits).

Both endpoints require a key with the `lists` scope — see [Scopes](/concepts/scopes).

## Start the export

`POST /business/lists/{listId}/export` with an empty JSON body:

```bash theme={null}
curl -X POST "https://api.tryfuse.ai/api/v1/business/lists/665f1c2ab61e0a3d90b1e777/export" \
  -u "af_YOUR_KEY:" \
  -H "Content-Type: application/json" \
  -d '{}'
```

The API responds `202` with a job id:

```json theme={null}
{
  "jobId": "68a1f5209c41d20014b3eb11"
}
```

A `202` means accepted, not finished — the file does not exist yet. Hold on to `jobId`; it is your only handle on the export.

If the list does not exist, or belongs to a different workspace, you get `404` with the standard [error envelope](/concepts/errors):

```json theme={null}
{
  "error": {
    "code": "LIST_NOT_FOUND",
    "message": "The requested list was not found.",
    "param": null,
    "doc_url": "https://fuseai.com/api/errors/list_not_found",
    "request_id": "req_8fK2mQ4x"
  }
}
```

## Poll the job

`GET /business/exports/{jobId}` returns the job's current state:

```bash theme={null}
curl "https://api.tryfuse.ai/api/v1/business/exports/68a1f5209c41d20014b3eb11" \
  -u "af_YOUR_KEY:"
```

```json theme={null}
{
  "exportJob": {
    "jobId": "68a1f5209c41d20014b3eb11",
    "status": "completed",
    "rowCount": 412,
    "downloadUrl": "https://fuseai-csv-uploads.s3.amazonaws.com/exports/.../export.csv?X-Amz-Signature=...",
    "error": null
  }
}
```

`status` moves through a fixed lifecycle:

| Status       | Meaning                                                         |
| ------------ | --------------------------------------------------------------- |
| `pending`    | Accepted, waiting to be picked up                               |
| `processing` | The CSV is being built                                          |
| `completed`  | Done — `downloadUrl` and `rowCount` are set                     |
| `failed`     | Terminal — `error` explains why when a specific reason is known |

`completed` and `failed` are terminal; once you see either, stop polling. Poll every few seconds with backoff — typical exports complete in seconds to a couple of minutes depending on list size. A compact loop:

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

BASE = "https://api.tryfuse.ai/api/v1"
AUTH = ("af_YOUR_KEY", "")

job_id = requests.post(
    f"{BASE}/business/lists/{list_id}/export", auth=AUTH, json={}
).json()["jobId"]

delay = 2
while True:
    job = requests.get(
        f"{BASE}/business/exports/{job_id}", auth=AUTH
    ).json()["exportJob"]
    if job["status"] in ("completed", "failed"):
        break
    time.sleep(delay)
    delay = min(delay * 1.5, 15)

if job["status"] == "completed":
    # The URL is presigned — no Authorization header on this request.
    csv_bytes = requests.get(job["downloadUrl"]).content
else:
    raise RuntimeError(f"Export failed: {job['error']}")
```

Polling an unknown `jobId` — or one that has since expired — returns `404 EXPORT_NOT_FOUND`. There is nothing to recover from that job; start a new export instead.

## Download the file

`downloadUrl` is a presigned URL: fetch it directly, with no authentication header. It is valid for roughly 15 minutes — presigned URLs are short-lived on purpose, so a leaked link does not become a permanent public copy of your data. Every poll of a completed job returns a freshly signed URL, so if yours expires before you download, poll once more and use the new one.

The file downloads named after the list. `rowCount` is the number of data rows — the header row is not counted.

Each row carries the contact's identity fields, emails and phones, company fields, and your [custom columns](/guides/custom-columns). It also includes a **Fuse Contact ID** column: keep it intact if you plan to edit the file offline and bring it back, because a re-upload uses it to match rows to existing contacts instead of creating duplicates — see [Upload your contacts](/guides/upload-your-contacts).

## When exports fail

A `failed` job sets `error` to a specific reason when there is one:

| `error`          | Meaning                                                 | What to do                                                                           |
| ---------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `EMPTY_LIST`     | The list has no members                                 | Add members first — see [Build a list from search](/guides/build-a-list-from-search) |
| `NO_DATA`        | The list has members, but none produced exportable data | Enrich or populate the list before exporting again                                   |
| `LIST_NOT_FOUND` | The list could not be found when the job ran            | Verify the list still exists                                                         |
| `EXPORT_FAILED`  | A transient failure with no more specific cause         | Retry by starting a new export                                                       |

<Note>
  Failed jobs are terminal. Retrying means calling `POST /business/lists/{listId}/export` again for a fresh `jobId` — re-polling a failed job will only ever return `failed`.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Upload your contacts" icon="upload" href="/guides/upload-your-contacts">
    Round-trip an exported CSV back into Fuse, matched by Fuse Contact ID
  </Card>

  <Card title="API reference" icon="book" href="/api-reference">
    Full request and response schemas for the export endpoints
  </Card>
</CardGroup>
