Track Marketing Campaigns End-to-End
Learn how to track a complete marketing campaign from setup to ROI analysis. This real-world walkthrough shows you how to measure multi-channel performance, attribute conversions, and optimize your marketing spend.
What You'll Learn
This tutorial walks through a complete product launch campaign, showing you how to:
- Structure campaigns with UTM parameters - Organize tracking for multi-channel campaigns
- Create channel-specific links - Generate unique tracking links for email, social, ads, and more
- Track conversions - Measure which channels drive actual sales or sign-ups
- Attribute revenue - Connect clicks to purchases using conversion tracking
- Calculate ROI - Determine which channels give you the best return
- Optimize in real-time - Shift budget to top-performing channels
Real-World Scenario
Campaign: Summer Sale 2026
You're launching a week-long summer sale for an e-commerce store selling outdoor gear.
- Goal: 500 orders, $50,000 revenue
- Budget: $5,000 ($10 CPA target)
- Channels: Email (30% budget), Instagram Ads (40%), Google Ads (20%), Influencer Posts (10%)
- Landing page: https://outdoorgear.example.com/summer-sale
- Duration: July 1-7, 2026
Prerequisites
- A SnipLink account with Pro plan or higher (sign up here)
- API key with links:write and analytics:read scopes
- Your workspace ID
- Landing page URL for your campaign
- Google Analytics or similar analytics platform (optional but recommended)
Step 1: Plan Your Campaign Structure
Before creating any links, plan your UTM parameter structure:
UTM Parameter Breakdown
| Parameter | Purpose | Example Value |
|---|---|---|
| utm_source | Where the traffic comes from | instagram, newsletter, google |
| utm_medium | Type of marketing channel | email, social, cpc, influencer |
| utm_campaign | Specific campaign name | summer-sale-2026 |
| utm_content | Variation or creative | banner-ad, story-swipe-up |
| utm_term | Paid search keywords | outdoor-gear-sale |
Campaign Link Plan
Create a spreadsheet with all links you'll need:
channel,slug,utm_source,utm_medium,utm_campaign,utm_content
Newsletter,summer-newsletter,newsletter,email,summer-sale-2026,hero-cta
Instagram Story,summer-ig-story,instagram,social,summer-sale-2026,story-swipe-up
Instagram Feed,summer-ig-feed,instagram,social,summer-sale-2026,feed-post
Google Ads,summer-google-ad,google,cpc,summer-sale-2026,search-ad
Influencer Post,summer-influencer,instagram,influencer,summer-sale-2026,influencer-bio
Facebook Ad,summer-fb-ad,facebook,social,summer-sale-2026,carousel-adStep 2: Create Campaign Links
Use the SnipLink API to create all your campaign links at once:
// create-campaign-links.ts
import fs from 039;fs039;;
import { parse } from 039;csv-parse/sync039;;
const API_BASE = 039;https://api.sniplink.co/v1';
const API_KEY = process.env.SNIPLINK_API_KEY!;
const WORKSPACE_ID = process.env.SNIPLINK_WORKSPACE_ID!;
const LANDING_PAGE = 039;https://outdoorgear.example.com/summer-sale';
const CAMPAIGN_NAME = 039;summer-sale-2026039;;
interface CampaignLink {
channel: string;
slug: string;
utm_source: string;
utm_medium: string;
utm_campaign: string;
utm_content: string;
}
async function createCampaignLink(link: CampaignLink) {
const response = await fetch(
`${API_BASE}/workspaces/${WORKSPACE_ID}/links`,
{
method: 039;POST039;,
headers: {
039;X-API-Key039;: API_KEY,
039;Content-Type039;: 039;application/json039;,
},
body: JSON.stringify({
url: LANDING_PAGE,
slug: link.slug,
title: `Summer Sale 2026 - ${link.channel}`,
tags: [CAMPAIGN_NAME, link.utm_source, link.utm_medium],
utmSource: link.utm_source,
utmMedium: link.utm_medium,
utmCampaign: link.utm_campaign,
utmContent: link.utm_content,
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Failed to create link: ${error.message}`);
}
return response.json();
}
async function main() {
// Read campaign plan from CSV
const csvContent = fs.readFileSync(039;campaign-links.csv039;, 039;utf-8039;);
const links: CampaignLink[] = parse(csvContent, {
columns: true,
skip_empty_lines: true,
});
console.log(`๐ Creating ${links.length} campaign links...\n`);
const results = [];
for (const link of links) {
try {
const result = await createCampaignLink(link);
console.log(`โ
${link.channel}: ${result.link.shortUrl}`);
results.push({
channel: link.channel,
shortUrl: result.link.shortUrl,
linkId: result.link.id,
});
} catch (error) {
console.error(`โ ${link.channel}: ${error.message}`);
}
// Rate limiting: wait 1 second between requests
await new Promise((resolve) => setTimeout(resolve, 1000));
}
// Save results to JSON for easy reference
fs.writeFileSync(039;campaign-links-output.json039;, JSON.stringify(results, null, 2));
console.log(`\nโจ Done! Created ${results.length} links`);
console.log(039;๐ Results saved to campaign-links-output.json039;);
}
main();Run the script:
npx tsx create-campaign-links.tsYou'll get output like:
๐ Creating 6 campaign links...
โ
Newsletter: https://r.sniplink.co/summer-newsletter
โ
Instagram Story: https://r.sniplink.co/summer-ig-story
โ
Instagram Feed: https://r.sniplink.co/summer-ig-feed
โ
Google Ads: https://r.sniplink.co/summer-google-ad
โ
Influencer Post: https://r.sniplink.co/summer-influencer
โ
Facebook Ad: https://r.sniplink.co/summer-fb-ad
โจ Done! Created 6 links
๐ Results saved to campaign-links-output.jsonStep 3: Deploy Links Across Channels
Now use your campaign links in each marketing channel:
Email Newsletter
<a href="https://r.sniplink.co/summer-newsletter">
Shop the Summer Sale โ
</a>Instagram Bio
Summer Sale is live! ๐
Get 30% off outdoor gear
๐ Shop now
r.sniplink.co/summer-ig-storyGoogle Ads
Headline: Summer Outdoor Gear Sale - 30% Off
Description: Premium tents, backpacks & more. Limited time only!
Final URL: https://r.sniplink.co/summer-google-adQR Code for Print Materials
Generate a QR code for flyers, posters, or packaging inserts:
// Using the QR code generation endpoint
const qrCodeUrl = `https://api.sniplink.co/v1/links/${linkId}/qr?size=512&format=png`;
// Or use a service like qrcode.react in your app
import QRCode from 039;qrcode.react039;;
<QRCode value="https://r.sniplink.co/summer-newsletter" size={256} />Step 4: Monitor Campaign Performance
Build a real-time campaign dashboard to track performance:
// campaign-dashboard.ts
import { api } from 039;./lib/sniplink039;;
interface CampaignMetrics {
channel: string;
clicks: number;
uniqueClicks: number;
conversions: number;
revenue: number;
spend: number;
roi: number;
cpa: number;
}
async function getCampaignMetrics(
linkId: string,
channel: string,
spend: number
): Promise<CampaignMetrics> {
// Fetch analytics from SnipLink
const analytics = await api.getOverallAnalytics(linkId, {
startDate: 039;2026-07-01T00:00:00Z039;,
endDate: 039;2026-07-07T23:59:59Z039;,
});
// In a real app, you'd fetch conversions from your e-commerce platform
// For this example, let's assume a 5% conversion rate
const conversions = Math.floor(analytics.uniqueClicks * 0.05);
const revenue = conversions * 100; // $100 average order value
const roi = ((revenue - spend) / spend) * 100;
const cpa = conversions > 0 ? spend / conversions : 0;
return {
channel,
clicks: analytics.clicks,
uniqueClicks: analytics.uniqueClicks,
conversions,
revenue,
spend,
roi,
cpa,
};
}
async function main() {
const campaignLinks = [
{ linkId: 039;lnk_newsletter039;, channel: 039;Newsletter039;, spend: 1500 },
{ linkId: 039;lnk_ig_story039;, channel: 039;Instagram Story039;, spend: 1000 },
{ linkId: 039;lnk_ig_feed039;, channel: 039;Instagram Feed039;, spend: 1000 },
{ linkId: 039;lnk_google039;, channel: 039;Google Ads039;, spend: 1000 },
{ linkId: 039;lnk_influencer039;, channel: 039;Influencer039;, spend: 500 },
{ linkId: 039;lnk_fb_ad039;, channel: 039;Facebook Ad039;, spend: 0 }, // Organic post
];
console.log(039;๐ Campaign Performance Dashboard\n039;);
console.log(039;Campaign: Summer Sale 2026039;);
console.log(039;Date Range: July 1-7, 2026\n039;);
const metrics = await Promise.all(
campaignLinks.map((link) =>
getCampaignMetrics(link.linkId, link.channel, link.spend)
)
);
// Display results in a table
console.table(
metrics.map((m) => ({
Channel: m.channel,
Clicks: m.clicks,
Unique: m.uniqueClicks,
Conversions: m.conversions,
Revenue: `$${m.revenue.toLocaleString()}`,
Spend: `$${m.spend.toLocaleString()}`,
ROI: `${m.roi.toFixed(0)}%`,
CPA: `$${m.cpa.toFixed(2)}`,
}))
);
// Calculate totals
const totals = metrics.reduce(
(acc, m) => ({
clicks: acc.clicks + m.clicks,
conversions: acc.conversions + m.conversions,
revenue: acc.revenue + m.revenue,
spend: acc.spend + m.spend,
}),
{ clicks: 0, conversions: 0, revenue: 0, spend: 0 }
);
const overallROI = ((totals.revenue - totals.spend) / totals.spend) * 100;
const overallCPA = totals.conversions > 0 ? totals.spend / totals.conversions : 0;
console.log(039;\n๐ Campaign Totals039;);
console.log(` Total Clicks: ${totals.clicks.toLocaleString()}`);
console.log(` Total Conversions: ${totals.conversions}`);
console.log(` Total Revenue: $${totals.revenue.toLocaleString()}`);
console.log(` Total Spend: $${totals.spend.toLocaleString()}`);
console.log(` Overall ROI: ${overallROI.toFixed(0)}%`);
console.log(` Overall CPA: $${overallCPA.toFixed(2)}`);
// Identify best performer
const bestChannel = metrics.reduce((best, current) =>
current.roi > best.roi ? current : best
);
console.log(`\n๐ Top Performer: ${bestChannel.channel}`);
console.log(` ROI: ${bestChannel.roi.toFixed(0)}%`);
console.log(` Conversions: ${bestChannel.conversions}`);
}
main();Example output:
๐ Campaign Performance Dashboard
Campaign: Summer Sale 2026
Date Range: July 1-7, 2026
โโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโ
โ (index) โ Channel โ Clicks โ Unique โ Conversions โ Revenue โ Spend โ ROI โ CPA โ
โโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโค
โ 0 โ 'Newsletter' โ 4521 โ 3876 โ 194 โ '$19,400'โ '$1,500'โ '1193%'โ '$7.73' โ
โ 1 โ 'Instagram Story' โ 3124 โ 2654 โ 133 โ '$13,300'โ '$1,000'โ '1230%'โ '$7.52' โ
โ 2 โ 'Instagram Feed' โ 2867 โ 2411 โ 121 โ '$12,100'โ '$1,000'โ '1110%'โ '$8.26' โ
โ 3 โ 'Google Ads' โ 1932 โ 1621 โ 81 โ '$8,100' โ '$1,000'โ '710%' โ '$12.35' โ
โ 4 โ 'Influencer' โ 1456 โ 1234 โ 62 โ '$6,200' โ '$500' โ '1140%'โ '$8.06' โ
โ 5 โ 'Facebook Ad' โ 892 โ 743 โ 37 โ '$3,700' โ '$0' โ 'Inf' โ '$0.00' โ
โโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโ
๐ Campaign Totals
Total Clicks: 14,792
Total Conversions: 628
Total Revenue: $62,800
Total Spend: $5,000
Overall ROI: 1156%
Overall CPA: $7.96
๐ Top Performer: Instagram Story
ROI: 1230%
Conversions: 133Step 5: Track Conversions with Webhooks
For accurate conversion tracking, set up a webhook to capture purchase events:
// pages/api/webhooks/clicks.ts
import { NextApiRequest, NextApiResponse } from 039;next039;;
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 039;POST039;) {
return res.status(405).json({ error: 039;Method not allowed039; });
}
const event = req.body;
if (event.type === 039;link.clicked039;) {
const { linkId, clickId, utmSource, utmMedium, utmCampaign } = event.data;
// Store click event for later attribution
await db.clicks.create({
data: {
clickId,
linkId,
utmSource,
utmMedium,
utmCampaign,
timestamp: new Date(),
},
});
console.log(`Click tracked: ${utmSource}/${utmMedium}`);
}
res.status(200).json({ received: true });
}
// On your checkout page, attribute the purchase to the click
async function handlePurchase(orderId: string, amount: number) {
// Get the click ID from cookie or session
const clickId = getCookie(039;sniplink_click_id039;);
if (clickId) {
// Find the click in your database
const click = await db.clicks.findUnique({ where: { clickId } });
if (click) {
// Record conversion
await db.conversions.create({
data: {
clickId: click.clickId,
linkId: click.linkId,
orderId,
amount,
utmSource: click.utmSource,
utmMedium: click.utmMedium,
utmCampaign: click.utmCampaign,
timestamp: new Date(),
},
});
console.log(`Conversion attributed to ${click.utmSource}/${click.utmMedium}`);
}
}
}Step 6: Analyze and Optimize
Based on performance data, make data-driven decisions:
Insights from Our Campaign
โ What's Working
- Newsletter has highest conversion rate (5%) and best ROI (1193%)
- Instagram Story delivers strong ROI (1230%) with good engagement
- Influencer post is cost-effective despite lower volume
โ ๏ธ What Needs Improvement
- Google Ads has highest CPA ($12.35) - optimize targeting or keywords
- Facebook Ad underperforming - test new creative or audience
๐ก Optimization Actions
- Increase newsletter frequency - High ROI channel deserves more investment
- Scale Instagram campaigns - Both Story and Feed performing well
- Pause Google Ads - Reallocate budget to Instagram
- A/B test Facebook creative - Try carousel vs single image
Advanced: Multi-Touch Attribution
Track the customer journey across multiple touchpoints:
// Track all clicks in customer journey
const customerJourney = [
{ timestamp: 039;2026-07-01 10:30039;, source: 039;instagram039;, medium: 039;social039; },
{ timestamp: 039;2026-07-02 14:15039;, source: 039;google039;, medium: 039;cpc039; },
{ timestamp: 039;2026-07-03 16:45039;, source: 039;newsletter039;, medium: 039;email039; }, // Converted
];
// Attribution models
const attributionModels = {
// Last click: All credit to final touchpoint
lastClick: () => customerJourney[customerJourney.length - 1],
// First click: All credit to first touchpoint
firstClick: () => customerJourney[0],
// Linear: Equal credit to all touchpoints
linear: () => {
const credit = 1 / customerJourney.length;
return customerJourney.map((touch) => ({ ...touch, credit }));
},
// Time decay: More recent touches get more credit
timeDecay: () => {
const halfLife = 7; // days
const now = new Date(039;2026-07-03039;);
return customerJourney.map((touch) => {
const touchDate = new Date(touch.timestamp);
const daysDiff = (now.getTime() - touchDate.getTime()) / (1000 * 60 * 60 * 24);
const credit = Math.pow(0.5, daysDiff / halfLife);
return { ...touch, credit };
});
},
};Reporting Template
Create a standardized report for stakeholders:
# Summer Sale 2026 Campaign Report
## Executive Summary
- **Campaign Duration:** July 1-7, 2026 (7 days)
- **Total Investment:** $5,000
- **Total Revenue:** $62,800
- **Overall ROI:** 1,156%
- **Total Orders:** 628
- **Average Order Value:** $100
## Channel Performance
### Top Performers
1. **Newsletter** - 1193% ROI, 194 conversions
2. **Instagram Story** - 1230% ROI, 133 conversions
3. **Influencer Post** - 1140% ROI, 62 conversions
### Underperformers
- **Google Ads** - 710% ROI, $12.35 CPA (above target)
- **Facebook Ad** - Low engagement, needs creative refresh
## Key Learnings
- Email marketing remains our highest-ROI channel
- Instagram audiences respond well to time-sensitive offers
- Search ads require better keyword targeting
## Recommendations for Next Campaign
1. Increase email campaign budget by 50%
2. Scale successful Instagram ad sets
3. Test video ads on Instagram Reels
4. Optimize Google Ads targeting and pause low-performers
5. Partner with 2-3 additional micro-influencersBest Practices
1. Consistent Naming Conventions
Use lowercase, hyphens, and descriptive names:
- โ
Good:
summer-sale-2026,instagram-story - โ Bad:
SummerSale2026,IG_Story
2. Tag Everything
Use tags to organize links by campaign, channel, and date:
tags: [039;summer-sale-2026039;, 039;instagram039;, 039;paid039;, 039;q3-2026039;]3. Set Expiration Dates
Prevent outdated campaign links from being used:
expiresAt: 039;2026-07-07T23:59:59Z039;4. Use Link Rotation for A/B Testing
Test different landing pages or offers:
// Create two versions
const linkA = await createLink({ url: 039;example.com/landing-a039;, slug: 039;sale-version-a039; });
const linkB = await createLink({ url: 039;example.com/landing-b039;, slug: 039;sale-version-b039; });
// Split traffic 50/50 manually or use a link rotation serviceCommon Pitfalls to Avoid
Can't attribute performance to specific channels
Clicks without conversions = incomplete data
Click all links to verify they work and UTM parameters are correct
Check device breakdown - optimize landing pages accordingly
Next Steps
- Automate reporting: Build a custom analytics dashboard for real-time insights
- Integrate with Make.com: Use Make.com integration to auto-create campaign links
- Export data: Use the bulk link generator to export analytics to CSV
- Set up webhooks: Get real-time notifications when links are clicked
Need Help Planning Your Campaign?
Our team can help you design a tracking strategy for your next marketing campaign.