Skip to main content

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)

  1. Go to Dashboard → Links.
  2. (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.
  3. Click Export CSV, then choose:
    • Summary by link for one row per link with aggregate performance.
    • Individual clicks for one row per retained click in the original 17-column format.
    • Individual clicks + locations for the version 2 file with separate click and app-match geography.

From the Analytics overview (time-range export)

  1. Go to Dashboard → Links → Analytics.
  2. Pick a time range (24 Hours, 7 Days, 30 Days or All Time). The headline numbers and the list update to that window.
  3. Click Export CSV and choose an export type. Summary by link exports every link in the project with its Clicks, New Installs, Reinstalls and the top-X / attributed columns counted inside the selected window, and the window is stamped in the Period Start / Period End columns. With All Time those two columns are empty and the numbers are lifetime totals. Individual clicks exports each click whose timestamp falls inside the selected window.

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.

Summary export columns​

One row per link, with these columns:

ColumnDescription
NameThe link's name
Typedynamic or unified
SlugThe link's slug
Short URLThe full short link
Created AtWhen the link was created
ClicksTotal clicks
New InstallsInstalls attributed to the link
Returning UsersDeprecated compatibility alias for Reinstalls; despite the legacy name, this value contains reinstall rows, not ordinary returning sessions
Attributed ClicksClicks matched to an installation
Top DeviceMost common device
Top OSMost common operating system
Top BrowserMost common browser
Top CountryMost common country
Last Clicked AtTimestamp of the most recent click
Period StartStart of the activity window the numbers were counted in (empty = lifetime)
Period EndEnd of the activity window (empty = lifetime)
ReinstallsReinstall rows attributed to the link; use this canonical column for new integrations

Individual-click export columns​

The click-level export has one row per retained click, ordered chronologically. It is a snapshot through the time it was requested, so clicks arriving while a large file is generated cannot shift its pages or produce duplicate rows.

ColumnDescription
Click IDUnique click-event identifier
Clicked At UTCExact click timestamp in UTC
Link NameThe link's display name
SlugThe link's slug
Short URLThe full short link
ReferrerReferring page when available
UTM Source / Medium / CampaignCaptured campaign parameters
Device / Operating System / BrowserDerived device information
Country / Region / CityDerived approximate location
ChannelQR or Direct / unknown
Attributed InstallWhether the click was matched to an installation

The Individual clicks + locations option requests click export version 2, which appends these six columns. The original dashboard option and API clients that omit clickExportVersion continue to receive the original 17 columns. API clients can request version 2 with "clickExportVersion": 2 alongside "kind": "clicks".

Version 2 columnDescription
Click Location SourceUnverified request IP (client-reported direct route) when the client reported a direct API resolve; Unverified request IP otherwise. The client-reported route is not trusted proof of the network path. Blank if no click location was resolved.
Attributed App Match At UTCTime the SDK deferred-match request attributed the click. Blank for older matches without a snapshot.
App Country / App Region / App CityApproximate location of that SDK match request, kept separate from click location. Blank when unavailable.
App Location SourceAttributed SDK match IP when app geography is available.

The two locations may differ. IP-based geography is approximate; historical click locations can reflect an intermediate proxy and are not retroactively corrected. An attributed install without an app-match snapshot leaves the new app-location cells blank. They also remain blank when attribution used a link-level fallback and could not identify the exact browser click. New deferred matches carry the browser click's ID for this purpose; older redirect pages and unmatched click rows do not produce an app-location snapshot. New redirect pages pass a short-lived click receipt with the click ID. When the receipt matches the resolved click, app-match location can be recorded even if the device changed networks between browser and SDK requests. Older pages without a receipt retain best-effort attribution without an app-location snapshot; during a rolling deployment, new click rows opened by old page scripts require matching browser network/device signals.

Raw IP addresses, full user-agent strings and arbitrary metadata are not included in customer exports.

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.
  • 50,000 clicks per individual-click export. Narrow the selected analytics period or link filters when a click export exceeds this limit.
  • 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

{
"kind": "summary",
"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. kind accepts summary (the default) or clicks.

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:

{
"kind": "clicks",
"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.