Updated

How to Know Which Meta Ads Made Money

meta adsreportingattributionrevenue trackinglean team

By the time the agency report lands in your inbox, you have already approved next week’s budget.

That is the real cost of slow reporting. Not the three hours it takes to build the summary. The decisions you make before the numbers are ready.

My team ran Meta campaigns across multiple ad sets. Every Monday I needed to know: which campaigns made money last week, which ones burned through budget without producing revenue, and where to move spend for the week ahead.

Instead I got a PDF on Friday covering last week. By the time I read it, the window to act had passed.

So I built a script. Now the answer takes four minutes.

The real question behind the report

Most reporting answers the wrong question. “How many leads did we get?” is a traffic question. “Which campaigns produced revenue?” is a business question.

The difference matters because they need different data. Traffic reports pull from Meta Ads Manager. Revenue reports pull from your CRM, your WhatsApp logs, or your invoicing system. Those systems almost never talk to each other automatically.

That gap — between where the lead came from and where the money landed — is exactly what eats three hours every week. You are not doing analysis. You are doing data assembly.

Python does the assembly automatically. Every week, the same script pulls from the same sources, cleans the same formats, and produces the same table. Four minutes to run and verify. The three hours go back to decisions.

What the script connects

The minimum version connects three sources.

Meta Ads Manager — campaign name, ad set, spend, clicks. Pulled via the Marketing API or exported as a CSV.

CRM or WhatsApp log — lead source (campaign tag), qualification status, quotation status, payment status. The source field is the key. If you tag leads at entry with the campaign that brought them, you can trace revenue back to the ad.

Revenue record — confirmed payments, invoice dates, amounts. This is what turns a report from platform data into bank data.

def build_weekly_revenue_report(ads_data, crm_data, start_date, end_date):
    ads = ads_data[(ads_data['date'] >= start_date) & (ads_data['date'] <= end_date)]
    leads = crm_data[(crm_data['source_date'] >= start_date) & (crm_data['source_date'] <= end_date)]

    spend_by_campaign = ads.groupby('campaign')['spend'].sum()
    revenue_by_campaign = (
        leads[leads['status'] == 'won']
        .groupby('campaign_source')['deal_value']
        .sum()
    )

    report = pd.DataFrame({
        'spend': spend_by_campaign,
        'revenue': revenue_by_campaign
    }).fillna(0)

    report['roas'] = report['revenue'] / report['spend'].replace(0, float('nan'))
    return report.sort_values('revenue', ascending=False)

The output is one table. Every campaign sorted by revenue. Spend in. Revenue out. Real ROAS next to each one — not the platform estimate, but banked cash.

What changes when reporting takes 5 minutes

When a report takes three hours, you run it once a week and accept stale data. When it takes four minutes, you run it any morning. Budget decisions change because the information is current.

The campaigns that looked good on Friday’s agency report often look different on Monday’s actual data. Leads that entered with no campaign tag — meaning the tracking was broken — show up as a gap in the revenue column. That gap tells you more than any green arrow in Ads Manager.

Speed also changes who asks the questions. When the report takes three hours, only one person ever builds it. When it runs in four minutes, the owner can run it themselves before the agency call. That changes the conversation.

If you want a list of the right questions to bring into that conversation, five questions to ask your Meta ads agency every month gives you the framework.

What to build first

Start with one data source and one output. Pull the Meta spend CSV. Load it with pandas. Group by campaign. Output one number: spend per campaign, last seven days.

That alone takes less than a week to build and saves at least an hour per week. Once that works, add the revenue column. Once that works, add the WhatsApp source tags.

Each step is small. The cumulative result is a business that makes budget decisions on Monday with data from Sunday — not on Friday with data from last week.

The code for this report is in the marketing-report-automation repository.

Flying blind past the lead form?

I work with business owners who are spending on Meta ads but can't connect the spend to closed sales. I track every lead from the ad click to the closed deal — so budget decisions are made on revenue, not lead counts.

Start a conversation →