Exporting Links (CSV)
Export every link in a project — together with its click and install analytics — to a CSV file. Exports are a paid-plan feature and run as a background job, so a large project never blocks your browser. When the file is ready you get an email and a download link.
Exporting from the dashboard
There are two places to start an export:
From the Links page (filtered export)
- Go to Dashboard → Links.
- (Optional) Apply a search or a date range. The date range on this page is an activity window: the list shows every link with its clicks and installs counted inside that range, and the export uses exactly the same window, so the CSV matches the numbers on screen.
- Click Export CSV.
From the Analytics overview (time-range export)
- Go to Dashboard → Links → Analytics.
- Pick a time range (24 Hours, 7 Days, 30 Days or All Time). The headline numbers and the list update to that window.
- Click Export CSV. Every link in the project is exported with its
Clicks,New Installs,Returning Usersand the top-X / attributed columns counted inside the selected window, and the window is stamped in thePeriod Start/Period Endcolumns. With All Time those two columns are empty and the numbers are lifetime totals.
The job starts immediately. You can keep working; when it finishes you'll receive an "export ready" email with a download link, and the download also appears in the dashboard.
What's in the file
One row per link, with these columns:
| Column | Description |
|---|---|
| Name | The link's name |
| Type | dynamic or unified |
| Slug | The link's slug |
| Short URL | The full short link |
| Created At | When the link was created |
| Clicks | Total clicks |
| New Installs | Installs attributed to the link |
| Returning Users | Returning-user opens attributed to the link |
| Attributed Clicks | Clicks matched to an install/open |
| Top Device | Most common device |
| Top OS | Most common operating system |
| Top Browser | Most common browser |
| Top Country | Most common country |
| Last Clicked At | Timestamp of the most recent click |
| Period Start | Start of the activity window the numbers were counted in (empty = lifetime) |
| Period End | End of the activity window (empty = lifetime) |
Limits
- 100,000 links per export. If a project (after filters) exceeds this, the job fails with a message asking you to narrow the filters — split the export by date range to stay under the cap.
- Download links are signed and valid for 7 days. Re-open the export from the dashboard to mint a fresh link after that.
How it works (API)
The dashboard drives two endpoints. They authenticate with a dashboard (Supabase) session — these are not API-key endpoints, and an export cannot be triggered with an SDK API key.
Start an export
POST https://api.ulink.ly/api/v1/projects/{projectId}/links/export
Authorization: Bearer <supabase-session-jwt>
Content-Type: application/json
{
"search": "summer",
"createdAfter": "2026-01-01",
"createdBefore": "2026-06-30",
"sortBy": "createdAt",
"sortOrder": "desc"
}
All body fields are optional and mirror the filters on the Links page.
To export with an activity window instead (what the dashboard's Links page and
Analytics overview do), send activeAfter / activeBefore ISO timestamps. The
click and install columns are then counted inside that window, and every link
in the project is included whether or not it had activity. createdAfter /
createdBefore still apply on top of the window if you send both, so "links
created in May, with their June clicks" is one request. An open-ended bound is
fine; if the two bounds are reversed they are swapped. Note that the 100,000-row
cap counts links, not clicks, so a narrower window does not reduce the row count;
use a creation-date range or a search to get under the cap:
{
"activeAfter": "2026-09-03T12:00:00.000Z",
"activeBefore": "2026-09-10T12:00:00.000Z",
"sortBy": "clickCount",
"sortOrder": "desc"
}
The response returns a job to poll:
{ "success": true, "data": { "jobId": "…", "status": "pending" } }
Poll for the result
GET https://api.ulink.ly/api/v1/projects/{projectId}/links/export/{jobId}
Authorization: Bearer <supabase-session-jwt>
{
"success": true,
"data": {
"status": "completed",
"rowCount": 1280,
"error": null,
"downloadUrl": "https://…signed-url…"
}
}
status moves through pending → processing → completed (or failed, with the
reason in error). A fresh signed downloadUrl is minted on each read of a
completed job, so you can always poll again for a new link rather than caching
the URL.
Free plans receive 402 Payment Required from the start endpoint.