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

# Exporting

> Bulk export properties and people as CSV files

The export endpoints let you download your entire filtered dataset as a compressed CSV file — up to **1,000,000 records** per export. This is the fastest way to extract large amounts of data from the DealMachine API.

## How It Works

1. **You send a request** with locations, filters, and optional fields — the same parameters you'd use for a search.
2. **DealMachine builds and runs the query** against its BigQuery data warehouse.
3. **The results are written to a compressed CSV file** and uploaded to cloud storage.
4. **You receive signed download URLs** in the response, which you use to download the file(s).

The entire process happens synchronously in a single API call. No polling or webhooks required.

## Available Endpoints

| Endpoint                                                         | Description                                                       |
| ---------------------------------------------------------------- | ----------------------------------------------------------------- |
| [Export Properties](/api-reference/properties/export-properties) | Export properties matching your location and filter criteria      |
| [Export People](/api-reference/people/export-people)             | Export people/contacts matching your location and filter criteria |
| [List Exports](/api-reference/exports/list-exports)              | List past exports with pagination and status filtering            |
| [Get Export](/api-reference/exports/get-export)                  | Retrieve a single export by ID to check status or re-download     |

## Important: This Is a Long-Running Request

Unlike most API endpoints that respond in under a second, **export requests typically take up to 30 seconds** to complete — and can take longer for very large datasets approaching the 1,000,000 record limit.

Your HTTP client must be configured to wait for the response. If your client times out before the export completes, you will not receive the download URLs and will need to retry.

### Recommended Timeout Settings

| Language / Tool       | Configuration                          |
| --------------------- | -------------------------------------- |
| **cURL**              | `--max-time 120`                       |
| **Node.js (fetch)**   | `signal: AbortSignal.timeout(120_000)` |
| **Python (requests)** | `timeout=120`                          |
| **Go (http.Client)**  | `client.Timeout = 120 * time.Second`   |

```bash cURL theme={null}
curl -X POST "https://api.v2.dealmachine.com/v1/properties/export" \
  -H "Authorization: Bearer dm_sk_live_xxx" \
  -H "Content-Type: application/json" \
  --max-time 120 \
  -d '{ "locations": [{ "type": "state", "code": "TX" }] }'
```

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

response = requests.post(
    "https://api.v2.dealmachine.com/v1/properties/export",
    headers={"Authorization": "Bearer dm_sk_live_xxx"},
    json={
        "locations": [{"type": "state", "code": "TX"}],
        "filters": [
            {"filter_id": "estimated_value", "operator": "range", "value": {"min": 200000, "max": 500000}}
        ]
    },
    timeout=120
)
export = response.json()

for file in export["download_urls"]:
    print(f"Download: {file['url']}")
```

```javascript Node.js theme={null}
const response = await fetch("https://api.v2.dealmachine.com/v1/properties/export", {
  method: "POST",
  headers: {
    "Authorization": "Bearer dm_sk_live_xxx",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    locations: [{ type: "state", code: "TX" }],
    filters: [
      { filter_id: "estimated_value", operator: "range", value: { min: 200000, max: 500000 } }
    ],
  }),
  signal: AbortSignal.timeout(120_000),
});

const export = await response.json();
for (const file of export.download_urls) {
  console.log(`Download: ${file.url}`);
}
```

## List And Previous-Export Filters

Exports support the same Query Builder protocol filters as search. You can export only records in specific lists, exclude records in other lists, and skip records your organization has already exported:

```json theme={null}
{
  "locations": [],
  "include_lists": { "property_list_ids": [123] },
  "exclude_lists": { "property_list_ids": [456] },
  "exclude_previously_exported": true,
  "fields": ["property_full_address", "estimated_value"]
}
```

Use `people_list_ids` on people exports. For advanced export exclusion, pass a full `exclude_previously_exported` object with `properties`, `people`, or `companies` criteria.

## Export Limits

| Limit                         | Value     |
| ----------------------------- | --------- |
| **Max records per export**    | 1,000,000 |
| **Max locations per request** | 15        |
| **Max filters per request**   | 50        |

If your query matches more than 1,000,000 records, the API returns a `400` error with the actual record count. Narrow your filters or break the export into smaller location-based chunks.

<Tip>
  Use the count endpoints ([Count Properties](/api-reference/properties/count-properties) or [Count
  People](/api-reference/people/count-people)) to check how many records match your filters
  **before** running an export. Count requests are free and return instantly.
</Tip>

## Credits

Data credits follow the entities included in the export. Property rows and chargeable property fields cost **1 property data credit per unique property**. Person, phone, email, and returned contact rows cost **1 people data credit per unique person/contact**. Credits are **deduplicated** within your billing period, so entities you've already accessed this month are free.

The response includes a `credits` object showing exactly what was charged:

```json theme={null}
{
  "credits": {
    "used": 15432,
    "properties": 15432
  }
}
```

If the export would exceed your plan's credit allowance, the API returns a `429` error before any data is exported. No credits are consumed in this case.

See [Credits](/concepts/credits) for more details on how billing works.

## Downloading the Files

The `download_urls` array in the response contains one or more signed URLs. Each URL points to a **gzip-compressed CSV file** (`.csv.gz`).

```json theme={null}
{
  "download_urls": [
    {
      "filename": "properties_export_2026-02-17.csv.gz",
      "url": "https://storage.googleapis.com/...",
      "size": 2457600
    }
  ]
}
```

<Warning>
  Download URLs are **signed and time-limited**. Download the files promptly after receiving the
  response. If a URL expires, you'll need to run the export again.
</Warning>

### Opening the Files

The exported CSV files are gzip-compressed. Most modern tools handle this automatically:

| Tool                | How to open                                                        |
| ------------------- | ------------------------------------------------------------------ |
| **Python (pandas)** | `pd.read_csv("export.csv.gz")` — pandas decompresses automatically |
| **Excel**           | Decompress first with a tool like 7-Zip, then open the `.csv`      |
| **Google Sheets**   | Upload the `.csv.gz` file directly — Sheets decompresses it        |
| **Command line**    | `gunzip export.csv.gz` to decompress, then open the `.csv`         |

## Anchor Types

By default, property exports produce one row per property and people exports produce one row per person. The `anchor` parameter lets you change what each row represents:

| Anchor     | Available On       | Description                                         |
| ---------- | ------------------ | --------------------------------------------------- |
| `property` | Properties         | One row per property (default for property exports) |
| `person`   | Properties, People | One row per unique person                           |
| `phone`    | Properties, People | One row per phone number                            |
| `email`    | Properties, People | One row per email address                           |

For example, if a property has two owners, exporting with `anchor: "person"` produces two rows — one for each owner. Exporting with `anchor: "phone"` produces one row per phone number across all contacts.

## Concurrency

Only one export can run at a time per organization. If you start an export while another is already running, the API returns a `429` error with the code `export_in_progress`. Wait for the first export to complete before starting another.

## Export History

Every export is saved and can be retrieved later using the [List Exports](/api-reference/exports/list-exports) and [Get Export](/api-reference/exports/get-export) endpoints. This is useful for:

* **Re-downloading files** — if you lose a download URL, you can retrieve it from the export record (as long as the export hasn't expired).
* **Checking status** — see whether a past export completed successfully or failed.
* **Auditing** — review what was exported, when, and how many records were included.

Export records include the status (`pending`, `processing`, `completed`, `failed`), record count, file size, download URLs, and timestamps. Download URLs are time-limited — expired URLs require a new export.

## Best Practices

1. **Check the count first.** Use the count endpoint to verify the number of matching records before exporting. This is free and helps you avoid hitting the 1,000,000 record limit.

2. **Set generous timeouts.** Configure your HTTP client with a timeout of at least 120 seconds. Exports of 500K+ records can take over a minute.

3. **Download immediately.** Signed download URLs expire after a limited time. Save the files to disk as soon as you receive the response.

4. **Break large exports into chunks.** If you need more than 1,000,000 records, split your request by location (e.g., export one state at a time) or use narrower filters.

5. **Use filters to reduce cost.** The more targeted your export, the fewer credits you consume. Add filters to limit results to the records you actually need.
