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
- You send a request with locations, filters, and optional fields — the same parameters you’d use for a search.
- DealMachine builds and runs the query against its BigQuery data warehouse.
- The results are written to a compressed CSV file and uploaded to cloud storage.
- 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 | Export properties matching your location and filter criteria |
| Export People | Export people/contacts matching your location and filter criteria |
| List Exports | List past exports with pagination and status filtering |
| 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 |
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" }] }'
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']}")
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:
{
"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.
Use the count endpoints (Count Properties or Count
People) to check how many records match your filters
before running an export. Count requests are free and return instantly.
Credits
Enrichment credits follow the entities included in the export. Property rows and chargeable property fields cost 1 property enrichment credit per unique property. Person, phone, email, and returned contact rows cost 1 people enrichment 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:
{
"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 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).
{
"download_urls": [
{
"filename": "properties_export_2026-02-17.csv.gz",
"url": "https://storage.googleapis.com/...",
"size": 2457600
}
]
}
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.
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 and 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
-
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.
-
Set generous timeouts. Configure your HTTP client with a timeout of at least 120 seconds. Exports of 500K+ records can take over a minute.
-
Download immediately. Signed download URLs expire after a limited time. Save the files to disk as soon as you receive the response.
-
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.
-
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.