Featured image of post Build a Marketing Attribution Engine with DuckDB: From Multi-Channel Data to SaaS Monetization

Build a Marketing Attribution Engine with DuckDB: From Multi-Channel Data to SaaS Monetization

Master multi-touch marketing attribution with DuckDB using LATERAL JOIN, weighted models, and cohort analysis. Learn how to package this into a subscription SaaS product earning $5K+/month.

Build a Marketing Attribution Engine with DuckDB: From Multi-Channel Data to SaaS Monetization

The Problem: The “Black Box” of Marketing Data

Your company runs ads on Google, Facebook, TikTok, SEO, and email campaigns simultaneously. Every conversion comes from a combination of multiple touchpoints—but who gets the credit?

Traditional solutions fall into three categories:

  • Excel manual matching: 30+ minutes for 100K rows, and error-prone
  • Professional attribution tools (Northbeam, Triple Whale): $500-$2,000/month, and data must be exported to external platforms
  • Custom-built pipelines: Requires Spark + Airflow + dedicated engineers, 3+ months to deploy

DuckDB offers a completely different approach—doing on a single laptop what previously required a big data platform for attribution analysis.

Data Structure: Reconstructing the User Journey

The first step in attribution analysis is stitching scattered cross-channel user behaviors into a coherent journey. Assume you have the following raw data:

-- Create sample data (simulating multi-channel clicks and conversions)
CREATE TABLE ad_clicks AS
SELECT * FROM (VALUES
    ('U001', '2026-08-01 08:00:00', 'google',  'cpc', 12.50),
    ('U001', '2026-08-01 09:30:00', 'facebook', 'cpm',  0.00),
    ('U001', '2026-08-02 10:00:00', 'tiktok',  'cpv',  0.50),
    ('U002', '2026-08-01 11:00:00', 'google',  'cpc', 15.00),
    ('U002', '2026-08-03 14:00:00', 'seo',     'org',  0.00),
    ('U003', '2026-08-01 07:00:00', 'email',   'flat',0.00),
    ('U003', '2026-08-01 07:15:00', 'google',  'cpc',  8.00),
    ('U003', '2026-08-02 09:00:00', 'facebook', 'cpm',  0.00),
    ('U004', '2026-08-01 16:00:00', 'tiktok',  'cpv',  0.30),
    ('U004', '2026-08-02 12:00:00', 'google',  'cpc', 11.00),
    ('U005', '2026-08-01 20:00:00', 'seo',     'org',  0.00)
) AS t(user_id, timestamp, channel, cost_type, cost);

CREATE TABLE conversions AS
SELECT * FROM (VALUES
    ('U001', '2026-08-02 15:00:00', 299.00),
    ('U003', '2026-08-02 18:00:00', 149.00),
    ('U004', '2026-08-03 10:00:00', 499.00)
) AS t(user_id, convert_time, revenue);

Core Technique 1: LATERAL JOIN for Time-Windowed Journeys

The key to attribution analysis is—for each conversion, find all channels the user touched within a time window (e.g., 7 days). DuckDB’s LATERAL JOIN + UNNEST is the perfect tool:

-- Build complete user journey timeline for each conversion
WITH conversion_journey AS (
    SELECT 
        c.user_id,
        c.convert_time,
        c.revenue,
        -- Get all touchpoints within 7 days before conversion
        ARRAY_AGG(
            ROW(ac.channel, ac.timestamp, ac.cost)
            ORDER BY ac.timestamp ASC
        ) FILTER (
            WHERE ac.timestamp BETWEEN c.convert_time - INTERVAL '7' DAY 
                                   AND c.convert_time
        ) AS journey
    FROM conversions c
    LEFT JOIN ad_clicks ac ON ac.user_id = c.user_id
    GROUP BY c.user_id, c.convert_time, c.revenue
)
SELECT * FROM conversion_journey;

The result shows each user’s complete touchpoint sequence—the foundation for any attribution model.

Core Technique 2: Three Main Attribution Models

Last Touch Attribution

The simplest and most common—gives all credit to the last touchpoint.

-- Last Touch Attribution
SELECT 
    channel,
    COUNT(*) AS conversions,
    SUM(revenue) AS total_revenue,
    SUM(revenue) / COUNT(*) AS avg_order_value
FROM (
    SELECT 
        c.user_id,
        c.revenue,
        -- Take the most recent touchpoint in the journey
        (journey[ARRAY_LENGTH(journey)]).channel AS last_channel
    FROM conversion_journey c
) t
GROUP BY channel
ORDER BY total_revenue DESC;

First Touch Attribution

Credit goes to the channel that first introduced the user to the brand.

-- First Touch Attribution
SELECT 
    channel,
    COUNT(*) AS conversions,
    SUM(revenue) AS total_revenue
FROM (
    SELECT 
        c.user_id,
        c.revenue,
        (journey[1]).channel AS first_channel
    FROM conversion_journey c
) t
GROUP BY channel
ORDER BY total_revenue DESC;

Linear Attribution

Equal credit to every touchpoint—fair but conservative.

-- Linear Attribution: split revenue evenly across touchpoints
SELECT 
    jt.channel,
    COUNT(*) AS touchpoints,
    SUM(jt.revenue / jt.journey_size) AS attributed_revenue
FROM (
    SELECT 
        c.user_id,
        c.revenue,
        ARRAY_LENGTH(c.journey) AS journey_size,
        UNNEST(c.journey) WITH ORDINALITY AS jt(journey_item, position)
    FROM conversion_journey c
) t
LATERAL (SELECT (t.jt.journey_item).channel) AS channel_info
GROUP BY jt.channel
ORDER BY attributed_revenue DESC;

Time Decay Attribution (Most Advanced)

Touchpoints closer to the conversion get higher weight—this is the most sophisticated model.

-- Time Decay Attribution (exponential decay, half-life of 3 days)
SELECT 
    jt.channel,
    SUM(
        c.revenue * EXP(-0.231 * (c.convert_time - jt.ts)::DOUBLE)
    ) AS weighted_revenue
FROM conversion_journey c
LATERAL (
    SELECT 
        (item).channel AS channel,
        (item).timestamp AS ts,
        (item).cost AS cost
    FROM UNNEST(c.journey) AS item
) jt
GROUP BY jt.channel
ORDER BY weighted_revenue DESC;

Core Technique 3: Cohort Analysis for Long-Term Value

Attribution isn’t just about single conversions—it’s about understanding the long-term value of users acquired through different channels:

-- Cohort analysis by first-touch channel
WITH first_touch AS (
    SELECT 
        user_id,
        MIN(timestamp) AS first_touch_time,
        channel AS first_channel
    FROM ad_clicks
    GROUP BY user_id, channel
),
cohort_data AS (
    SELECT 
        ft.first_channel,
        DATE_TRUNC('week', ft.first_touch_time) AS cohort_week,
        COUNT(DISTINCT c.user_id) AS users,
        SUM(c.revenue) AS total_revenue,
        AVG(c.revenue) AS avg_revenue_per_user
    FROM first_touch ft
    JOIN conversions c ON c.user_id = ft.user_id
    GROUP BY ft.first_channel, DATE_TRUNC('week', ft.first_touch_time)
)
SELECT * FROM cohort_data
ORDER BY cohort_week, total_revenue DESC;

Performance Comparison: DuckDB vs Traditional Solutions

MetricExcel/VLOOKUPSpark + PythonDuckDB
1M row attribution query20+ min (often crashes)Needs cluster setup< 2 seconds
Memory usageGB-level (Excel limit)Several GB~50 MB
Deployment costFree$200+/month (cloud)Free
Learning curveSimple but error-proneComplexSQL-only
EmbeddabilityNoneRequires integration frameworkNative embedding

💡 Key Insight: DuckDB’s columnar storage + vectorized execution engine makes it 5-10x faster than Pandas for attribution analysis’s heavy aggregation and window functions, while reducing code volume by 60%.

Complete Runnable Code (Python + DuckDB)

import duckdb
import pandas as pd

# Connect to in-memory database (zero configuration)
con = duckdb.connect(":memory:")

# 1. Create sample data
con.execute("""
CREATE TABLE ad_clicks AS
SELECT * FROM read_csv_auto('ad_clicks.csv');

CREATE TABLE conversions AS
SELECT * FROM read_csv_auto('conversions.csv');
""")

# 2. Run attribution analysis
result = con.execute("""
WITH conversion_journey AS (
    SELECT 
        c.user_id,
        c.convert_time,
        c.revenue,
        ARRAY_AGG(
            ROW(ac.channel, ac.timestamp, ac.cost)
            ORDER BY ac.timestamp ASC
        ) FILTER (
            WHERE ac.timestamp BETWEEN c.convert_time - INTERVAL '7' DAY 
                                   AND c.convert_time
        ) AS journey
    FROM conversions c
    LEFT JOIN ad_clicks ac ON ac.user_id = c.user_id
    GROUP BY c.user_id, c.convert_time, c.revenue
)
SELECT 
    jt.channel,
    COUNT(*) AS touchpoints,
    SUM(c.revenue / ARRAY_LENGTH(c.journey)) AS linear_attribution
FROM conversion_journey c
LATERAL (SELECT (item).channel FROM UNNEST(c.journey) AS item) jt
GROUP BY jt.channel
ORDER BY linear_attribution DESC
""").fetchdf()

print(result)

From Analysis Tool to SaaS Product: Monetization Paths

This is the most critical part—turning this technology into revenue.

Wrap the DuckDB attribution engine into a web app for small e-commerce businesses and marketing agencies:

Product Architecture:
┌─────────────────────────────────────────┐
│  SaaS Platform (Streamlit / FastAPI)     │
│  ┌───────────┐  ┌───────────┐           │
│  │ Data Upload │  │ Attribution │           │
│  │ CSV/API    │  │ Model Select│           │
│  └─────┬─────┘  └─────┬─────┘           │
│        └───────┬───────┘                 │
│                ▼                         │
│      ┌─────────────────┐                │
│      │  DuckDB Engine   │                │
│      │  LATERAL JOIN    │                │
│      │  Window Functions│                │
│      └────────┬────────┘                │
│               ▼                         │
│      ┌─────────────────┐                │
│      │  Visual Reports  │                │
│      │  Export / Schedule│                │
│      └─────────────────┘                │
└─────────────────────────────────────────┘

Pricing Strategy:

  • Free tier: 10K records/month, 5 users
  • Professional: $49/month/business, unlimited records, API access
  • Enterprise: $199/month, multi-tenant + custom attribution models

Revenue Projection:

  • 50 paying customers × $49 = $2,450/month
  • 10 enterprise clients × $199 = $1,990/month
  • Total: ~$4,440/month

Path B: Project-Based Attribution Consulting

Offer one-time attribution analysis services for e-commerce businesses:

  • Single project fee: $1,000-$3,000
  • Maintenance: $300/month
  • 3-5 projects per month = $5,000+/month

Path C: Embedded Analytics API

Wrap the DuckDB attribution engine as a REST API:

  • Pay-per-call pricing: $0.005/call
  • 10,000 calls/day = $150/day = $4,500/month

Why DuckDB Instead of Other Solutions?

DimensionDuckDBSparkPython + PandasCommercial Tools
Deployment complexityZero dependenciesNeeds clusterModerateNone needed
Query performanceColumnar vectorizedDistributedMemory-limitedDepends on volume
Model flexibilityFully custom SQLProgrammable but complexProgrammable but slowFixed models
EmbeddabilityNative embeddingRequires REST serviceNeeds wrappingVia API
CostFree open-source$200+/monthFree$500+/month
Learning curveSQLPySparkPythonLow

Monetization Summary

  1. Fastest path: Build a free attribution analysis tool with Streamlit, promote through Google Ads/Facebook marketing communities, collect emails, then convert to paying users
  2. Differentiation: Commercial tools are expensive with fixed models; DuckDB enables custom attribution models (e.g., hybrid models combining business rules)
  3. Technical moat: Package attribution SQL as a DuckDB extension or Python package for reusable technical assets
  4. Expansion path: Attribution → LTV prediction → Budget allocation optimization, progressively building a complete marketing intelligence platform

📌 Call to Action: Run your first attribution analysis with DuckDB today. Prepare two CSV files (click records + conversion records), run the SQL above, and see how different attribution models change your channel budget recommendations—that’s your first sellable analysis report.

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

Built with Hugo
Theme Stack designed by Jimmy

⚠️ This site is an independent community project, not affiliated with, endorsed by, or sponsored by the DuckDB Foundation or official DuckDB project.

"DuckDB" is a registered trademark of the DuckDB Foundation. This site uses the name solely for factual description purposes.

All content is for educational and community promotion purposes only and does not constitute any commercial service.