This guide shows how to build an automated nightly sales report that fetches purchase data from the Karma API.
Overview
Many integrations need to sync sales data on a nightly basis for accounting, inventory reconciliation, or business intelligence. This example demonstrates:
Fetching purchases for a specific date range
Handling pagination for large result sets
Processing and aggregating sales data
Error handling and retry logic
Prerequisites
API Key with purchases.read permission
Location ID(s) you want to fetch data for
API Endpoint
Code
GET /api/v1/purchases
TypeScript Implementation
Code
import { createKarmaClient } from '@karmalicious/karma-api-js'interface NightlySalesReport { date: string locationId: number totalSales: number totalTransactions: number averageOrderValue: number salesByHour: Record<number, number> topProducts: Array<{ title: string; quantity: number; revenue: number }>}async function fetchNightlySalesData( apiKey: string, locationId: number, date: string // YYYY-MM-DD format): Promise<NightlySalesReport> { const client = createKarmaClient({ apiKey, baseUrl: 'https://common-api.karma.life' }) // Define date range for the full day const startDate = `${date}T00:00:00Z` const endDate = `${date}T23:59:59Z` // Fetch all purchases with pagination const allPurchases: any[] = [] let page = 1 const limit = 100 let hasMore = true while (hasMore) { const response = await client.purchases.getPurchases({ locationId, startDate, endDate, state: 'confirmed', // Only completed purchases page, limit }) allPurchases.push(...response.data.items) hasMore = response.data.pagination.hasMore page++ // Rate limiting protection if (hasMore) { await new Promise(resolve => setTimeout(resolve, 100)) } } // Aggregate the data return aggregateSalesData(allPurchases, date, locationId)}function aggregateSalesData( purchases: any[], date: string, locationId: number): NightlySalesReport { const salesByHour: Record<number, number> = {} const productSales: Record<string, { quantity: number; revenue: number }> = {} let totalSales = 0 for (const purchase of purchases) { // Add to total const amount = purchase.lineItems .filter((li: any) => li.type === 'product') .reduce((sum: number, li: any) => sum + li.finalAmountCents, 0) totalSales += amount // Track by hour const hour = new Date(purchase.purchasedAt).getHours() salesByHour[hour] = (salesByHour[hour] || 0) + amount // Track by product for (const lineItem of purchase.lineItems) { if (lineItem.type === 'product') { if (!productSales[lineItem.title]) { productSales[lineItem.title] = { quantity: 0, revenue: 0 } } productSales[lineItem.title].quantity += lineItem.quantity productSales[lineItem.title].revenue += lineItem.finalAmountCents } } } // Get top 10 products by revenue const topProducts = Object.entries(productSales) .map(([title, data]) => ({ title, ...data })) .sort((a, b) => b.revenue - a.revenue) .slice(0, 10) return { date, locationId, totalSales, totalTransactions: purchases.length, averageOrderValue: purchases.length > 0 ? Math.round(totalSales / purchases.length) : 0, salesByHour, topProducts }}// Example usage with retry logicasync function fetchWithRetry( apiKey: string, locationId: number, date: string, maxRetries = 3): Promise<NightlySalesReport> { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await fetchNightlySalesData(apiKey, locationId, date) } catch (error: any) { if (error.status === 429) { // Rate limited - wait and retry const retryAfter = error.headers?.['retry-after'] || 60 console.log(`Rate limited. Waiting ${retryAfter}s before retry ${attempt}/${maxRetries}`) await new Promise(resolve => setTimeout(resolve, retryAfter * 1000)) } else if (attempt === maxRetries) { throw error } else { // Other error - exponential backoff await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000)) } } } throw new Error('Max retries exceeded')}// Run nightly at 2 AMasync function runNightlyJob() { const apiKey = process.env.KARMA_API_KEY! const locationIds = [100, 101, 102] // Your location IDs // Get yesterday's date const yesterday = new Date() yesterday.setDate(yesterday.getDate() - 1) const dateStr = yesterday.toISOString().split('T')[0] console.log(`Fetching sales data for ${dateStr}`) const reports: NightlySalesReport[] = [] for (const locationId of locationIds) { try { const report = await fetchWithRetry(apiKey, locationId, dateStr) reports.push(report) console.log(`Location ${locationId}: ${report.totalTransactions} transactions, ${(report.totalSales / 100).toFixed(2)} SEK`) } catch (error) { console.error(`Failed to fetch data for location ${locationId}:`, error) } } // Send reports to your system await sendToAccountingSystem(reports) await sendDailyEmail(reports)}runNightlyJob()
cURL Example
Code
# Fetch purchases for a specific datecurl -X GET "https://common-api.karma.life/api/v1/purchases?locationId=100&startDate=2026-01-24T00:00:00Z&endDate=2026-01-24T23:59:59Z&state=confirmed&limit=100" \ -H "X-API-Key: karma_live_your_api_key_here" \ -H "Content-Type: application/json"