Back to skills
SKILL.md
Marketing Analytics
ASecurityMarketing analytics - UTM, attribution, CAC, ROAS, conversion tracking.
- 530 stars
- 0 votes
- 0 copies
- 1 view
- Added May 29, 2026
Works with
Security analysis
100/100npx -y skills add vibeeval/vibecosystem --skill marketing-analytics --agent claude-codeAre you the author of Marketing Analytics?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/vibeeval-marketing-analytics)---
name: marketing-analytics
description: "Marketing analytics - UTM, attribution, CAC, ROAS, conversion tracking."
---
# Marketing Analytics
## UTM Tracking Setup
### UTM Parameter Standartlari
```
https://example.com/landing?
utm_source=google # Trafik kaynagi (google, facebook, newsletter)
&utm_medium=cpc # Kanal tipi (cpc, email, social, organic)
&utm_campaign=spring_2026 # Kampanya adi
&utm_term=saas+analytics # Arama terimi (paid search)
&utm_content=hero_banner # Reklam varyanti (A/B test)
```
### UTM Naming Convention
| Parameter | Format | Ornekler |
|-----------|--------|---------|
| source | lowercase, platform adi | google, facebook, linkedin, newsletter |
| medium | lowercase, kanal tipi | cpc, cpm, email, social, organic, referral |
| campaign | snake_case, tarih dahil | spring_sale_2026, product_launch_q1 |
| term | + ile ayrilmis | saas+analytics, project+management |
| content | snake_case, varyant | hero_banner, sidebar_cta, email_v2 |
### UTM Builder (TypeScript)
```typescript
interface UTMConfig {
baseUrl: string;
source: string;
medium: string;
campaign: string;
term?: string;
content?: string;
}
function buildUTMUrl(config: UTMConfig): string {
const params = new URLSearchParams();
params.set("utm_source", config.source.toLowerCase());
params.set("utm_medium", config.medium.toLowerCase());
params.set("utm_campaign", config.campaign.toLowerCase().replace(/\s+/g, "_"));
if (config.term) params.set("utm_term", config.term.toLowerCase());
if (config.content) params.set("utm_content", config.content.toLowerCase());
const separator = config.baseUrl.includes("?") ? "&" : "?";
return `${config.baseUrl}${separator}${params.toString()}`;
}
// UTM parametrelerini parse et ve kaydet
function captureUTM(): UTMParams | null {
const params = new URLSearchParams(window.location.search);
const utm: UTMParams = {
source: params.get("utm_source") || undefined,
medium: params.get("utm_medium") || undefined,
campaign: params.get("utm_campaign") || undefined,
term: params.get("utm_term") || undefined,
content: params.get("utm_content") || undefined,
};
if (utm.source) {
// First-touch ve last-touch ayri kaydet
if (!localStorage.getItem("utm_first_touch")) {
localStorage.setItem("utm_first_touch", JSON.stringify({ ...utm, timestamp: Date.now() }));
}
localStorage.setItem("utm_last_touch", JSON.stringify({ ...utm, timestamp: Date.now() }));
return utm;
}
return null;
}
```
## Attribution Modeling
### Attribution Modelleri
| Model | Aciklama | Ne Zaman Kullan |
|-------|----------|----------------|
| First Touch | Ilk temas %100 kredi alir | Awareness kampanyalari |
| Last Touch | Son temas %100 kredi alir | Direct response kampanyalari |
| Linear | Tum temaslar esit kredi alir | Tum kanallari esit degerlendirme |
| Time Decay | Son temaslara daha cok kredi | Uzun satis dongusu |
| U-Shaped | Ilk ve son temas %40, orta %20 | Balanced B2B attribution |
| W-Shaped | Ilk, lead, opportunity %30, geri kalan %10 | Full-funnel B2B |
| Data-Driven | Algoritmik (Markov chain, Shapley) | Yeterli veri varsa (10K+ conversion) |
### Multi-Touch Attribution Query
```sql
-- U-Shaped Attribution
WITH touchpoints AS (
SELECT
conversion_id,
user_id,
channel,
touch_timestamp,
ROW_NUMBER() OVER (PARTITION BY conversion_id ORDER BY touch_timestamp) AS touch_order,
COUNT(*) OVER (PARTITION BY conversion_id) AS total_touches
FROM marketing_touches
WHERE conversion_id IS NOT NULL
),
attributed AS (
SELECT
conversion_id,
channel,
CASE
WHEN total_touches = 1 THEN 1.0
WHEN total_touches = 2 THEN 0.5
WHEN touch_order = 1 THEN 0.4 -- first touch
WHEN touch_order = total_touches THEN 0.4 -- last touch
ELSE 0.2 / (total_touches - 2) -- middle touches
END AS attribution_weight
FROM touchpoints
)
SELECT
channel,
ROUND(SUM(attribution_weight), 2) AS attributed_conversions,
ROUND(SUM(attribution_weight * c.revenue), 2) AS attributed_revenue
FROM attributed a
JOIN conversions c ON a.conversion_id = c.id
GROUP BY channel
ORDER BY attributed_revenue DESC;
```
### Markov Chain Attribution
```typescript
interface TransitionMatrix {
[fromState: string]: {
[toState: string]: number; // probability
};
}
// Removal effect: Her kanalin conversion'a katki oranini hesapla
function calculateRemovalEffect(
matrix: TransitionMatrix,
channels: string[]
): Record<string, number> {
const baseConversionRate = simulateConversions(matrix, channels);
const effects: Record<string, number> = {};
for (const channel of channels) {
const withoutChannel = channels.filter(c => c !== channel);
const reducedRate = simulateConversions(matrix, withoutChannel);
effects[channel] = (baseConversionRate - reducedRate) / baseConversionRate;
}
// Normalize to sum to 1
const total = Object.values(effects).reduce((a, b) => a + b, 0);
for (const channel of channels) {
effects[channel] = effects[channel] / total;
}
return effects;
}
```
## CAC (Customer Acquisition Cost)
### CAC Hesaplama
```typescript
interface CACMetrics {
totalMarketingSpend: number; // Toplam marketing harcamasi
totalSalesSpend: number; // Toplam sales harcamasi (maas dahil)
newCustomers: number; // Kazanilan musteri sayisi
period: string; // "2026-Q1"
}
function calculateCAC(metrics: CACMetrics): {
blendedCAC: number;
paidCAC: number;
organicCAC: number;
} {
const totalSpend = metrics.totalMarketingSpend + metrics.totalSalesSpend;
return {
blendedCAC: totalSpend / metrics.newCustomers,
paidCAC: metrics.totalMarketingSpend / (metrics.newCustomers * 0.6), // %60 paid
organicCAC: (metrics.totalSalesSpend * 0.3) / (metrics.newCustomers * 0.4),
};
}
```
### CAC by Channel Query
```sql
SELECT
channel,
SUM(spend) AS total_spend,
COUNT(DISTINCT conversion_user_id) AS new_customers,
ROUND(SUM(spend) / NULLIF(COUNT(DISTINCT conversion_user_id), 0), 2) AS cac,
ROUND(AVG(first_order_value), 2) AS avg_first_order
FROM (
SELECT
a.channel,
a.spend,
c.user_id AS conversion_user_id,
c.revenue AS first_order_value
FROM ad_spend a
LEFT JOIN conversions c ON c.attributed_channel = a.channel
AND c.conversion_date BETWEEN a.date AND a.date + INTERVAL '30 days'
WHERE a.date >= CURRENT_DATE - INTERVAL '90 days'
) channel_data
GROUP BY channel
ORDER BY cac;
```
### CAC Benchmarks
| Industry | Median CAC | Iyi CAC | Target LTV:CAC |
|----------|-----------|---------|---------------|
| SaaS B2B (SMB) | $200-500 | < $200 | 3:1+ |
| SaaS B2B (Enterprise) | $5K-20K | < $5K | 5:1+ |
| SaaS B2C | $20-100 | < $30 | 3:1+ |
| E-commerce | $10-50 | < $15 | 3:1+ |
| Fintech | $100-500 | < $100 | 4:1+ |
| Marketplace | $50-200 | < $50 | 3:1+ |
## ROAS (Return on Ad Spend)
### ROAS Calculator
```typescript
function calculateROAS(
revenue: number,
adSpend: number
): { roas: number; roasPercentage: number; profitable: boolean } {
const roas = revenue / adSpend;
return {
roas: Math.round(roas * 100) / 100,
roasPercentage: Math.round(roas * 100),
profitable: roas > 1,
};
}
// Hedef ROAS hesapla (break-even icin)
function targetROAS(grossMargin: number): number {
// Minimum ROAS = 1 / Gross Margin
// %70 margin -> minimum 1.43 ROAS
return Math.round((1 / grossMargin) * 100) / 100;
}
```
### ROAS Dashboard Query
```sql
SELECT
campaign_name,
channel,
SUM(impressions) AS impressions,
SUM(clicks) AS clicks,
ROUND(100.0 * SUM(clicks) / NULLIF(SUM(impressions), 0), 2) AS ctr_pct,
SUM(spend) AS spend,
SUM(conversions) AS conversions,
ROUND(SUM(spend) / NULLIF(SUM(conversions), 0), 2) AS cost_per_conversion,
SUM(revenue) AS revenue,
ROUND(SUM(revenue) / NULLIF(SUM(spend), 0), 2) AS roas,
ROUND(SUM(revenue) - SUM(spend), 2) AS profit
FROM campaign_performance
WHERE date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY campaign_name, channel
ORDER BY roas DESC;
```
### ROAS Benchmarks
| Platform | Ortalama ROAS | Iyi ROAS | Mukemmel ROAS |
|----------|-------------|---------|-------------|
| Google Search | 2:1 | 4:1 | 8:1+ |
| Google Display | 0.5:1 | 1.5:1 | 3:1+ |
| Facebook/Instagram | 1.5:1 | 3:1 | 6:1+ |
| LinkedIn | 1:1 | 2.5:1 | 5:1+ |
| TikTok | 1:1 | 2:1 | 4:1+ |
| Email Marketing | 10:1 | 30:1 | 40:1+ |
## Conversion Tracking
### Conversion Event Setup
```typescript
interface ConversionEvent {
event_name: string;
value: number;
currency: string;
conversion_type: "micro" | "macro";
attribution_window_days: number;
}
const conversionEvents: ConversionEvent[] = [
// Macro conversions (primary goals)
{ event_name: "purchase_completed", value: 0, currency: "USD", conversion_type: "macro", attribution_window_days: 30 },
{ event_name: "subscription_started", value: 0, currency: "USD", conversion_type: "macro", attribution_window_days: 30 },
// Micro conversions (leading indicators)
{ event_name: "trial_started", value: 0, currency: "USD", conversion_type: "micro", attribution_window_days: 14 },
{ event_name: "demo_requested", value: 50, currency: "USD", conversion_type: "micro", attribution_window_days: 7 },
{ event_name: "email_subscribed", value: 5, currency: "USD", conversion_type: "micro", attribution_window_days: 7 },
];
// Server-side conversion tracking
async function trackConversion(
event: ConversionEvent,
userId: string,
metadata: Record<string, unknown>
): Promise<void> {
// 1. Internal analytics
await analytics.track(event.event_name, {
...metadata,
conversion_type: event.conversion_type,
value: metadata.value || event.value,
});
// 2. Facebook Conversions API
await sendFacebookConversion(event, userId, metadata);
// 3. Google Ads offline conversion
await sendGoogleOfflineConversion(event, userId, metadata);
}
```
### Conversion Funnel Query
```sql
-- Marketing funnel: Visit -> Lead -> MQL -> SQL -> Customer
SELECT
'Visit' AS stage, COUNT(DISTINCT session_id) AS count, 100.0 AS pct
FROM sessions WHERE date >= CURRENT_DATE - INTERVAL '30 days'
UNION ALL
SELECT
'Lead', COUNT(DISTINCT user_id),
ROUND(100.0 * COUNT(DISTINCT user_id) /
(SELECT COUNT(DISTINCT session_id) FROM sessions WHERE date >= CURRENT_DATE - INTERVAL '30 days'), 1)
FROM leads WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
UNION ALL
SELECT
'MQL', COUNT(DISTINCT user_id),
ROUND(100.0 * COUNT(DISTINCT user_id) /
(SELECT COUNT(DISTINCT user_id) FROM leads WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'), 1)
FROM leads WHERE status = 'mql' AND created_at >= CURRENT_DATE - INTERVAL '30 days'
UNION ALL
SELECT
'SQL', COUNT(DISTINCT user_id),
ROUND(100.0 * COUNT(DISTINCT user_id) /
(SELECT COUNT(DISTINCT user_id) FROM leads WHERE status = 'mql' AND created_at >= CURRENT_DATE - INTERVAL '30 days'), 1)
FROM leads WHERE status = 'sql' AND created_at >= CURRENT_DATE - INTERVAL '30 days'
UNION ALL
SELECT
'Customer', COUNT(DISTINCT user_id),
ROUND(100.0 * COUNT(DISTINCT user_id) /
(SELECT COUNT(DISTINCT user_id) FROM leads WHERE status = 'sql' AND created_at >= CURRENT_DATE - INTERVAL '30 days'), 1)
FROM conversions WHERE date >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY
CASE stage
WHEN 'Visit' THEN 1 WHEN 'Lead' THEN 2 WHEN 'MQL' THEN 3
WHEN 'SQL' THEN 4 WHEN 'Customer' THEN 5
END;
```
## Landing Page Optimization
### Key Metrics
| Metrik | Formul | Iyi Deger |
|--------|--------|----------|
| Bounce Rate | single_page_sessions / total_sessions | < %40 |
| Conversion Rate | conversions / visitors | %3-5 (B2C), %2-3 (B2B) |
| Time on Page | avg(exit_time - entry_time) | > 60 saniye |
| Scroll Depth | avg(max_scroll_percentage) | > %60 |
| CTA Click Rate | cta_clicks / visitors | > %5 |
| Form Completion | form_submits / form_starts | > %30 |
### Landing Page A/B Test Template
```typescript
interface LandingPageTest {
name: string;
hypothesis: string;
element: "headline" | "cta" | "hero_image" | "social_proof" | "pricing" | "layout";
control: string;
treatment: string;
primary_metric: string;
traffic_split: number;
duration_days: number;
}
const tests: LandingPageTest[] = [
{
name: "headline_benefit_vs_feature",
hypothesis: "Benefit-focused headline, feature-focused'a gore %15 daha yuksek conversion verir",
element: "headline",
control: "AI-Powered Analytics Dashboard",
treatment: "Get Insights 10x Faster With AI",
primary_metric: "cta_click_rate",
traffic_split: 0.5,
duration_days: 14,
},
];
```
## Email Marketing Metrics
### KPI Dashboard
| Metrik | Formul | Iyi Deger | Aksiyonlar |
|--------|--------|----------|-----------|
| Open Rate | opens / delivered | %20-30 | Subject line A/B test |
| CTR | clicks / delivered | %2-5 | CTA ve icerik optimize |
| CTOR | clicks / opens | %10-15 | Icerik kalitesini olc |
| Unsubscribe Rate | unsubs / delivered | < %0.5 | Frekans ve segmentasyon |
| Bounce Rate | bounces / sent | < %2 | Liste temizligi |
| Conversion Rate | conversions / clicks | %1-5 | Landing page optimize |
| Revenue per Email | total_revenue / delivered | Varies | Segmentasyon iyilestir |
### Email Performance Query
```sql
SELECT
campaign_name,
sent_at::date AS send_date,
COUNT(*) AS sent,
SUM(CASE WHEN delivered THEN 1 ELSE 0 END) AS delivered,
SUM(CASE WHEN opened THEN 1 ELSE 0 END) AS opens,
ROUND(100.0 * SUM(CASE WHEN opened THEN 1 ELSE 0 END) /
NULLIF(SUM(CASE WHEN delivered THEN 1 ELSE 0 END), 0), 1) AS open_rate,
SUM(CASE WHEN clicked THEN 1 ELSE 0 END) AS clicks,
ROUND(100.0 * SUM(CASE WHEN clicked THEN 1 ELSE 0 END) /
NULLIF(SUM(CASE WHEN delivered THEN 1 ELSE 0 END), 0), 1) AS ctr,
SUM(CASE WHEN converted THEN 1 ELSE 0 END) AS conversions,
SUM(revenue) AS total_revenue,
ROUND(SUM(revenue) / NULLIF(SUM(CASE WHEN delivered THEN 1 ELSE 0 END), 0), 2) AS revenue_per_email
FROM email_campaigns
WHERE sent_at >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY campaign_name, send_date
ORDER BY send_date DESC;
```
## Social Media Analytics
### Platform Metrics
| Platform | Key Metrics | Engagement Formula |
|----------|------------|-------------------|
| Twitter/X | Impressions, Engagement Rate, Link Clicks | (likes + retweets + replies) / impressions |
| LinkedIn | Impressions, CTR, Follower Growth | (likes + comments + shares + clicks) / impressions |
| Instagram | Reach, Saves, Shares | (likes + comments + saves + shares) / followers |
| TikTok | Views, Watch Time, Shares | (likes + comments + shares) / views |
| YouTube | Views, Watch Time, CTR | (likes + comments) / views |
### Social ROI Template
```typescript
interface SocialROI {
platform: string;
totalSpend: number; // paid + organic (time cost)
impressions: number;
engagements: number;
websiteTraffic: number;
conversions: number;
revenue: number;
}
function calculateSocialROI(data: SocialROI): {
cpm: number; // Cost per 1000 impressions
cpe: number; // Cost per engagement
cpc: number; // Cost per click (to website)
cpa: number; // Cost per acquisition
roi: number; // Return on Investment %
} {
return {
cpm: (data.totalSpend / data.impressions) * 1000,
cpe: data.totalSpend / data.engagements,
cpc: data.totalSpend / data.websiteTraffic,
cpa: data.totalSpend / data.conversions,
roi: ((data.revenue - data.totalSpend) / data.totalSpend) * 100,
};
}
```
## SEO Metrics
### Core SEO KPIs
| Metrik | Kaynak | Hedef |
|--------|--------|-------|
| Organic Traffic | Google Search Console / GA | +10% MoM |
| Keyword Rankings | Ahrefs / SEMrush | Top 10 icin hedef keyword |
| Click-Through Rate | Search Console | > %3 ortalama |
| Domain Authority | Ahrefs / Moz | Rakiplerden yuksek |
| Backlink Growth | Ahrefs | +5% MoM |
| Core Web Vitals | PageSpeed Insights | LCP < 2.5s, CLS < 0.1, INP < 200ms |
| Indexed Pages | Search Console | Sitemap'teki sayfa sayisina yakin |
| Organic Conversion Rate | GA | > %2 |
### Content Performance Scoring
```typescript
interface ContentScore {
url: string;
organic_traffic_30d: number;
avg_position: number;
ctr: number;
conversions: number;
backlinks: number;
word_count: number;
last_updated: string;
}
function scoreContent(content: ContentScore): {
score: number;
action: "keep" | "update" | "consolidate" | "remove";
} {
let score = 0;
// Traffic (0-30)
if (content.organic_traffic_30d > 1000) score += 30;
else if (content.organic_traffic_30d > 100) score += 20;
else if (content.organic_traffic_30d > 10) score += 10;
// Rankings (0-25)
if (content.avg_position <= 3) score += 25;
else if (content.avg_position <= 10) score += 15;
else if (content.avg_position <= 20) score += 5;
// Conversions (0-25)
if (content.conversions > 10) score += 25;
else if (content.conversions > 1) score += 15;
else if (content.conversions > 0) score += 5;
// Freshness (0-10)
const daysSinceUpdate = (Date.now() - new Date(content.last_updated).getTime()) / 86400000;
if (daysSinceUpdate < 90) score += 10;
else if (daysSinceUpdate < 180) score += 5;
// Backlinks (0-10)
if (content.backlinks > 10) score += 10;
else if (content.backlinks > 0) score += 5;
let action: "keep" | "update" | "consolidate" | "remove";
if (score >= 70) action = "keep";
else if (score >= 40) action = "update";
else if (score >= 20) action = "consolidate";
else action = "remove";
return { score, action };
}
```
## Marketing Funnel Optimization
### Funnel Stage Metrics
```
TOFU (Awareness) MOFU (Consideration) BOFU (Decision)
------------------- ---------------------- ------------------
Impressions Email subscribers Demo requests
Website visitors Content downloads Trial signups
Social followers Webinar attendees Quote requests
Blog readers Return visitors Free trial users
Newsletter opens Pricing page visits
```
### Channel Efficiency Matrix
```sql
SELECT
channel,
SUM(spend) AS spend,
COUNT(DISTINCT visitor_id) AS visitors,
COUNT(DISTINCT lead_id) AS leads,
COUNT(DISTINCT customer_id) AS customers,
ROUND(SUM(spend) / NULLIF(COUNT(DISTINCT visitor_id), 0), 2) AS cost_per_visit,
ROUND(SUM(spend) / NULLIF(COUNT(DISTINCT lead_id), 0), 2) AS cost_per_lead,
ROUND(SUM(spend) / NULLIF(COUNT(DISTINCT customer_id), 0), 2) AS cac,
ROUND(100.0 * COUNT(DISTINCT lead_id) / NULLIF(COUNT(DISTINCT visitor_id), 0), 1) AS visit_to_lead_pct,
ROUND(100.0 * COUNT(DISTINCT customer_id) / NULLIF(COUNT(DISTINCT lead_id), 0), 1) AS lead_to_customer_pct,
SUM(customer_revenue) AS revenue,
ROUND(SUM(customer_revenue) / NULLIF(SUM(spend), 0), 2) AS roas
FROM marketing_data
WHERE date >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY channel
ORDER BY roas DESC;
```
## Anti-Patterns
| Anti-Pattern | Dogru Yol |
|-------------|-----------|
| UTM'siz kampanya | Her kampanyada tutarli UTM kullan |
| Sadece last-touch attribution | Multi-touch modelleme yap |
| CAC'i toplam baz al | Kanal bazli CAC hesapla |
| ROAS'i revenue ile hesapla | Profit-based ROAS (POAS) kullan |
| Vanity metrics raporu (impressions) | Conversion-focused metriklere odaklan |
| Email herkese ayni icerik | Segmentasyon + kisisellesetirme |
| SEO sadece keyword | Technical SEO + Content + Backlink |
| Kanal silolari | Cross-channel attribution |
Attribution
Comments
Loading comments…