Link Analytics
Retrieve detailed click analytics and performance metrics for your links.
Overview
LinkForty provides comprehensive analytics for every link, including:
- Click counts (total and unique)
- Geographic distribution
- Device and platform breakdown
- Time-based patterns
- UTM campaign performance
- Referrer analysis
Get Link Analytics
GET /api/analytics/links/:linkId
Retrieve detailed analytics for a specific link.
Authentication
Requires authentication via:
- Bearer token (user session)
- API key in
Authorizationheader
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
linkId | string (UUID) | Yes | Link ID |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
days | number | No | 30 | Number of days of data to include (1-365) |
Response
{
"totalClicks": 1248,
"uniqueClicks": 892,
"clicksByDate": [
{
"date": "2024-03-15",
"clicks": 45
},
{
"date": "2024-03-14",
"clicks": 38
}
],
"clicksByCountry": [
{
"countryCode": "US",
"country": "United States",
"clicks": 523
},
{
"countryCode": "CA",
"country": "Canada",
"clicks": 187
}
],
"clicksByCity": [
{
"city": "New York",
"countryCode": "US",
"region": "NY",
"clicks": 142
},
{
"city": "Los Angeles",
"countryCode": "US",
"region": "CA",
"clicks": 98
}
],
"clicksByRegion": [
{
"region": "California",
"countryCode": "US",
"clicks": 256
}
],
"clicksByTimezone": [
{
"timezone": "America/New_York",
"clicks": 312
}
],
"clicksByDevice": [
{
"device": "mobile",
"clicks": 742
},
{
"device": "desktop",
"clicks": 398
},
{
"device": "tablet",
"clicks": 108
}
],
"clicksByPlatform": [
{
"platform": "iOS",
"clicks": 623
},
{
"platform": "Android",
"clicks": 425
},
{
"platform": "Windows",
"clicks": 200
}
]
}
Example Request
Last 30 days (default):
curl https://api.linkforty.com/api/analytics/links/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer $LINKFORTY_API_KEY"
Last 7 days:
curl "https://api.linkforty.com/api/analytics/links/a1b2c3d4-e5f6-7890-abcd-ef1234567890?days=7" \
-H "Authorization: Bearer $LINKFORTY_API_KEY"
Last 90 days:
curl "https://api.linkforty.com/api/analytics/links/a1b2c3d4-e5f6-7890-abcd-ef1234567890?days=90" \
-H "Authorization: Bearer $LINKFORTY_API_KEY"
Get Click Events
GET /api/analytics/clicks/:linkId
Retrieve raw click event data for detailed analysis.
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
limit | number | No | 100 | Maximum events to return (1-1000) |
Response
[
{
"id": "click_1",
"linkId": "link_abc123",
"clickedAt": "2024-03-15T14:23:12Z",
"ipAddress": "192.168.1.1",
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)",
"deviceType": "mobile",
"platform": "iOS",
"browser": "Safari",
"browserVersion": "17.0",
"os": "iOS",
"osVersion": "17.0",
"countryCode": "US",
"countryName": "United States",
"region": "California",
"city": "San Francisco",
"timezone": "America/Los_Angeles",
"latitude": 37.7749,
"longitude": -122.4194,
"utmSource": "instagram",
"utmMedium": "social",
"utmCampaign": "spring-sale",
"utmTerm": null,
"utmContent": "story-1",
"referrer": "https://www.instagram.com/"
}
]
Example Request
curl "https://api.linkforty.com/api/analytics/clicks/a1b2c3d4-e5f6-7890-abcd-ef1234567890?limit=50" \
-H "Authorization: Bearer $LINKFORTY_API_KEY"
TypeScript Examples
1. Display Analytics Dashboard
interface LinkAnalytics {
totalClicks: number;
uniqueClicks: number;
clicksByDate: Array<{ date: string; clicks: number }>;
clicksByCountry: Array<{ countryCode: string; country: string; clicks: number }>;
clicksByDevice: Array<{ device: string; clicks: number }>;
clicksByPlatform: Array<{ platform: string; clicks: number }>;
}
async function getLinkAnalytics(linkId: string, days: number = 30): Promise<LinkAnalytics> {
const response = await fetch(
`https://api.linkforty.com/api/analytics/links/${linkId}?days=${days}`,
{
headers: { 'Authorization': `Bearer ${API_KEY}` },
}
);
return response.json();
}
// Usage
const analytics = await getLinkAnalytics('link_id', 7);
console.log(`Total Clicks: ${analytics.totalClicks}`);
console.log(`Unique Visitors: ${analytics.uniqueClicks}`);
console.log(`Top Country: ${analytics.clicksByCountry[0].country}`);
console.log(`Primary Device: ${analytics.clicksByDevice[0].device}`);
2. Calculate Conversion Rate
async function getConversionRate(linkId: string) {
const analytics = await getLinkAnalytics(linkId);
const conversions = 42; // From your conversion tracking
const conversionRate = (conversions / analytics.totalClicks) * 100;
return {
clicks: analytics.totalClicks,
conversions,
rate: conversionRate.toFixed(2) + '%',
};
}
3. Compare Link Performance
async function compareLinks(linkIds: string[], days: number = 30) {
const results = await Promise.all(
linkIds.map(async id => {
const analytics = await getLinkAnalytics(id, days);
const link = await getLink(id);
return {
linkId: id,
shortCode: link.shortCode,
title: link.title,
clicks: analytics.totalClicks,
uniqueClicks: analytics.uniqueClicks,
ctr: ((analytics.uniqueClicks / analytics.totalClicks) * 100).toFixed(1),
};
})
);
// Sort by clicks
return results.sort((a, b) => b.clicks - a.clicks);
}
// Usage
const comparison = await compareLinks(['link1', 'link2', 'link3']);
console.table(comparison);
4. Generate Analytics Report
async function generateReport(linkId: string, days: number = 30) {
const analytics = await getLinkAnalytics(linkId, days);
const link = await getLink(linkId);
return {
summary: {
linkTitle: link.title,
shortCode: link.shortCode,
period: `Last ${days} days`,
totalClicks: analytics.totalClicks,
uniqueClicks: analytics.uniqueClicks,
avgClicksPerDay: (analytics.totalClicks / days).toFixed(1),
},
geographic: {
topCountries: analytics.clicksByCountry.slice(0, 5),
topCities: analytics.clicksByCity?.slice(0, 5) || [],
},
devices: {
byType: analytics.clicksByDevice,
byPlatform: analytics.clicksByPlatform,
mobilePercentage: (
(analytics.clicksByDevice.find(d => d.device === 'mobile')?.clicks || 0) /
analytics.totalClicks *
100
).toFixed(1),
},
timeline: analytics.clicksByDate,
};
}
5. Export to CSV
async function exportAnalyticsToCSV(linkId: string, days: number = 30) {
const clicks = await fetch(
`https://api.linkforty.com/api/analytics/clicks/${linkId}?limit=1000`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
).then(r => r.json());
const csv = [
// Header
'Date,Time,Country,City,Device,Platform,Browser,UTM Source,UTM Campaign',
// Rows
...clicks.map(click => [
new Date(click.clickedAt).toLocaleDateString(),
new Date(click.clickedAt).toLocaleTimeString(),
click.countryName || 'Unknown',
click.city || 'Unknown',
click.deviceType || 'Unknown',
click.platform || 'Unknown',
click.browser || 'Unknown',
click.utmSource || 'Direct',
click.utmCampaign || 'None',
].join(','))
].join('\n');
return csv;
}
// Save to file
const csv = await exportAnalyticsToCSV('link_id');
await fs.writeFile('analytics.csv', csv);
Common Analytics Patterns
1. Track Campaign Performance
async function getCampaignPerformance(linkId: string) {
const analytics = await getLinkAnalytics(linkId);
const clicks = await getClickEvents(linkId, 1000);
// Group by UTM campaign
const byCampaign = clicks.reduce((acc, click) => {
const campaign = click.utmCampaign || 'None';
acc[campaign] = (acc[campaign] || 0) + 1;
return acc;
}, {} as Record<string, number>);
return Object.entries(byCampaign)
.map(([campaign, clicks]) => ({ campaign, clicks }))
.sort((a, b) => b.clicks - a.clicks);
}
2. Find Peak Traffic Times
async function getPeakHours(linkId: string) {
const clicks = await getClickEvents(linkId, 1000);
const byHour = clicks.reduce((acc, click) => {
const hour = new Date(click.clickedAt).getHours();
acc[hour] = (acc[hour] || 0) + 1;
return acc;
}, {} as Record<number, number>);
const sorted = Object.entries(byHour)
.map(([hour, clicks]) => ({ hour: parseInt(hour), clicks }))
.sort((a, b) => b.clicks - a.clicks);
return {
peakHour: sorted[0].hour,
peakClicks: sorted[0].clicks,
distribution: sorted,
};
}
3. Calculate Growth Rate
async function getGrowthRate(linkId: string) {
const last7Days = await getLinkAnalytics(linkId, 7);
const prev7Days = await getLinkAnalytics(linkId, 14);
const currentWeek = last7Days.totalClicks;
const previousWeek = prev7Days.totalClicks - currentWeek;
const growth = ((currentWeek - previousWeek) / previousWeek) * 100;
return {
currentWeek,
previousWeek,
growth: growth.toFixed(1) + '%',
isGrowing: growth > 0,
};
}
4. Detect Geographic Patterns
async function getGeographicInsights(linkId: string) {
const analytics = await getLinkAnalytics(linkId);
const totalClicks = analytics.totalClicks;
return {
topCountry: analytics.clicksByCountry[0],
diversity: analytics.clicksByCountry.length,
concentration: (
(analytics.clicksByCountry[0]?.clicks || 0) / totalClicks * 100
).toFixed(1) + '%',
internationalPercentage: (
analytics.clicksByCountry
.filter(c => c.countryCode !== 'US')
.reduce((sum, c) => sum + c.clicks, 0) /
totalClicks *
100
).toFixed(1) + '%',
};
}
Error Responses
Link Not Found
{
"statusCode": 404,
"error": "Not Found",
"message": "Link not found"
}
Invalid Days Parameter
{
"statusCode": 400,
"error": "Bad Request",
"message": "Days must be between 1 and 365"
}
Best Practices
1. Cache Analytics Data
const analyticsCache = new Map<string, { data: LinkAnalytics; timestamp: number }>();
async function getCachedAnalytics(linkId: string, ttl: number = 300000) {
const cached = analyticsCache.get(linkId);
if (cached && Date.now() - cached.timestamp < ttl) {
return cached.data;
}
const data = await getLinkAnalytics(linkId);
analyticsCache.set(linkId, { data, timestamp: Date.now() });
return data;
}
2. Handle Large Date Ranges
async function getAnalyticsInChunks(linkId: string, totalDays: number) {
const chunkSize = 30;
const chunks = Math.ceil(totalDays / chunkSize);
const results = [];
for (let i = 0; i < chunks; i++) {
const days = Math.min(chunkSize, totalDays - i * chunkSize);
const analytics = await getLinkAnalytics(linkId, days);
results.push(analytics);
// Wait between requests
await new Promise(resolve => setTimeout(resolve, 100));
}
return mergeAnalytics(results);
}
3. Real-Time Updates
function useRealtimeAnalytics(linkId: string, refreshInterval: number = 60000) {
const [analytics, setAnalytics] = useState<LinkAnalytics | null>(null);
useEffect(() => {
const fetchAnalytics = async () => {
const data = await getLinkAnalytics(linkId, 1); // Last 24 hours
setAnalytics(data);
};
fetchAnalytics();
const interval = setInterval(fetchAnalytics, refreshInterval);
return () => clearInterval(interval);
}, [linkId, refreshInterval]);
return analytics;
}
Related Endpoints
- Get Link - Link details and basic click count
- Overview Analytics - Organization-wide analytics
- Data Export - Export raw analytics data
Guides
- Analytics Dashboard - Understanding your analytics
- Campaign Tracking - Setting up UTM tracking
- Attribution Windows - Configuring attribution