Appearance
Analytics API
The Analytics API gives you programmatic access to your click data. Use it to build custom dashboards, calculate ROI by merchant or network, monitor routing performance, or feed data into your own BI tools.
All analytics data comes from the click records generated by the Fyralynx edge redirector. Only clicks that passed through the routing engine are present - bot traffic (Cloudflare Bot Score < 30), simulated clicks (CRC Simulate action), and /route endpoint calls are never counted.
Base path: https://api.fyralynx.com/v1
Tier required: Publisher or Enterprise
Privacy Model
Analytics data in the API is always anonymised:
- No visitor PII (no IP address, no user ID, no fingerprint).
- No creator account ID in individual click records - only your own data is accessible via your API key.
- No third-party tracking. No pixel injection.
- Data is stored on Cloudflare's global infrastructure and deleted after 90 days.
- GDPR Article 7 and CCPA compliant.
If a creator is in local-only analytics mode (the default in the browser extension), their click records are stored only in browser.storage.local and are not present in this API. API analytics are only populated when the creator has opted in to server-side analytics.
The Click Record Object
Individual click records returned by GET /analytics/clicks:
json
{
"clickId": "clk_01HX4Z9Y3ABCDEF1234567890AB",
"timestamp": "2026-05-15T14:30:22Z",
"linkId": "01HX4Z9Y3ABCDEF1234567890AB",
"merchantId": "amazon",
"networkId": "Impact",
"region": "US",
"country": "US",
"device": "desktop",
"preset": "auto",
"confidence": 87,
"normalizedEpcUsd": 0.043,
"deepLinkUsed": true,
"passthrough": false
}Field reference
| Field | Type | Description |
|---|---|---|
clickId | string | Unique click identifier. Use as idempotency key for webhook processing. |
timestamp | string (ISO 8601) | When the edge redirector served the click. |
linkId | string (UUID) | The link that was clicked. May be absent for clicks on links that have since been deleted. |
merchantId | string | Merchant the visitor was routed to. |
networkId | string | Affiliate network used for the route. |
region | string | Storefront region (e.g. US, IN, NG). |
country | string | Visitor country (ISO 3166-1 alpha-2), resolved from Cloudflare's geo-IP. |
device | string | Visitor device type: desktop, ios, android. |
preset | string | Routing preset applied: auto, link-in-bio, or long-form. |
confidence | number | Routing confidence score (0-100) at the time of click. |
normalizedEpcUsd | number | EPC estimate (USD) from the winning candidate's profile at the time of click. |
deepLinkUsed | boolean | Whether an app deep link was applied. |
passthrough | boolean | Whether the link was in passthrough mode. |
The Analytics Summary Object
Aggregated analytics returned by GET /analytics/summary:
json
{
"dateFrom": "2026-05-01",
"dateTo": "2026-05-15",
"totalClicks": 12847,
"totalPassthroughClicks": 1203,
"totalRoutedClicks": 11644,
"averageConfidence": 83.4,
"averageNormalizedEpcUsd": 0.038,
"topMerchants": [
{ "merchantId": "amazon", "clicks": 5210, "pct": 44.7 },
{ "merchantId": "flipkart", "clicks": 2103, "pct": 18.1 },
{ "merchantId": "jumia", "clicks": 1847, "pct": 15.9 }
],
"topNetworks": [
{ "networkId": "Impact", "clicks": 4800, "pct": 41.2 },
{ "networkId": "Cuelinks", "clicks": 3200, "pct": 27.5 },
{ "networkId": "CJ", "clicks": 1900, "pct": 16.3 }
],
"byPreset": {
"auto": { "clicks": 9200, "pct": 79.0 },
"link-in-bio": { "clicks": 1800, "pct": 15.5 },
"long-form": { "clicks": 644, "pct": 5.5 }
},
"byRegion": {
"US": { "clicks": 4100, "pct": 35.2 },
"IN": { "clicks": 3800, "pct": 32.6 },
"NG": { "clicks": 1200, "pct": 10.3 },
"GB": { "clicks": 890, "pct": 7.6 }
},
"byDevice": {
"desktop": { "clicks": 5200, "pct": 44.7 },
"android": { "clicks": 4900, "pct": 42.1 },
"ios": { "clicks": 1544, "pct": 13.3 }
},
"byCountry": {
"US": { "clicks": 4100 },
"IN": { "clicks": 3800 },
"NG": { "clicks": 1200 },
"GB": { "clicks": 890 },
"DE": { "clicks": 420 }
},
"deepLinkUsageRate": 0.31,
"passthroughRate": 0.094
}GET /analytics/clicks - List click records
Returns paginated individual click records within the requested date range.
Tier: Publisher+
Maximum date range per request: 90 days
Order: Timestamp descending (newest first)
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
dateFrom | string (ISO 8601 date) | ✅ | Start of date range, inclusive. Format: YYYY-MM-DD. |
dateTo | string (ISO 8601 date) | ✅ | End of date range, inclusive. Format: YYYY-MM-DD. |
merchantId | string | Filter by merchant ID. | |
networkId | string | Filter by affiliate network. | |
preset | string | Filter by routing preset: auto, link-in-bio, long-form. | |
country | string (ISO 3166-1 alpha-2) | Filter by visitor country. | |
device | string | Filter by device type: desktop, ios, android. | |
page | integer | 1-based page number. Default: 1. | |
limit | integer | Items per page (1-100). Default: 20. |
Response - 200 OK
json
{
"data": [
{
"clickId": "clk_01HX4Z9Y3ABCDEF1234567890AB",
"timestamp": "2026-05-15T14:30:22Z",
"linkId": "01HX4Z9Y3ABCDEF1234567890AB",
"merchantId": "amazon",
"networkId": "Impact",
"region": "US",
"country": "US",
"device": "desktop",
"preset": "auto",
"confidence": 87,
"normalizedEpcUsd": 0.043,
"deepLinkUsed": true,
"passthrough": false
},
{
"clickId": "clk_01HX4Z9Z3ABCDEF1234567890CD",
"timestamp": "2026-05-15T14:28:45Z",
"linkId": "01HX4Z9Z3ABCDEF1234567890CD",
"merchantId": "jumia",
"networkId": "InvolveAsia",
"region": "NG",
"country": "NG",
"device": "android",
"preset": "link-in-bio",
"confidence": 74,
"normalizedEpcUsd": 0.021,
"deepLinkUsed": false,
"passthrough": false
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 12847,
"totalPages": 643,
"hasNextPage": true,
"hasPrevPage": false
}
}curl examples
bash
# All clicks for May 2026
curl "https://api.fyralynx.com/v1/analytics/clicks?dateFrom=2026-05-01&dateTo=2026-05-31" \
-H "Authorization: Bearer $FYRALYNX_API_KEY"
# Amazon clicks from Nigerian visitors on Android
curl "https://api.fyralynx.com/v1/analytics/clicks?dateFrom=2026-05-01&dateTo=2026-05-31&merchantId=amazon&country=NG&device=android" \
-H "Authorization: Bearer $FYRALYNX_API_KEY"
# Impact network clicks, page 2
curl "https://api.fyralynx.com/v1/analytics/clicks?dateFrom=2026-05-01&dateTo=2026-05-31&networkId=Impact&page=2&limit=50" \
-H "Authorization: Bearer $FYRALYNX_API_KEY"TypeScript - export all clicks for a month
typescript
interface ClickRecord {
clickId: string;
timestamp: string;
merchantId: string;
networkId: string;
region: string;
country: string;
device: string;
preset: string;
confidence: number;
normalizedEpcUsd: number;
deepLinkUsed: boolean;
passthrough: boolean;
}
async function getAllClicksForMonth(year: number, month: number): Promise<ClickRecord[]> {
const dateFrom = `${year}-${String(month).padStart(2, '0')}-01`;
const lastDay = new Date(year, month, 0).getDate();
const dateTo = `${year}-${String(month).padStart(2, '0')}-${lastDay}`;
const allClicks: ClickRecord[] = [];
let page = 1;
let hasNextPage = true;
while (hasNextPage) {
const params = new URLSearchParams({
dateFrom,
dateTo,
page: String(page),
limit: '100',
});
const response = await fetch(
`https://api.fyralynx.com/v1/analytics/clicks?${params}`,
{
headers: {
Authorization: `Bearer ${process.env.FYRALYNX_API_KEY}`,
},
},
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Fyralynx ${response.status}: ${error.code}`);
}
const { data, pagination } = await response.json();
allClicks.push(...data);
hasNextPage = pagination.hasNextPage;
page++;
}
return allClicks;
}Python example
python
import httpx
from datetime import date
def get_all_clicks(date_from: str, date_to: str) -> list[dict]:
"""Fetch all click records for a date range, handling pagination."""
client = httpx.Client(
base_url="https://api.fyralynx.com/v1",
headers={"Authorization": f"Bearer {FYRALYNX_API_KEY}"},
)
all_clicks = []
page = 1
while True:
response = client.get("/analytics/clicks", params={
"dateFrom": date_from,
"dateTo": date_to,
"page": page,
"limit": 100,
})
response.raise_for_status()
data = response.json()
all_clicks.extend(data["data"])
if not data["pagination"]["hasNextPage"]:
break
page += 1
return all_clicksGET /analytics/summary - Get analytics summary
Returns aggregated totals for the requested date range: total clicks, top merchants, top networks, breakdown by preset, region, device, and country.
Tier: Publisher+
Maximum date range per request: 365 days
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
dateFrom | string (ISO 8601 date) | ✅ | Start of date range, inclusive. |
dateTo | string (ISO 8601 date) | ✅ | End of date range, inclusive. |
Response - 200 OK
Full analytics summary as shown in The Analytics Summary Object above.
curl examples
bash
# May 2026 summary
curl "https://api.fyralynx.com/v1/analytics/summary?dateFrom=2026-05-01&dateTo=2026-05-31" \
-H "Authorization: Bearer $FYRALYNX_API_KEY"
# Last 90 days summary
curl "https://api.fyralynx.com/v1/analytics/summary?dateFrom=2026-02-15&dateTo=2026-05-15" \
-H "Authorization: Bearer $FYRALYNX_API_KEY"TypeScript example
typescript
interface AnalyticsSummary {
dateFrom: string;
dateTo: string;
totalClicks: number;
totalPassthroughClicks: number;
totalRoutedClicks: number;
averageConfidence: number;
averageNormalizedEpcUsd: number;
topMerchants: Array<{ merchantId: string; clicks: number; pct: number }>;
topNetworks: Array<{ networkId: string; clicks: number; pct: number }>;
byPreset: Record<string, { clicks: number; pct: number }>;
byRegion: Record<string, { clicks: number; pct: number }>;
byDevice: Record<string, { clicks: number; pct: number }>;
byCountry: Record<string, { clicks: number }>;
deepLinkUsageRate: number;
passthroughRate: number;
}
async function getAnalyticsSummary(dateFrom: string, dateTo: string): Promise<AnalyticsSummary> {
const params = new URLSearchParams({ dateFrom, dateTo });
const response = await fetch(
`https://api.fyralynx.com/v1/analytics/summary?${params}`,
{
headers: {
Authorization: `Bearer ${process.env.FYRALYNX_API_KEY}`,
},
},
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Fyralynx ${response.status}: ${error.code}`);
}
const { data } = await response.json();
return data;
}Common Analytics Patterns
Track which merchant earns the most per click
typescript
async function getEpcByMerchant(dateFrom: string, dateTo: string) {
// Fetch all clicks (handle pagination)
const clicks = await getAllClicksForMonth(2026, 5);
// Group by merchant and compute average EPC
const byMerchant = new Map<string, { total: number; count: number }>();
for (const click of clicks) {
if (!click.passthrough) {
const existing = byMerchant.get(click.merchantId) ?? { total: 0, count: 0 };
byMerchant.set(click.merchantId, {
total: existing.total + click.normalizedEpcUsd,
count: existing.count + 1,
});
}
}
return Array.from(byMerchant.entries())
.map(([merchantId, { total, count }]) => ({
merchantId,
avgEpcUsd: total / count,
clicks: count,
}))
.sort((a, b) => b.avgEpcUsd - a.avgEpcUsd);
}Monitor routing confidence trends
typescript
async function getConfidenceTrend(dateFrom: string, dateTo: string) {
// Use summary instead of iterating all clicks
const summary = await getAnalyticsSummary(dateFrom, dateTo);
return {
averageConfidence: summary.averageConfidence,
status:
summary.averageConfidence >= 80 ? 'green' :
summary.averageConfidence >= 60 ? 'amber' :
'red',
};
}Find under-performing merchants (low confidence)
Confidence below 60 for a merchant indicates data uncertainty. Query click records filtered by merchant and inspect confidence values:
bash
curl "https://api.fyralynx.com/v1/analytics/clicks?dateFrom=2026-05-01&dateTo=2026-05-31&merchantId=temu&limit=100" \
-H "Authorization: Bearer $FYRALYNX_API_KEY"Identify geographic coverage gaps
typescript
async function findUnroutableCountries(dateFrom: string, dateTo: string) {
const summary = await getAnalyticsSummary(dateFrom, dateTo);
// High passthrough rate in specific countries suggests no matching merchant
const clicksByCountry = summary.byCountry;
return Object.entries(clicksByCountry)
.filter(([, { clicks }]) => clicks > 100)
.map(([country]) => country);
}Data Lag & Freshness
Click records appear in the API with a latency of approximately 5-30 seconds from the moment the edge redirector serves the click. The EWMA trend computation (used in the EPC formula) runs on an hourly schedule - so trend signals reflect data up to 1 hour old.
For real-time monitoring use the link.clicked webhook (Enterprise tier) - it fires within seconds of every tracked click.
Retention Policy
| Data | Retention |
|---|---|
| Individual click records | 90 days rolling. Records older than 90 days are deleted. |
| Aggregated summary data | Retained indefinitely (summary values are not raw records). |
| Local analytics (extension) | Until cleared by creator or extension uninstall. Not accessible via API. |
If you need longer retention, export click data via this API and store it in your own data warehouse before the 90-day window expires.
Relationship to Local Analytics
The browser extension stores the last 500 click records in browser.storage.local regardless of plan tier. These local records are available for CSV export from the Collections screen but are not accessible via this API.
This API reflects only server-side analytics - data that was sent to Fyralynx's servers when the creator opted in to server-side telemetry. Creators in local-only mode will see totalClicks: 0 in API responses.
Error Reference
| Code | Status | Description |
|---|---|---|
VALIDATION_ERROR | 422 | dateFrom or dateTo is missing or not a valid ISO 8601 date. |
VALIDATION_ERROR | 422 | Date range exceeds the 90-day limit (clicks) or 365-day limit (summary). |
PUBLISHER_TIER_REQUIRED | 403 | API access requires Publisher or Enterprise plan. |
RATE_LIMIT_EXCEEDED | 429 | Rate limit exceeded. |
See Also
- API Overview - response format, pagination, error codes
- Account API - monthly click usage and billing period
- Webhooks - real-time
link.clickedevents (Enterprise) - Concepts: EPC Model - how
normalizedEpcUsdandconfidenceare computed