The flow
1
Create a job
POST /exports with exports:create. Returns 202 Accepted and a job in
pending status.2
Poll for completion
GET /exports/{export_id} with exports:read. The status moves from
pending → running → completed (or failed). Poll every few seconds.3
Download
Once Fetch the
completed, GET /exports/{export_id}/download returns a short-lived
signed URL and the seconds until it expires:download_url promptly — it’s valid for about 10 minutes. You can
request a fresh one any time while the export exists.Request options
Job lifecycle and retention
- Statuses are
pending,running,completed,failed, anddeleted. - Completed exports are retained for 7 days, then deleted automatically. The
expires_atfield tells you when. After that, create a new export. - A key sees only exports it created within its own organization — exports made in the web console or by other keys are not visible.
Athletes who can’t be included are skipped, not fatal
An export never fails because one athlete can’t be included. Athletes who have left the organization (or otherwise can’t be accessed) are skipped and listed in the job’sskipped_athletes array, each with a uid and a reason, and are
also recorded in the export’s manifest. The rest of the export completes normally.
Athletes are notified
When an export includes an athlete’s data, that athlete receives an in-app transparency notification telling them their organization exported training data that includes their activities, and pointing them to the organization’s audit log. Every export is also written to the org audit log with the key that created it. This is core to the Data Portability Promise: data moves, but never silently.Rate limits:
POST /exports is additionally capped at 5 requests/hour per
key, on top of the standard per-key limits. See
Rate limits.