Running exports
Queries and record endpoints answer questions and return pages. When you want everything — a full order history, a nightly warehouse load — that is an export, and these endpoints let you trigger one from code instead of clicking in the app.
Exports need the exports scope. A key with only read_data gets 403
here — triggering work and downloading files is a different permission from
reading data.
How it works
An export of a large store takes minutes, which is longer than an HTTP request should wait. So you don't wait: you start a job, get an id back immediately, and check on it.
1. POST /v1/exports/{id}/run → { "job_id": "...", "status": "queued" }
2. GET /v1/export-jobs/{job_id} → poll until "completed"
3. GET /v1/export-jobs/{job_id}/download → the file
1. Find your exports
You run exports you have already set up in the app — the same configurations that power your scheduled exports, with their columns, filters and format already chosen.
curl -H "Authorization: Bearer $ECOMSOLO_API_KEY" \
https://api.ecomsolo.com/v1/exports
{
"data": [
{
"id": "exp_7f21...",
"name": "Nightly orders",
"entity_type": "Orders",
"format": "csv",
"runnable_via_api": true,
"last_run_at": "2026-07-30T02:00:11Z",
"last_run_status": "Success",
"total_runs": 214
}
]
}
info
runnable_via_api tells you whether that configuration can be triggered here. A
few export types run only from the app; trying to run one returns 422 naming
the type rather than silently doing nothing.
2. Start a run
curl -X POST -H "Authorization: Bearer $ECOMSOLO_API_KEY" \
https://api.ecomsolo.com/v1/exports/exp_7f21.../run
{ "job_id": "job_a93c...", "status": "queued" }
202 Accepted — the run has been accepted, not finished. Keep the job_id.
info
An API-triggered run still delivers to the destination you configured — email, FTP, Google Drive or Sheets. Downloading through the API is additive, so automating the trigger never stops colleagues receiving the file where they expect it.
3. Poll until it's done
curl -H "Authorization: Bearer $ECOMSOLO_API_KEY" \
https://api.ecomsolo.com/v1/export-jobs/job_a93c...
{
"job_id": "job_a93c...",
"status": "running",
"name": "Nightly orders",
"entity_type": "Orders",
"created_at": "2026-07-31T02:00:03Z",
"file_name": null,
"error": null
}
status | Meaning |
|---|---|
queued | Accepted, not started yet. |
running | Being produced. |
completed | The file is ready to download. |
failed | It did not finish — see error. |
Poll every 15–30 seconds. Most exports finish in under a minute; very large ones
take several. There is no callback or webhook — poll until completed or
failed.
4. Download
curl -H "Authorization: Bearer $ECOMSOLO_API_KEY" \
-o orders.csv \
https://api.ecomsolo.com/v1/export-jobs/job_a93c.../download
Returns the file with its real content type (text/csv,
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for Excel,
application/zip for bundles).
Asking to download before the job finishes returns 409 — the job exists, it
just isn't ready. Poll first.
A complete nightly pull
#!/usr/bin/env bash
set -euo pipefail
API="https://api.ecomsolo.com"
AUTH="Authorization: Bearer $ECOMSOLO_API_KEY"
JOB=$(curl -sS -X POST -H "$AUTH" "$API/v1/exports/$EXPORT_ID/run" \
| jq -r .job_id)
while :; do
STATUS=$(curl -sS -H "$AUTH" "$API/v1/export-jobs/$JOB" | jq -r .status)
case "$STATUS" in
completed) break ;;
failed) curl -sS -H "$AUTH" "$API/v1/export-jobs/$JOB" | jq -r .error >&2; exit 1 ;;
*) sleep 20 ;;
esac
done
curl -sS -H "$AUTH" -o orders.csv "$API/v1/export-jobs/$JOB/download"
Notes worth knowing
Files don't live forever. Export files are cleaned up when a store re-syncs
or the app is uninstalled. Download soon after completion rather than relying on
an old job_id — if the file has gone, run the export again.
Each run is its own job. Running the same configuration twice gives two
job_ids and two files. The configuration is the recipe; the job is one
cooking of it.
Rate limits apply to these endpoints like any other — see Rate limits. Starting an export costs one request; so does each poll, which is the other reason not to poll every second.
Which should I use?
| You want | Use |
|---|---|
| A total, a trend, a breakdown | POST /v1/query |
| Some records, filtered | GET /v1/orders |
| Everything, as a file | POST /v1/exports/{id}/run |