ExaLink API
Beta
REST

Link Analytics

Measure every click on an ExaLink smart link. Pull summary KPIs plus dimensional breakdowns (countries, devices, referrers) and daily timeseries for dashboards and reporting.

Click totals

Total clicks, unique visitors, and conversion counts for any date window.

Country breakdown

ISO country codes and names ranked by click volume — ideal for regional campaigns.

Device mix

Mobile, desktop, tablet, and bot classification from the user-agent at click time.

Referrers

See whether traffic came from WhatsApp, SMS clients, search, social, or direct.

Freshness

~1s

click to API visibility

Max range

366d

per analytics request

Dimensions

4+

geo, device, ref, time

Rate limit

100/s

shared with other ExaLink calls

GET
/v1/exalink/links/:id/analytics
Beta

Returns aggregated analytics for a single link. Omit date filters to use the link's entire lifetime. All times are UTC.

Path & query parameters

FieldTypeRequiredDescription
idpath string
required
Link id from create (e.g. link_01HZX…). Returns 404 if missing or not owned by the business.
fromquery dateoptionalInclusive start date (YYYY-MM-DD or full ISO 8601). Defaults to link createdAt date.
toquery dateoptionalInclusive end date. Defaults to current UTC day. Must be ≥ from.
granularityquery stringoptionalTimeseries bucket size: "day" (default), "hour", or "week". Hour requires a range ≤ 14 days.
timezonequery stringoptionalIANA timezone for bucketing (e.g. "Africa/Accra"). Defaults to "UTC".
limitquery integeroptionalMax rows per dimension array (countries, devices, referrers). Default 25, max 100.

Request examples

Bash
curl -G 'https://api.sendexa.co/v1/exalink/links/link_01HZXK9P2M7QVR8N4W3T6Y5B0A/analytics' \
-H 'Authorization: Basic YOUR_DASHBOARD_BASE64_TOKEN' \
-H 'Accept: application/json' \
--data-urlencode 'from=2026-07-01' \
--data-urlencode 'to=2026-07-12' \
--data-urlencode 'granularity=day' \
--data-urlencode 'timezone=Africa/Accra' \
--data-urlencode 'limit=25'

Response shape

The payload always includes summary, countries, devices, referrers, and timeseries. Dimension arrays are sorted by clicks descending.

JSON
{
"success": true,
"code": "ANALYTICS_OK",
"message": "Analytics retrieved.",
"requestId": "REQ-1782202000001",
"timestamp": "2026-07-12T11:00:00.001Z",
"data": {
"linkId": "link_01HZXK9P2M7QVR8N4W3T6Y5B0A",
"shortUrl": "https://go.example.com/summer26",
"title": "Summer Sale 2026",
"range": {
"from": "2026-07-01T00:00:00.000Z",
"to": "2026-07-12T23:59:59.999Z",
"timezone": "Africa/Accra",
"granularity": "day"
},
"summary": {
"clicks": 1420,
"uniqueClicks": 980,
"conversions": 86,
"conversionRate": 0.0606,
"avgClicksPerUnique": 1.45,
"botClicks": 12,
"passwordFailures": 3
},
"countries": [
{
"country": "GH",
"name": "Ghana",
"clicks": 1102,
"uniqueClicks": 760,
"share": 0.7761
},
{
"country": "NG",
"name": "Nigeria",
"clicks": 188,
"uniqueClicks": 140,
"share": 0.1324
},
{
"country": "US",
"name": "United States",
"clicks": 72,
"uniqueClicks": 48,
"share": 0.0507
},
{
"country": "XX",
"name": "Unknown",
"clicks": 58,
"uniqueClicks": 32,
"share": 0.0408
}
],
"devices": [
{
"device": "mobile",
"clicks": 1288,
"uniqueClicks": 900,
"share": 0.9070
},
{
"device": "desktop",
"clicks": 98,
"uniqueClicks": 62,
"share": 0.0690
},
{
"device": "tablet",
"clicks": 22,
"uniqueClicks": 14,
"share": 0.0155
},
{
"device": "bot",
"clicks": 12,
"uniqueClicks": 4,
"share": 0.0085
}
],
"referrers": [
{
"referrer": "direct",
"clicks": 810,
"uniqueClicks": 560,
"share": 0.5704
},
{
"referrer": "whatsapp",
"clicks": 320,
"uniqueClicks": 240,
"share": 0.2254
},
{
"referrer": "sms",
"clicks": 180,
"uniqueClicks": 120,
"share": 0.1268
},
{
"referrer": "facebook",
"clicks": 70,
"uniqueClicks": 40,
"share": 0.0493
},
{
"referrer": "other",
"clicks": 40,
"uniqueClicks": 20,
"share": 0.0282
}
],
"timeseries": [
{
"date": "2026-07-01",
"clicks": 42,
"uniqueClicks": 31,
"conversions": 2
},
{
"date": "2026-07-02",
"clicks": 88,
"uniqueClicks": 60,
"conversions": 5
},
{
"date": "2026-07-11",
"clicks": 210,
"uniqueClicks": 150,
"conversions": 14
},
{
"date": "2026-07-12",
"clicks": 156,
"uniqueClicks": 112,
"conversions": 9
}
]
}
}
Unique clicks
Uniques are approximated with a privacy-preserving visitor key (hashed IP + UA family + day). The same person clicking twice in one day counts as one unique for that day, but two total clicks. Do not treat uniques as a strict GDPR identity.
Conversions
conversions increments when your app reports a conversion tied to the link (or when a configured conversion pixel fires). The matching webhook event is exalink.converted. Webhooks →
Countries
Geo is derived from the edge IP at click time. country: "XX" means unresolved (VPN, private range, or missing header). Shares sum approximately to 1.0 within rounding.
Timeseries gaps
Day and week buckets with zero activity may be omitted for sparse links, or filled with zeros depending on range size. Always chart against the requested fromto window.
Metric definitions
FieldDefinition
summary.clicksSuccessful redirects (password passed if required). Bot traffic included unless filtered client-side via botClicks.
summary.uniqueClicksApprox. distinct visitors in the range (see unique clicks note above).
summary.conversionsAttributed conversions in the range.
summary.conversionRateconversions / clicks (0 if clicks is 0).
summary.botClicksSubset of clicks classified as automated agents.
summary.passwordFailuresFailed password attempts on protected links (not counted as clicks).
devices[].devicemobile | desktop | tablet | bot | other
referrers[].referrerNormalized source label: direct, whatsapp, sms, facebook, twitter, instagram, google, other, …
*.shareclicks for the row divided by summary.clicks.
Errors
HTTPCodeCause
400INVALID_DATE_RANGEfrom > to, or range longer than 366 days
400INVALID_GRANULARITYhour requested with range > 14 days
401UNAUTHORIZEDMissing/invalid Basic Auth
404LINK_NOT_FOUNDUnknown or deleted link id
429RATE_LIMIT_EXCEEDEDToo many analytics polls — prefer webhooks
JSON
{
"success": false,
"code": "INVALID_DATE_RANGE",
"message": "Parameter 'from' must be on or before 'to'. Maximum range is 366 days.",
"requestId": "REQ-1782202100123",
"timestamp": "2026-07-12T11:10:00.123Z"
}
Integration patterns

Dashboard cache

Poll analytics every 60–300s and cache results. Do not poll on every page view of your admin UI.

Real-time counters

Increment local counters from exalink.click webhooks; use this endpoint for reconciliation only.

Nightly reports

Query granularity=day for the previous UTC day and store rows in your warehouse.