How to Calculate True Facebook Ads ROAS (Beyond the Dashboard)
By MCP Analytics Team | Published , updated
Facebook's Ads Manager reports a ROAS (return on ad spend, or sales per ad dollar). That number counts every conversion Facebook claims credit for, including some the ads did not cause. Incremental ROAS counts only the sales the ads added beyond what a group that saw no ads would have bought. A worked example with simulated data comes first, then the method.
Worked Example (Simulated): How Much Did the Ads Add?
Question: How much more money may ads have brought in than a group that saw no ads?
Inputs: Simulated data, seed 24. The script sets up two groups of 1,000 people. It sets the buy rate at 0.08 for the control group, which sees no ads (a holdout group), and 0.10 for the ad group. Each buy is worth $50. Ad spend is $400. All of these are made-up values.
Method: Find the gap between the two buy rates. Multiply that gap by the group size and the value of one buy. Then divide the extra sales by ad spend. Incremental means sales the ads may have caused.
import numpy as np
seed = 24
rng = np.random.default_rng(seed)
people_per_group = 1000
control_purchase_rate = 0.08
ad_group_purchase_rate = 0.10
purchase_value = 50
ad_spend = 400
control_purchases = rng.binomial(people_per_group, control_purchase_rate)
test_purchases = rng.binomial(people_per_group, ad_group_purchase_rate)
rate_gap = (test_purchases / people_per_group
- control_purchases / people_per_group)
estimated_incremental_purchases = rate_gap * people_per_group
estimated_incremental_revenue = estimated_incremental_purchases * purchase_value
incremental_roas = estimated_incremental_revenue / ad_spend
print(f"Simulated data, seed {seed}")
print(f"People per group: {people_per_group}")
print(f"Control purchase rate: {control_purchase_rate:.2f}")
print(f"Ad group purchase rate: {ad_group_purchase_rate:.2f}")
print(f"Control purchases: {control_purchases}")
print(f"Ad group purchases: {test_purchases}")
print(f"Purchase value: ${purchase_value}")
print(f"Ad spend: ${ad_spend}")
print(f"Estimated rate gap: {rate_gap:.3f}")
print(f"Estimated incremental revenue: ${estimated_incremental_revenue:.2f}")
print(f"Estimated incremental ROAS: {incremental_roas:.2f}x")
Printed output:
Simulated data, seed 24
People per group: 1000
Control purchase rate: 0.08
Ad group purchase rate: 0.10
Control purchases: 83
Ad group purchases: 86
Purchase value: $50
Ad spend: $400
Estimated rate gap: 0.003
Estimated incremental revenue: $150.00
Estimated incremental ROAS: 0.37x
Result: The ad group had 86 buys. The control group had 83. The rate gap was 0.003, which gives $150.00 in extra sales and 0.37x incremental ROAS. The script builds in a larger lift: a buy rate of 0.10 against 0.08 is 20 more buys per 1,000 people, or $1,000 in extra sales and 2.5x incremental ROAS. This draw shows 0.37x. Two groups of 1,000 are too small to tell the two apart. Chance alone moves the gap between two groups of this size by roughly 13 buys (the square root of 1,000 x 0.08 x 0.92 plus 1,000 x 0.10 x 0.90), so both the built-in 20 and the drawn 3 are within what chance gives. This is low power, not no effect. Do not read 0.37x as "the ads barely worked".
Limit: This is a small made-up case with one draw. It cannot show what a real ad campaign would cause.
The problem isn't that Facebook is lying. The problem is structural: Facebook's attribution model was designed to measure its own contribution, using only its own data. It can't see what would have happened without the ad. It can't share credit with Google, email, or organic search. And its default attribution window (7-day click, 1-day view) captures conversions that were driven by other channels entirely.
Here's how to calculate what your Facebook ads are actually worth.
Why Facebook's ROAS Number Is Inflated
Understanding the inflation mechanics is essential before you can correct for them. Three structural factors cause Facebook to overcount conversions.
If you held out a random group, the free incrementality test tool counts how many customers your campaign caused beyond those who would have converted anyway.
Factor 1: View-Through Attribution
Facebook's default attribution window includes 1-day view-through conversions. If someone sees your ad in their feed (even without clicking) and then buys your product within 24 hours, Facebook claims that conversion. The user may have been searching for your product on Google, received an email, or simply typed your URL directly. Facebook's ad was one of many things they encountered that day -- but Facebook takes full credit.
View-through conversions have much weaker causal evidence than click-through ones. Export them separately so you can see what share of your own reported ROAS depends on them.
Factor 2: No Cross-Platform Credit Sharing
A customer sees your Facebook ad on Monday. On Wednesday, they search for your product on Google and click a paid search ad. On Friday, they open your email newsletter and buy. Facebook claims the conversion (within 7-day click window). Google claims the conversion (last-click). Your email platform claims the conversion (drove the final purchase). One sale, three platforms claiming credit. Your total "attributed revenue" across platforms is 3x the actual revenue.
Factor 3: Audience Selection Bias
Facebook's algorithm is optimized to show ads to people most likely to convert. This is great for efficiency, but it creates an attribution problem: these people were already likely to buy. When Facebook shows your ad to someone who was going to purchase anyway and they convert, the platform attributes the sale to the ad. The ad didn't cause the conversion -- it just happened to be shown to someone who was already in a buying mindset.
This effect is strongest in retargeting campaigns. You show ads to people who already visited your site, added items to their cart, or browsed your products. Many of these people were going to come back and buy regardless. Facebook takes credit for all of them.
How to Export the Right Data from Facebook
To calculate true ROAS, you need granular data that Facebook's summary dashboard doesn't show. Here's what to export and how.
Step 1: Set Up Your Export in Ads Manager
- Go to Ads Manager and click the Campaigns tab
- Set your date range (minimum 90 days for meaningful analysis)
- Click Columns: Performance and switch to Customize Columns
- Add these columns: Campaign Name, Ad Set Name, Date, Amount Spent, Impressions, Link Clicks, Purchases (7-day click), Purchases (1-day view), Purchase Value (7-day click), Purchase Value (1-day view)
- Under Breakdown, select By Time: Day
- Click Export and download as CSV
The key step is adding separate columns for 7-day click and 1-day view conversions. Facebook's default "Purchases" column combines both. You need them separated to analyze how much of your ROAS comes from view-through attribution (which has much weaker causal evidence than click-through).
Step 2: Structure Your CSV
Your exported CSV should have these columns for analysis:
date,campaign_name,ad_set_name,spend,impressions,link_clicks,
purchases_7d_click,purchases_1d_view,revenue_7d_click,revenue_1d_view
Example rows:
2026-01-15,Prospecting_Lookalike,LAL_1pct_Purchasers,842.30,124500,1830,28,12,3920.00,1680.00
2026-01-15,Retargeting_CartAbandoners,ATC_7d,312.50,45200,890,42,31,5880.00,4340.00
2026-01-15,Prospecting_Interest,Fitness_Interest,628.00,98300,1240,15,8,2100.00,1120.00
Step 3: Add Your Independent Revenue Data
Export daily revenue from your own system (Shopify, WooCommerce, Stripe, or your CRM). This is your source of truth for actual revenue. You'll compare this against Facebook's claims to identify the gap.
date,total_revenue,total_orders,new_customer_revenue,returning_customer_revenue
2026-01-15,18420.00,156,7890.00,10530.00
Three Methods to Calculate True ROAS
Method 1: Click-Only ROAS (Simple, Conservative)
Remove all view-through conversions and calculate ROAS using only click-attributed revenue. This is the simplest correction and gives you a more conservative (and more defensible) number.
Click-Only ROAS = Revenue (7-day click only) / Spend
Example:
Total Spend: $42,000/month
Revenue (7d click + 1d view): $176,400 (Reported ROAS: 4.2x)
Revenue (7d click only): $123,480 (Click-Only ROAS: 2.9x)
Gap: 30% of "revenue" came from view-through attribution
This is a good first step, but it still overcounts because click-attributed conversions can also be non-incremental (the person clicked the ad but would have bought anyway).
Method 2: Geo-Holdout Test (Gold Standard)
Pause Facebook ads in test markets and compare revenue to control markets. This measures causal impact, not correlation.
Credit is not the same as cause. What incremental impact actually means explains the difference in plain words.
- Select test and control markets: Choose 3-4 geographic regions with similar demographics, purchasing patterns, and seasonal trends. Use historical revenue data to verify they track closely.
- Run the holdout: Pause all Facebook ads in test markets for 4-6 weeks. Keep everything else constant (email, Google, organic).
- Measure the difference: Compare revenue per capita in test markets (no Facebook) vs. control markets (Facebook running).
- Calculate incremental ROAS:
Incremental Revenue = (Control Revenue/capita - Test Revenue/capita) x Test Population
Incremental ROAS = Incremental Revenue / Facebook Spend in Control Markets
Example:
Control markets revenue/capita: $2.40/month
Test markets revenue/capita: $1.92/month (20% drop)
Incremental revenue: $0.48/capita x 150,000 population = $72,000
Facebook spend (control): $28,000
Incremental ROAS: 2.57x (vs. 4.2x reported)
Run a geo-holdout test when your Facebook budget is large enough that over-investing would be costly. The test itself costs you weeks of reduced advertising in the test markets, but the result can keep you from over-investing based on an inflated ROAS.
Method 3: Regression-Based Incrementality (Data-Driven)
Use your daily spend and revenue data to model the relationship between Facebook investment and actual revenue, controlling for confounding variables.
The approach: regress your actual daily revenue (from Shopify/Stripe, not Facebook) against Facebook daily spend, while controlling for day of week, seasonality, email campaigns, promotions, and Google Ads spend.
Revenue = B0 + B1(FB_Spend) + B2(Google_Spend) + B3(Email_Sends) + B4(DayOfWeek) + B5(Month) + error
In this model, B1 estimates the link between Facebook spend and
revenue after the listed controls. Treat it as a link. To claim cause,
a study needs a sound design and clear assumptions.
If B1 = 2.1, the model links each extra $1 of spend with about $2.10
of revenue.
This method requires at least 90 days of daily data with meaningful variation in spend levels. If you spend the same amount every day, regression can't separate Facebook's effect from the baseline.
The regression works because Facebook spend varies day to day (budget pacing, auction dynamics, campaign changes). Days with higher spend should produce higher revenue if Facebook is driving incremental sales. The regression coefficient captures this relationship while filtering out other factors that also drive revenue.
What Real ROAS Looks Like vs. Reported ROAS
The table below is a made-up illustration of how reported, click-only, and incremental ROAS can differ by campaign type. The numbers are invented to show the pattern. They are not measured data or a benchmark.
| Campaign Type | Reported ROAS | Click-Only ROAS | Incremental ROAS | Inflation % |
|---|---|---|---|---|
| Prospecting (Cold) | 2.8x | 2.2x | 1.8x | 36% |
| Prospecting (Lookalike) | 4.1x | 3.2x | 2.4x | 41% |
| Retargeting (Site Visitors) | 8.5x | 5.8x | 2.1x | 75% |
| Retargeting (Cart Abandoners) | 12.0x | 8.2x | 3.2x | 73% |
| Brand Awareness | 1.2x | 0.8x | 0.9x* | 25% |
*In this illustration, a brand awareness campaign has a higher incremental ROAS than click-only, because its value can appear in other channels (branded search, direct traffic) that click attribution doesn't capture.
In the made-up table, retargeting shows the highest reported ROAS and the biggest gap. Many cart abandoners come back and buy without seeing a retargeting ad. Before you celebrate retargeting's reported return, run a holdout test and compare it with what happens to actual revenue.
Applying This to Your Budget Decisions
Once you know your true incremental ROAS by campaign type, you can make smarter budget decisions.
Step 1: Rank Campaigns by Incremental ROAS
Sort all your campaigns from highest to lowest incremental ROAS. This is your priority order for budget allocation. Don't use Facebook's reported ROAS for this ranking -- it will lead you to over-invest in retargeting and under-invest in prospecting.
Step 2: Find the Diminishing Returns Point
For each campaign type, plot daily spend against incremental revenue. At some spend level, additional investment stops producing proportional returns. This is your saturation point. Budget beyond this point is wasted -- it goes to reaching less relevant audiences at higher CPMs.
Typical saturation patterns:
- Prospecting (cold): Returns can stay steady at first, then drop as you run out of new people to reach
- Lookalike audiences: Returns are often strongest for the closest lookalike audience and weaker as the audience widens
- Retargeting: Can saturate quickly because the audience pool is small. A holdout test shows how much of the budget is needed
Step 3: Reallocate Based on Incremental Value
Move budget from campaigns with low incremental ROAS (typically retargeting) to campaigns with high incremental ROAS that haven't hit saturation (typically cold prospecting). This feels counterintuitive because you're moving money from campaigns that "look good" in Ads Manager to campaigns that look worse. But you're optimizing for real revenue, not reported revenue.
One option is to shift part of the retargeting budget into top-of-funnel prospecting and test the result. Total Facebook-reported ROAS may drop (because prospecting shows lower reported numbers), while actual revenue may rise if you buy more incremental conversions. Compare actual revenue before and after.
Building an Ongoing Measurement System
Calculating true ROAS isn't a one-time exercise. Your incremental returns change as audiences saturate, creatives age, and competitors shift strategy. Build a measurement cadence:
- Weekly: Track click-only ROAS by campaign type. Compare to prior 4-week average. Flag campaigns where click-only ROAS drops below breakeven.
- Monthly: Run the regression analysis with updated daily data. Compare the Facebook spend coefficient month-over-month. A declining coefficient means diminishing returns -- either you're reaching saturation or creative fatigue is setting in.
- Quarterly: Run a geo-holdout test on your largest campaign type. Rotate which campaign you test each quarter. After a year, you'll have fresh incrementality data for all major campaign types.
Set up a weekly Facebook Ads export (Campaigns > Reports > Schedule Report). Include separate click and view columns. Combine with your Shopify/Stripe daily revenue export. Upload both CSVs for automated ROAS analysis that tracks click-only and regression-based ROAS over time, catching problems before they waste budget.
Working on this for your business? See the marketing analytics tools.
Your turn
Bring your own data and the question you actually need answered.
MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai
Frequently Asked Questions
Facebook's attribution model takes credit for any conversion that happens within a defined window (default: 7-day click, 1-day view) after a user sees or clicks an ad. This means if someone was already going to buy your product and happened to see a Facebook ad earlier that day, Facebook claims that conversion. Some Facebook-attributed conversions are not incremental: they would have happened without the ad. Facebook also can't see conversions from other channels, so it has no way to share credit.
In Facebook Ads Manager, go to the Campaigns tab and click "Reports" then "Export Table Data" to download a CSV. For more granular data, use the Ads Reporting tool: select your date range, break down by day, and include columns for spend, impressions, clicks, purchases, and purchase value. The critical step is adding separate columns for 7-day click and 1-day view conversions, which Facebook combines by default.
Reported ROAS is total attributed revenue divided by ad spend -- it includes all conversions Facebook claims credit for. Incremental ROAS only counts conversions that would NOT have happened without the ad. If Facebook reports 4x ROAS but 40% of conversions were not incremental, your true incremental ROAS is 2.4x. The gap can be wider for retargeting campaigns, where many attributed buyers were already likely to buy.
Facebook offers built-in Conversion Lift Studies. Facebook randomly splits your target audience into a test group (sees ads) and a holdout group (doesn't see ads), then compares conversion rates. You can also run a manual geo-holdout: pause Facebook ads in one geographic region for 4-6 weeks while keeping them running in a comparable region. Compare revenue per capita in both regions. The difference is your incremental revenue from Facebook.
Target ROAS depends on your margins. For example, with 60% gross margins you need at least 1.67x ROAS to break even (1 / 0.60). The key is benchmarking against incremental ROAS, not the dashboard number. Calculate your breakeven ROAS first (1 / gross margin %), then set a target above it.