Tutorial 3: Analytics Dashboard
Learn how to track sales performance, analyze customer behavior, and generate data-driven insights.
What You'll Build
A complete analytics dashboard that:
- Fetches sales data and conversion metrics
- Analyzes search queries and product visibility
- Retrieves stock history and inventory trends
- Generates CSV reports for deeper analysis
Estimated Time: 30 minutes Difficulty: Intermediate
Learning Objectives
By the end of this tutorial, you'll be able to:
- ✅ Track sales funnel metrics (views → clicks → purchases)
- ✅ Analyze search query performance
- ✅ Monitor stock levels and inventory history
- ✅ Export data to CSV for external analysis
- ✅ Build data-driven insights for business decisions
Prerequisites
Before you begin, ensure you have:
- ✅ Node.js ≥ 20.0.0 installed
- ✅ Wildberries API key with analytics access
- ✅ SDK installed (
npm install wb-api-sdk) - ✅ Completed Quickstart Guide
- ✅ Some existing products and sales data
Introduction
Analytics are essential for understanding your business performance. Wildberries provides rich analytics covering:
Sales Funnel Analytics
- Views: How many times your products appeared in search/browse
- Add to Cart: How many users added products to cart
- Purchases: Completed transactions
- Conversion Rate: Percentage of views that become purchases
Search Analytics
- Search Queries: What customers search for
- Product Visibility: Where your products rank in search
- Click-Through Rate: How often your products get clicked
Stock Analytics
- Inventory Levels: Current stock per warehouse
- Stock History: Historical inventory trends
- Turnover Rate: How fast products sell
This tutorial shows you how to extract and analyze this data.
Step 1: Fetch Sales Data (10 minutes)
Let's start by retrieving sales funnel metrics for your products.
Understanding Sales Funnel
The sales funnel shows customer journey:
Views (1000)
↓ 20% click-through
Clicks (200)
↓ 40% add to cart
Add to Cart (80)
↓ 50% purchase
Purchases (40)
Conversion Rate: 4% (40/1000)Code Example
Create a file analytics-dashboard.ts:
import { WildberriesSDK } from 'wb-api-sdk';
const sdk = new WildberriesSDK({
apiKey: process.env.WB_API_KEY
});
async function getSalesData() {
try {
// Define date range (last 30 days)
const dateTo = new Date();
const dateFrom = new Date();
dateFrom.setDate(dateFrom.getDate() - 30);
console.log(`Fetching sales data from ${dateFrom.toISOString().split('T')[0]} to ${dateTo.toISOString().split('T')[0]}\n`);
// Get sales funnel data
const salesFunnel = await sdk.analytics.getSalesFunnel({
dateFrom: dateFrom.toISOString(),
dateTo: dateTo.toISOString()
});
console.log('=== Sales Funnel (Last 30 Days) ===\n');
salesFunnel.data.forEach(product => {
console.log(`Product: ${product.productName}`);
console.log(` SKU: ${product.sku}`);
console.log(` Views: ${product.views.toLocaleString()}`);
console.log(` Add to Cart: ${product.addToCarts.toLocaleString()}`);
console.log(` Purchases: ${product.purchases.toLocaleString()}`);
console.log(` Conversion Rate: ${product.conversionRate.toFixed(2)}%`);
console.log(` Revenue: ${product.revenue.toLocaleString()} RUB`);
console.log(` Average Order Value: ${product.averageOrderValue.toFixed(2)} RUB`);
console.log('');
});
// Calculate totals
const totals = salesFunnel.data.reduce((acc, p) => ({
views: acc.views + p.views,
purchases: acc.purchases + p.purchases,
revenue: acc.revenue + p.revenue
}), { views: 0, purchases: 0, revenue: 0 });
console.log('=== Totals ===');
console.log(` Total Views: ${totals.views.toLocaleString()}`);
console.log(` Total Purchases: ${totals.purchases.toLocaleString()}`);
console.log(` Total Revenue: ${totals.revenue.toLocaleString()} RUB`);
console.log(` Overall Conversion: ${((totals.purchases / totals.views) * 100).toFixed(2)}%`);
return salesFunnel.data;
} catch (error) {
console.error('Error fetching sales data:', error.message);
throw error;
}
}
getSalesData();Expected Output
Fetching sales data from 2024-09-26 to 2024-10-26
=== Sales Funnel (Last 30 Days) ===
Product: TechBrand Smartphone Pro 15
SKU: TB-SP15-BLK
Views: 15,240
Add to Cart: 1,829
Purchases: 427
Conversion Rate: 2.80%
Revenue: 1,279,273 RUB
Average Order Value: 2,996.43 RUB
Product: Phone Case Premium
SKU: PC-PREM-BLK
Views: 8,430
Add to Cart: 952
Purchases: 312
Conversion Rate: 3.70%
Revenue: 155,000 RUB
Average Order Value: 496.79 RUB
=== Totals ===
Total Views: 23,670
Total Purchases: 739
Total Revenue: 1,434,273 RUB
Overall Conversion: 3.12%Analyzing Top Performers
async function getTopPerformers() {
const dateFrom = new Date();
dateFrom.setDate(dateFrom.getDate() - 30);
const salesFunnel = await sdk.analytics.getSalesFunnel({
dateFrom: dateFrom.toISOString(),
dateTo: new Date().toISOString()
});
// Sort by revenue
const topByRevenue = [...salesFunnel.data]
.sort((a, b) => b.revenue - a.revenue)
.slice(0, 5);
console.log('Top 5 Products by Revenue:\n');
topByRevenue.forEach((p, i) => {
console.log(`${i + 1}. ${p.productName}`);
console.log(` Revenue: ${p.revenue.toLocaleString()} RUB`);
console.log(` Purchases: ${p.purchases}`);
});
// Sort by conversion rate
const topByConversion = [...salesFunnel.data]
.sort((a, b) => b.conversionRate - a.conversionRate)
.slice(0, 5);
console.log('\nTop 5 Products by Conversion Rate:\n');
topByConversion.forEach((p, i) => {
console.log(`${i + 1}. ${p.productName}`);
console.log(` Conversion: ${p.conversionRate.toFixed(2)}%`);
console.log(` Views: ${p.views.toLocaleString()}`);
});
}Step 2: Get Search Queries (5 minutes)
Understand what customers are searching for and how your products rank.
Search Query Metrics
- Search Term: What users typed
- Impressions: How often your products appeared in results
- Clicks: How often users clicked your products
- CTR: Click-through rate (clicks / impressions)
Code Example
async function getSearchQueries() {
try {
const dateFrom = new Date();
dateFrom.setDate(dateFrom.getDate() - 7); // Last 7 days
const queries = await sdk.analytics.getSearchQueries({
dateFrom: dateFrom.toISOString(),
dateTo: new Date().toISOString(),
limit: 20
});
console.log('=== Top Search Queries (Last 7 Days) ===\n');
queries.data.forEach(query => {
console.log(`Search: "${query.searchTerm}"`);
console.log(` Impressions: ${query.impressions.toLocaleString()}`);
console.log(` Clicks: ${query.clicks.toLocaleString()}`);
console.log(` CTR: ${query.clickThroughRate.toFixed(2)}%`);
console.log(` Avg Position: ${query.averagePosition.toFixed(1)}`);
console.log('');
});
// Identify high-impression, low-click queries (optimization opportunities)
const lowCTR = queries.data.filter(q => q.clickThroughRate < 2.0 && q.impressions > 100);
if (lowCTR.length > 0) {
console.log('⚠️ Optimization Opportunities (High impressions, Low CTR):\n');
lowCTR.forEach(q => {
console.log(` - "${q.searchTerm}"`);
console.log(` Impressions: ${q.impressions}, CTR: ${q.clickThroughRate.toFixed(2)}%`);
console.log(` → Consider improving product titles/images for this search`);
});
}
return queries.data;
} catch (error) {
console.error('Error fetching search queries:', error.message);
throw error;
}
}Expected Output
=== Top Search Queries (Last 7 Days) ===
Search: "смартфон недорогой"
Impressions: 4,230
Clicks: 127
CTR: 3.00%
Avg Position: 12.3
Search: "чехол для телефона"
Impressions: 3,890
Clicks: 156
CTR: 4.01%
Avg Position: 8.7
Search: "телефон черный"
Impressions: 2,450
Clicks: 49
CTR: 2.00%
Avg Position: 18.5
⚠️ Optimization Opportunities (High impressions, Low CTR):
- "телефон черный"
Impressions: 2450, CTR: 2.00%
→ Consider improving product titles/images for this searchStep 3: Retrieve Stock History (5 minutes)
Track inventory trends to optimize stock levels and prevent stockouts.
Stock History Metrics
- Warehouse: Which warehouse holds inventory
- Date: Historical timestamp
- Quantity: Stock level at that time
- Turnover: How fast items sell
Code Example
async function getStockHistory(sku: string) {
try {
const dateFrom = new Date();
dateFrom.setDate(dateFrom.getDate() - 30);
const history = await sdk.analytics.getStockHistory({
sku: sku,
dateFrom: dateFrom.toISOString(),
dateTo: new Date().toISOString()
});
console.log(`=== Stock History for ${sku} (Last 30 Days) ===\n`);
// Group by warehouse
const byWarehouse = history.data.reduce((acc, entry) => {
if (!acc[entry.warehouseId]) {
acc[entry.warehouseId] = [];
}
acc[entry.warehouseId].push(entry);
return acc;
}, {} as Record<string, typeof history.data>);
Object.entries(byWarehouse).forEach(([warehouseId, entries]) => {
console.log(`Warehouse: ${warehouseId}`);
const latestStock = entries[entries.length - 1].quantity;
const initialStock = entries[0].quantity;
const stockChange = latestStock - initialStock;
console.log(` Current Stock: ${latestStock}`);
console.log(` 30 Days Ago: ${initialStock}`);
console.log(` Change: ${stockChange >= 0 ? '+' : ''}${stockChange} units`);
// Calculate average daily turnover
const sold = initialStock - latestStock;
const days = 30;
const dailyTurnover = sold / days;
console.log(` Avg Daily Turnover: ${dailyTurnover.toFixed(1)} units/day`);
console.log(` Days Until Stockout: ${latestStock > 0 ? (latestStock / dailyTurnover).toFixed(1) : 'OUT OF STOCK'}`);
console.log('');
});
return history.data;
} catch (error) {
console.error('Error fetching stock history:', error.message);
throw error;
}
}
// Example usage
getStockHistory('TB-SP15-BLK');Expected Output
=== Stock History for TB-SP15-BLK (Last 30 Days) ===
Warehouse: WH-MOSCOW-01
Current Stock: 245
30 Days Ago: 500
Change: -255 units
Avg Daily Turnover: 8.5 units/day
Days Until Stockout: 28.8
Warehouse: WH-SPB-02
Current Stock: 89
30 Days Ago: 200
Change: -111 units
Avg Daily Turnover: 3.7 units/day
Days Until Stockout: 24.1Low Stock Alerts
async function checkLowStock(threshold: number = 30) {
const products = await sdk.products.listProducts();
const lowStockProducts = [];
for (const product of products.data) {
const stock = await sdk.products.getStock(product.sku);
const totalStock = stock.data.reduce((sum, w) => sum + w.quantity, 0);
if (totalStock < threshold) {
lowStockProducts.push({
sku: product.sku,
name: product.name,
stock: totalStock
});
}
}
if (lowStockProducts.length > 0) {
console.log('⚠️ Low Stock Alert:\n');
lowStockProducts.forEach(p => {
console.log(` - ${p.name} (${p.sku})`);
console.log(` Stock: ${p.stock} units (below threshold of ${threshold})`);
});
} else {
console.log('✓ All products have sufficient stock');
}
}Step 4: Generate CSV Reports (5 minutes)
Export analytics data to CSV for deeper analysis in Excel, Google Sheets, or BI tools.
CSV Report Types
Wildberries supports several report types:
- Sales Report: Detailed transaction history
- Product Performance: Metrics per product
- Financial Report: Revenue, fees, payouts
- Stock Report: Inventory levels and turnover
Code Example
async function generateCSVReport() {
try {
console.log('Generating CSV report...');
// Request report generation
const reportRequest = await sdk.analytics.generateCSVReport({
reportType: 'sales',
dateFrom: new Date('2024-09-01').toISOString(),
dateTo: new Date('2024-09-30').toISOString(),
format: 'csv'
});
console.log(`✓ Report requested`);
console.log(` Report ID: ${reportRequest.data.reportId}`);
console.log(` Status: ${reportRequest.data.status}`); // 'pending'
// Poll for completion
console.log('\nWaiting for report generation...');
let reportStatus;
let attempts = 0;
const maxAttempts = 30; // 30 seconds timeout
while (attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second
reportStatus = await sdk.analytics.getCSVReportStatus(reportRequest.data.reportId);
if (reportStatus.data.status === 'completed') {
console.log('✓ Report ready!');
break;
} else if (reportStatus.data.status === 'failed') {
throw new Error('Report generation failed');
}
attempts++;
process.stdout.write('.');
}
console.log('');
if (reportStatus.data.status !== 'completed') {
throw new Error('Report generation timeout');
}
// Download report
console.log('Downloading report...');
const reportData = await sdk.analytics.downloadCSVReport(reportRequest.data.reportId);
// Save to file
const fs = require('fs');
const fileName = `sales-report-${new Date().toISOString().split('T')[0]}.csv`;
fs.writeFileSync(fileName, reportData.data);
console.log(`✓ Report saved to ${fileName}`);
console.log(` File size: ${(reportData.data.length / 1024).toFixed(2)} KB`);
return fileName;
} catch (error) {
console.error('Error generating report:', error.message);
throw error;
}
}Expected Output
Generating CSV report...
✓ Report requested
Report ID: RPT-789012
Status: pending
Waiting for report generation...
......✓ Report ready!
Downloading report...
✓ Report saved to sales-report-2024-10-26.csv
File size: 245.67 KBComplete Example: Analytics Dashboard
Here's a comprehensive analytics dashboard combining all metrics:
import { WildberriesSDK } from 'wb-api-sdk';
const sdk = new WildberriesSDK({
apiKey: process.env.WB_API_KEY
});
async function runAnalyticsDashboard() {
try {
console.log('=== Wildberries Analytics Dashboard ===\n');
const dateFrom = new Date();
dateFrom.setDate(dateFrom.getDate() - 30);
const dateTo = new Date();
// 1. Sales Overview
console.log('📊 Sales Overview (Last 30 Days)\n');
const salesFunnel = await sdk.analytics.getSalesFunnel({
dateFrom: dateFrom.toISOString(),
dateTo: dateTo.toISOString()
});
const totals = salesFunnel.data.reduce((acc, p) => ({
views: acc.views + p.views,
purchases: acc.purchases + p.purchases,
revenue: acc.revenue + p.revenue
}), { views: 0, purchases: 0, revenue: 0 });
console.log(`Total Views: ${totals.views.toLocaleString()}`);
console.log(`Total Purchases: ${totals.purchases.toLocaleString()}`);
console.log(`Total Revenue: ${totals.revenue.toLocaleString()} RUB`);
console.log(`Conversion Rate: ${((totals.purchases / totals.views) * 100).toFixed(2)}%`);
// 2. Top Products
console.log('\n🏆 Top 5 Products by Revenue\n');
const topProducts = [...salesFunnel.data]
.sort((a, b) => b.revenue - a.revenue)
.slice(0, 5);
topProducts.forEach((p, i) => {
console.log(`${i + 1}. ${p.productName}`);
console.log(` Revenue: ${p.revenue.toLocaleString()} RUB`);
console.log(` Conversion: ${p.conversionRate.toFixed(2)}%\n`);
});
// 3. Search Performance
console.log('🔍 Top Search Queries (Last 7 Days)\n');
const dateFrom7 = new Date();
dateFrom7.setDate(dateFrom7.getDate() - 7);
const queries = await sdk.analytics.getSearchQueries({
dateFrom: dateFrom7.toISOString(),
dateTo: dateTo.toISOString(),
limit: 5
});
queries.data.forEach(q => {
console.log(`"${q.searchTerm}"`);
console.log(` Impressions: ${q.impressions.toLocaleString()} | CTR: ${q.clickThroughRate.toFixed(2)}%\n`);
});
// 4. Stock Alerts
console.log('📦 Stock Status\n');
const lowStockThreshold = 50;
const products = await sdk.products.listProducts({ limit: 10 });
for (const product of products.data) {
const stock = await sdk.products.getStock(product.sku);
const totalStock = stock.data.reduce((sum, w) => sum + w.quantity, 0);
if (totalStock < lowStockThreshold) {
console.log(`⚠️ ${product.name} (${product.sku})`);
console.log(` Stock: ${totalStock} units (LOW)\n`);
}
}
// 5. Export Data
console.log('📄 Generating CSV Report...\n');
const report = await sdk.analytics.generateCSVReport({
reportType: 'sales',
dateFrom: dateFrom.toISOString(),
dateTo: dateTo.toISOString(),
format: 'csv'
});
console.log(`✓ Report generated: ${report.data.reportId}`);
console.log('\n=== Dashboard Complete ===');
} catch (error) {
console.error('❌ Dashboard error:', error.message);
process.exit(1);
}
}
// Run the dashboard
runAnalyticsDashboard();Run It
# Set your API key
export WB_API_KEY='your_api_key_here'
# Run the dashboard
npx tsx analytics-dashboard.tsExpected Output
=== Wildberries Analytics Dashboard ===
📊 Sales Overview (Last 30 Days)
Total Views: 45,230
Total Purchases: 1,234
Total Revenue: 3,678,900 RUB
Conversion Rate: 2.73%
🏆 Top 5 Products by Revenue
1. TechBrand Smartphone Pro 15
Revenue: 1,279,273 RUB
Conversion: 2.80%
2. Laptop Pro 14
Revenue: 987,450 RUB
Conversion: 3.12%
...
🔍 Top Search Queries (Last 7 Days)
"смартфон недорогой"
Impressions: 4,230 | CTR: 3.00%
"чехол для телефона"
Impressions: 3,890 | CTR: 4.01%
...
📦 Stock Status
⚠️ Phone Case Premium (PC-PREM-BLK)
Stock: 23 units (LOW)
📄 Generating CSV Report...
✓ Report generated: RPT-789012
=== Dashboard Complete ===Next Steps
Great work! You've built a comprehensive analytics dashboard. Continue learning:
- Tutorial 4: Multi-Module Integration - Connect all modules together
- Best Practices Guide - Production analytics patterns
- API Reference - Complete analytics module documentation
Key Takeaways
✅ Sales funnel shows conversion from views → purchases ✅ Search queries reveal customer intent and optimization opportunities ✅ Stock history predicts stockouts and guides reordering ✅ CSV reports enable deep analysis in external tools ✅ Dashboard combines multiple metrics for holistic view ✅ Low-stock alerts prevent missed sales opportunities ✅ Conversion rate is key metric for product performance
← Back to Getting Started | Next Tutorial: Multi-Module Integration →