Features
Meta Ads spend and ROAS
On this page
See which Facebook and Instagram campaigns bring paying customers: Meta's spend next to the visitors, customers and revenue MetricRaven attributes to each campaign, ad set and ad.
Before you start
- The tracking script on every landing page.
- Revenue from a payment provider or the Payments API. Without revenue you still get spend, visitors and cost per visit, but no ROAS.
- A Facebook account with access to the ad account in Meta Business Settings.
Connect Meta Ads
Open Settings → Integrations → Meta Ads
Choose Connect Meta Ads. Website admins and owners can connect; viewers see the status.
Sign in with Facebook and allow ads_read
MetricRaven asks only for read access to ad performance (
ads_read). It can't create, edit or pay for ads.Choose the ad account
Each website tracks one ad account. MetricRaven imports the last 90 days of spend, impressions and clicks per ad and day, then syncs once a day. Sync now fetches the latest numbers.
Add the URL parameters to your ads
See below. Visits only match a campaign when its ads carry these tags.
Open Revenue → Paid acquisition
Spend, attributed revenue, ROAS, cost per customer and your top campaigns for the period. A ROAS tile joins the Revenue headline figures. Full table switches between campaigns, ad sets and ads. Click a campaign for its daily spend against the revenue it brought, its ad sets and ads, and its customers (each opens their journey). On Traffic, Acquisition → Campaign links here (Ad spend & ROAS on Revenue).
Verify it works
The settings panel shows the ad account, its currency and timezone, and the last synced day. Click one of your own ads, then check that the visit appears on its campaign row.
Recommended URL parameters
In Ads Manager, open each ad (or select several and edit them together), go to Tracking → URL parameters and paste:
utm_source=meta&utm_medium=paid-social&utm_campaign={{campaign.id}}&utm_term={{adset.id}}&utm_content={{ad.id}}Meta fills in the {{…}} values when someone clicks. IDs never change when you rename a campaign, so history stays matched. If you prefer readable values, names work too (a campaign, ad set or ad still matches the names it had in synced days before a rename, and is shown under its current name):
utm_source=meta&utm_medium=paid-social&utm_campaign={{campaign.name}}&utm_term={{adset.name}}&utm_content={{ad.name}}utm_medium=paid-social puts these visits in the Paid channel. Keep one template on every ad so all of them are matched the same way.How visits are matched to campaigns
For each visit, the strongest signal wins:
utm_contentequal to an ad ID: that ad, its ad set and campaign.utm_campaignequal to a campaign ID, or to a campaign name (ignoring case).utm_termthen picks the ad set andutm_contentthe ad, by ID or name.utm_termequal to an ad set ID: that ad set and its campaign.- Meta-looking traffic (
utm_sourcemeta, facebook, fb, instagram, ig…, or anfbclidclick) whoseutm_campaigncontains a campaign name. The longest matching name wins.
Paid Meta visits that match no synced campaign (for example a click with only fbclid, or Meta tags with an unknown campaign) appear on their own line below the totals, Unattributed Meta clicks (fbclid only, may include organic shares), with their visitors, customers and revenue but no spend or ROAS. Facebook also adds fbclid to links shared in organic posts, so these visits can't be told apart from ad clicks: they are left out of the totals, ROAS and cost per customer. Tag your ads to keep this line small.
Metrics
| Column | Meaning |
|---|---|
| Spend | Meta's reported spend, converted into your reporting currency. |
| Link clicks | Clicks to the ad's destination (Meta's inline link clicks). |
| Ad visitors | Visitors who arrived from this campaign, ad set or ad in the period, each counted once (on their first visit from it). |
| Customers | Distinct paying customers whose first tracked visit matched this row, for payments in the period. |
| Revenue | Those payments, net of refunds, in your reporting currency (first touch). |
| ROAS | Revenue ÷ spend. 2.50x means each unit spent brought back 2.50 in revenue. |
| Cost / customer | Spend ÷ customers. |
| Assisted revenue | Revenue in the period from customers who clicked this row's ads before paying but whose first tracked visit came from somewhere else (another campaign, search, a newsletter…). Not part of Revenue or ROAS. |
Why ad visitors and revenue can disagree: revenue is credited to the customer's first tracked visit, the same rule as the rest of the dashboard, even when that visit was before the period. Ad visitors are the people who arrived from the ad during the period. A campaign can have many ad visitors and little revenue (they haven't paid yet, or first came from elsewhere: see Assisted revenue), or revenue from customers who first clicked months ago.
Assisted revenue counts each payment on every row whose ads the customer clicked before paying, except the row that already gets it as first touch, so rows don't add up to the total. The total counts payments whose first touch was no campaign of the ad account at all, once each.
Totals add every campaign row; customers are counted once. Unattributed Meta clicks are not included. Revenue uses the same first-touch rule as the Campaigns table, so the two never double count: a customer whose first visit came from search and a later one from a Meta ad is credited to search. Revenue includes renewals, like the rest of the dashboard. A visitor counts once, on their first visit in the period that matches a campaign or the unattributed line; an untagged or organic Facebook visit before it doesn't hide it. Ratios show — when their denominator is zero. The API also returns cost per link click.
Currency and timezone
Spend arrives in the ad account's currency. When it differs from your reporting currency, each day is converted at that day's European Central Bank reference rate, like payments (each payment at its own day's rate), so ROAS compares both in one currency. Days without a rate yet are left out of spend, ROAS and cost per link click, and the table says how much.
Meta reports whole days in the ad account's timezone, while your period follows the dashboard's timezone. An ad account day counts in the period that contains its midpoint (noon in the ad account's timezone), so a one-day period always gets one day of spend and two neighbouring periods never share a day. A period that runs up to now also includes the ad account's current day, spend so far. When both timezones are the same, the days are exactly the dates you selected. The table shows which days it summed.
Rolling periods get as many ad account days as the dashboard period has, counting today's partial day as one: Last 7 days (the last 7 × 24 hours, as on the traffic report) is today and the 6 days before it, never 8 days of spend. Last 30 days is 30 days, Last 24 hours is today. Calendar periods (Today, This week, This month, a custom range) keep every day they contain.
API, MCP and CLI
- REST:
GET /api/v1/stats/ads?provider=meta(or the alias/api/v1/stats/meta-ads) with the usual period parameters andlevel=campaign|adset|ad;provider=allcombines every connected network. See the Reports API. - MCP:
get_ad_spend(every network) orget_meta_ads. - CLI:
metricraven analytics ads [campaign|adgroup|ad] --provider metaormetricraven analytics meta-ads [campaign|adset|ad].
Troubleshooting
- No ad accounts listed: the Facebook login you used has no access to the ad account. Ask for access in Business Settings, then choose Reconnect Meta.
- ROAS is empty: there is no spend in the period, or no attributed revenue yet. Check the payment integration and that ads carry the URL parameters.
- Connection expires: Meta user access lasts about 60 days and can only be renewed by signing in again. Settings → Integrations → Meta Ads shows Connection expires on the date, turns into a warning 7 days before, and admins and the owner get an email then and again when it has expired. Choose Reconnect Meta: your ad account and history are kept and the sync catches up.
- Numbers differ from Ads Manager: Meta counts modeled and view-through conversions from its pixel. MetricRaven counts tracked visitors and payments only, so revenue is usually lower and closer to what you were paid.
Details
- Disconnecting deletes the imported spend and the stored Meta token. Visits and payments stay.
- Switching ad accounts removes the old account's spend and imports 90 days of the new one.
- Each sync re-reads the last three days, because Meta can still adjust recent spend, and replaces them (no double counting). Paused, archived and deleted ads are imported too, so spend matches the account total in Ads Manager.
- Very large ad accounts: when Meta answers that a request is too large, times out, or a window has more rows than one read allows, the sync runs the same query as one of Meta's asynchronous report jobs and reads the result when it is ready.
- Traffic filters don't apply to this table: spend can't be split by visitor properties.
- The public demo shows a synthetic ad account with sample spend.