Analytics API
GET /api/v1/links/{slug}/analytics and GET /api/v1/analytics return the dashboard's report as JSON: views, time, read-through, audience, timing.
Two read-only endpoints return the same report the dashboard's Analytics pages render, as JSON: one for a single link and one for the whole account. Both are computed by the same engine as the dashboard, so a number here never differs from what you see on the site. They need a Pro account (Lifetime included); Free keys get a 403 with code pro_required.
Analytics for one link#
/api/v1/links/{slug}/analyticsAPI key required{slug} is the part of the public URL after /p/. The link must belong to the key's owner.
Query parameters
rangestring- One of
7d,14d,30d,90dorall. Default30d. Ranges are whole UTC days ending today;allruns from the link's creation.
curl "https://linkinseconds.com/api/v1/links/q3-summary/analytics?range=30d" \
-H "Authorization: Bearer $LIS_API_KEY"{
"link": { "slug": "q3-summary", "title": "Q3 summary", "url": "https://linkinseconds.com/p/q3-summary", "kind": "pdf" },
"range": { "key": "30d", "from": "2026-09-05T00:00:00.000Z", "to": "2026-10-05T00:00:00.000Z", "days": 30, "label": "Last 30 days" },
"totals": {
"views": 128, "uniques": 96, "sessions": 74,
"avgEngagedMs": 102000, "medianEngagedMs": 61000, "totalEngagedMs": 7548000,
"completionPct": 58, "downloads": 12, "qrScans": 9,
"returningPct": 18.9, "bouncePct": 21.6, "botsExcluded": 7, "atypical": 3
},
"previous": { "views": 91, "uniques": 70, "avgEngagedMs": 88000, "downloads": 4, "sessions": 52 },
"deltas": {
"views": { "pct": 41, "dir": "up" },
"uniques": { "pct": 37, "dir": "up" },
"avgEngagedMs": { "pct": 16, "dir": "up" },
"downloads": { "pct": 50, "dir": "up" }
},
"granularity": "day",
"series": [
{ "date": "2026-09-05", "label": "Sep 5", "views": 3, "uniques": 3, "avgEngagedS": 74 }
],
"breakdowns": {
"countries": [{ "key": "IN", "count": 69, "pct": 53.9 }, { "key": "US", "count": 27, "pct": 21.1 }],
"cities": [{ "key": "Surat, IN", "count": 31, "pct": 24.2 }],
"referrers": [{ "key": "Direct", "count": 90, "pct": 70.3 }, { "key": "linkedin.com", "count": 22, "pct": 17.2 }],
"sources": [{ "key": "Direct", "count": 90, "pct": 70.3 }, { "key": "Referral", "count": 29, "pct": 22.7 }, { "key": "QR scan", "count": 9, "pct": 7 }],
"devices": [{ "key": "Mobile", "count": 79, "pct": 61.7 }, { "key": "Desktop", "count": 49, "pct": 38.3 }],
"browsers": [{ "key": "Safari", "count": 41, "pct": 55.4 }],
"os": [{ "key": "iOS", "count": 38, "pct": 51.4 }],
"screens": [{ "key": "360-479", "count": 40, "pct": 54.1 }]
},
"timing": { "byHour": [0, 1, 2], "byWeekday": [9, 24, 31, 22, 20, 15, 7], "heat": [[0]], "peakHour": 14, "peakWeekday": 2 },
"content": {
"kind": "pages", "pagesTotal": 12, "avgPagesSeen": 6.9, "readToEndPct": 23,
"pages": [{ "page": 1, "views": 74, "reachPct": 100, "avgMs": 9000, "attentionPct": 8.8 }],
"dropoff": { "afterPage": 6, "lostPct": 34 }
},
"insights": [
{ "id": "attention", "label": "Average attention", "value": "1m 42s", "detail": "median 1m 1s across 74 opens", "tone": "good" }
],
"recent": [
{
"at": "2026-10-04T13:12:44.120Z", "country": "IN", "city": "Surat",
"device": "Mobile", "browser": "Safari", "os": "iOS", "source": "Direct",
"engagedMs": 95000, "progress": "7 of 12 pages", "downloaded": false, "returning": false, "atypical": false
}
]
}Reading the report
| Field | Meaning |
|---|---|
range | UTC. to is exclusive (the start of tomorrow). days is the width. |
totals | Views come from the view log and cover every open; sessions and everything about time or progress come from the engagement tracker, so sessions can be lower than views. downloads is all-time. atypical counts sessions from a bot, an email scanner or a link preview (engaged under 1 s and gone within 2 s): they are not in sessions and move no average. A bot session is also in botsExcluded. |
previous | The same width of time immediately before the range, for the deltas. |
deltas | Signed percent change vs previous. pct is null when the previous period was zero. |
series | One point per day, or per week when granularity is week (ranges over 120 days). |
breakdowns | Top 8 buckets each, with pct as the share of the card's total. Countries are ISO 3166-1 alpha-2 codes. |
timing | Counts by UTC hour (0 to 23) and weekday (0 = Sunday), plus heat[weekday][hour]. |
content | kind is pages (PDF), media (video, audio), scroll (web pages, images, text, documents) or none (archives). Only the matching sub-object is present. pages carries dropoff (afterPage, lostPct: the page after which the largest share of readers stopped) and media carries media.dropoff (beforePct of 25, 50, 75 or 100, lostPct). Both are null until at least 3 sessions and a loss of at least 10 points. |
insights | The same cards the dashboard shows, already phrased. tone is good, neutral or warn. |
recent | Up to 25 most recent opens that have engagement data, newest first. Each carries the time, country and city, device, browser and OS, how far the viewer got, and atypical (true for a bot, scanner or preview open, which is listed but in no average). No names, emails or IP addresses. |
Analytics for the whole account#
/api/v1/analyticsAPI key requiredEvery link the key's owner has, as one report, plus a leaderboard and the file-kind mix. Album and website pages are not included; their member files are.
Query parameters
rangestring- Same values and default as above.
allruns from your oldest link.
curl "https://linkinseconds.com/api/v1/analytics?range=7d" \
-H "Authorization: Bearer $LIS_API_KEY"{
"range": { "key": "7d", "from": "2026-09-28T00:00:00.000Z", "to": "2026-10-05T00:00:00.000Z", "days": 7, "label": "Last 7 days" },
"totals": { "views": 412, "uniques": 301, "sessions": 240, "avgEngagedMs": 64000, "completionPct": null, "downloads": 58, "botsExcluded": 19, "atypical": 11 },
"previous": { "views": 380, "uniques": 270, "avgEngagedMs": 60000, "downloads": 11, "sessions": 210 },
"deltas": { "views": { "pct": 8, "dir": "up" }, "uniques": { "pct": 11, "dir": "up" }, "avgEngagedMs": { "pct": 7, "dir": "up" }, "downloads": { "pct": 9, "dir": "up" } },
"granularity": "day",
"series": [ { "date": "2026-09-28", "label": "Sep 28", "views": 51, "uniques": 40, "avgEngagedS": 58 } ],
"breakdowns": { "countries": [], "cities": [], "referrers": [], "sources": [], "devices": [], "browsers": [], "os": [], "screens": [] },
"timing": { "byHour": [], "byWeekday": [], "heat": [], "peakHour": 10, "peakWeekday": 3 },
"insights": [],
"top_links": [
{
"id": "6f1c…", "slug": "q3-summary", "title": "Q3 summary", "kind": "pdf",
"views": 128, "uniques": 96, "avgEngagedMs": 102000, "completionPct": 58, "downloads": 12,
"spark": [3, 5, 2, 9, 11, 4, 6]
}
],
"mix": [{ "key": "PDF", "count": 260, "pct": 63.1 }, { "key": "Images", "count": 152, "pct": 36.9 }],
"all_time": { "views": 9120, "downloads": 1033, "links": 42 },
"truncated": false
}top_links holds at most 25 rows, most viewed first, each with a spark of daily views (oldest first, up to 30 days). mix is views by file kind. all_time comes from the link rows and ignores the range. The account report has no content or recent block; ask a single link for those. truncated (on both endpoints) is true when a very busy window hit the row cap: the newest views were counted and some older ones in the range were not, so totals and deltas are lower bounds. Ask for a shorter range for exact numbers.
Errors#
| Status | code | What happened |
|---|---|---|
| 400 | bad_range | range is not one of 7d, 14d, 30d, 90d, all. |
| 401 | unauthorized | Missing, malformed or revoked API key. |
| 403 | pro_required | The account is on the Free plan. The body carries a docs link; upgrade to read the report. |
| 403 | banned | The account is suspended. |
| 404 | not_found | No link with that slug belongs to this account (link endpoint only). |
See Errors and limits for the shared conventions. Responses are sent with Cache-Control: no-store and the usual CORS headers, so a browser dashboard can call them directly with a key you control.
How the numbers are made
totals.botsExcluded. Atypical sessions (a bot, or an email scanner or link preview that rendered the page for under a second) are counted in totals.atypical, flagged in recent and kept out of every average. Opens recorded before engagement tracking launched have views but no time data, so sessions can be lower than views. Details in Views and analytics.Last updated 4 October 2026. Something unclear or missing? Tell us.