Customer Example: E-commerce Integration
See how a Nordic e-commerce/affiliate site integrated Besökskollen and got data-driven "Popular Products" in 30 minutes.
Results
- Integration took ~30 minutes
- No external dependencies - just Next.js + Besökskollen
- Fallback logic based on confidence levels
- ISR caching for best performance
Background
A Nordic e-commerce site in electronics accessories wanted to show "Popular Products" based on actual clicks, not manually selected products.
Previously, they used a static list that was manually updated every other week. With Besökskollen, they now get real-time data on which products visitors actually click on.
Step-by-step
| Step | Time | What |
|---|---|---|
| 1 | 2 min | Registered event properties in the dashboard |
| 2 | 1 min | Created API key |
| 3 | 1 min | Saved credentials in .env.local |
| 4 | 10 min | Implemented va() tracking on affiliate links |
| 5 | 15 min | Built API client with fallback |
| 6 | 5 min | Integrated with product lists |
1. Register event properties
First, they registered which properties they wanted to save. In Settings → Event Properties they clicked "Add all" to get the most common ones:
product_id- Unique product IDcategory_slug- Product categorypartner_slug- Affiliate partnerprice- Price at click time
2-3. Create API key and save credentials
In Settings → API Keys they created a key and saved it in .env.local:
# .env.local BESOKSKOLLEN_API_KEY=bk_xxxxxxxxxxxx BESOKSKOLLEN_SITE_ID=din-sajt-id
4. Implement va() tracking
They added tracking to their affiliate link component. When a user clicks, an event is sent with relevant properties:
// components/AffiliateLinkButton.tsx
'use client';
interface Props {
productId: string;
categorySlug: string;
partnerSlug: string;
price: number;
href: string;
children: React.ReactNode;
}
export function AffiliateLinkButton({
productId, categorySlug, partnerSlug, price, href, children
}: Props) {
const handleClick = () => {
// Send event to analytics
if (typeof window !== 'undefined' && window.va) {
window.va('event', 'affiliate_click', {
product_id: productId,
category_slug: categorySlug,
partner_slug: partnerSlug,
price: price
});
}
};
return (
<a
href={href}
onClick={handleClick}
target="_blank"
rel="noopener noreferrer"
className="btn-primary"
>
{children}
</a>
);
}Tip: Add TypeScript types for window.va in a types/analytics.d.ts file.
5. Build API client with fallback
The key to a robust integration is handling the case when there's too little data. They built a client that automatically falls back to an alternative data source:
// lib/besokskollen.ts
const API_KEY = process.env.BESOKSKOLLEN_API_KEY;
const SITE_ID = process.env.BESOKSKOLLEN_SITE_ID;
const BASE_URL = 'https://besokskollen.se/api/v1';
interface PopularProductsResponse {
data: Array<{
product_id: string;
product_slug?: string;
clicks: number;
}>;
meta: {
confidence: 'high' | 'medium' | 'low';
total_clicks: number;
};
}
export async function getPopularProductIds(limit = 10) {
try {
const res = await fetch(
`${BASE_URL}/popular/products?site_id=${SITE_ID}&days=30&limit=${limit}`,
{
headers: { 'Authorization': `Bearer ${API_KEY}` },
next: { revalidate: 1800 } // 30 min ISR
}
);
if (!res.ok) throw new Error('API error');
const data: PopularProductsResponse = await res.json();
return {
ids: data.data.map(p => p.product_id),
confidence: data.meta.confidence
};
} catch (error) {
console.error('Besökskollen error:', error);
return { ids: [], confidence: 'low' as const };
}
}6. Integrate with product lists
Finally, they used the API client in their product list with smart fallback:
// lib/queries.ts
import { getPopularProductIds } from './besokskollen';
export async function getPopularProducts(limit = 12) {
// Try to fetch from analytics
const { ids, confidence } = await getPopularProductIds(limit);
if (confidence !== 'low' && ids.length > 0) {
// Fetch products from your database based on IDs
const products = await db.query.products.findMany({
where: inArray(products.id, ids),
limit
});
// Sort in the same order as analytics returned
return products.sort((a, b) =>
ids.indexOf(a.id) - ids.indexOf(b.id)
);
}
// Fallback: Most recently updated products
return db.query.products.findMany({
orderBy: desc(products.updatedAt),
limit
});
}Confidence Levels
- high - 100+ events, 10+ products → Show "Popular Products"
- medium - 20+ events → Show with warning or mix
- low - Little data → Use fallback
Final Result
After the integration, the site now has:
- Data-driven product lists - "Popular Products" based on actual clicks
- Robust fallback - Works even with little data
- Minimal overhead - ISR caching provides fast page loading
- Insights in the dashboard - Can see which products are trending in the Properties tab
"The integration was surprisingly simple. The RFC documentation was clear, the API is standard REST with Bearer token, and the confidence level makes fallback logic trivial."
— Developer, Nordic e-commerce site
Want to do the same?
- Affiliate Tracking Guide - Complete step-by-step
- Public API - All endpoints documented
- Event Properties - Which properties to save