Ad Spend (CPA & ROAS)
Connect your ad platforms to pull real spend into Trcker and see CPA and ROAS next to your conversions. ChatGPT Ads and Google Ads supported today.
What it does
Trcker already tracks the conversions your paid traffic drives. The Ad Spend connector pulls the cost side too — real spend per campaign, per day — and joins it to your conversions so you can see:
- CPA (cost per acquisition) — ad spend ÷ the conversions that traffic drove
- ROAS (return on ad spend) — the revenue those conversions booked ÷ ad spend
Two connectors are available today: OpenAI ChatGPT Ads and Google Ads. Both surface CPA and ROAS through the same dashboard surfaces — Overview tile and Offers columns — with no new mental model to learn.
---
Connecting ChatGPT Ads
- Go to Integrations → Ad Spend in your brand sidebar.
- Paste your OpenAI Advertiser API key and click Connect.
- - Find it in ChatGPT Ads Manager → Settings → General → "Create New API Key."
- - This is the Advertiser API key — it is not the same as the Conversions API key you use to send conversions back to OpenAI.
- Trcker stores the key encrypted at rest and runs a first sync immediately.
> Your API key is decrypted only at the moment Trcker calls the ad platform, and is never written to logs.
---
Mapping campaigns to offers
After the first successful sync, Trcker lists the campaigns it discovered. For each one, pick the Trcker offer it drives traffic to.
- Mapped campaigns get precise per-offer Spend / CPA / ROAS columns on your Offers table.
- Unmapped spend still counts toward your brand-level Spend / CPA / ROAS — so you see useful numbers from day one, even before you finish mapping.
You can re-map a campaign at any time; changes apply on the next dashboard load.
---
Where CPA & ROAS show up
- Overview — a brand-level Ad spend · ChatGPT Ads tile with Spend, CPA, and ROAS for the selected date range. It only appears once you have spend in the window.
- Offers table — Spend, CPA, and ROAS columns per offer. Offers with no mapped spend show "—".
How the numbers are computed
CPA and ROAS divide ad spend by the conversions attributed to ad-sourced traffic (clicks Trcker tagged as coming from ChatGPT) — never by your organic conversions. Dividing ad spend by all of your conversions would understate CPA and overstate ROAS, so Trcker scopes the denominator to the traffic your ad actually drove.
- Per-offer: spend from the campaigns mapped to that offer ÷ that offer's ad-sourced conversions.
- Brand-level (blended): all pulled spend ÷ all ad-sourced conversions.
Both CPA and ROAS show "—" when there's nothing to divide by (no conversions, or no spend).
---
Connecting Google Ads
- Go to Integrations → Advertising → Google Ads in your brand sidebar.
- Click Connect and authorize with your Google account. Trcker requests read-only access to your Google Ads spend data — it cannot change your campaigns, bids, or settings.
- After authorization, Trcker runs a first sync and begins pulling daily campaign spend.
> Trcker connects via OAuth and stores only the access needed to read your spend. Your Google account credentials are never stored, and Trcker cannot modify your campaigns.
---
Mapping Google Ads campaigns to offers
After the first successful sync, Trcker lists the campaigns it discovered — same flow as ChatGPT Ads. For each campaign, pick the Trcker offer it drives traffic to.
- Mapped campaigns get precise per-offer Spend / CPA / ROAS columns on your Offers table.
- Unmapped spend still counts toward your brand-level Spend / CPA / ROAS — so numbers are useful from day one, even before you finish mapping.
---
Google Ads click-ID attribution
Google's click IDs — gclid, gbraid, and wbraid — identify ad-sourced clicks. Trcker captures and preserves these on every redirect automatically (no setup required; see Ad-Platform Click IDs).
CPA and ROAS from Google Ads spend are computed against clicks that carried one of these IDs — never your organic conversions. This keeps both metrics accurate: dividing ad spend by all your conversions would understate CPA and overstate ROAS.
---
Campaign-level ROAS
The Overview tile and Offers columns show CPA and ROAS at the account and per-offer level. To see cost vs revenue by campaign — the media-buyer view — open Reports → Campaigns.
It needs one piece of setup: Trcker has to know which campaign each conversion came from. Pass Google's {campaignid} ValueTrack parameter into your ad's final URL so it lands on a Trcker sub param, then tell Trcker which sub holds it:
- In your Google Ads final URL (or tracking template), pass the campaign id into a sub — e.g.
?sub1={campaignid}. - On Reports → Campaigns, set "Campaign id tracked in" to that sub (
sub1–sub5).
The report joins your Google spend (by campaign) to the revenue from conversions whose click carried that campaign id, and shows per campaign:
- Spend, Revenue, and Profit — profit is revenue − ad spend (the media-buyer number), not revenue − affiliate payout
- Conv. and CPA
- ROAS — revenue ÷ spend
Campaigns are sorted by spend. A campaign with spend but no revenue, and revenue whose campaign id matches no spend (a mis-tagged link), are both shown so nothing hides.
---
Hourly spend
To see your intraday pattern — which hours of the day you spend, and where clicks are cheapest — open Reports → Hourly. It shows Google Ads spend, impressions, clicks, and CPC by hour of day (0–23), summed across the selected date range, with a bar for the spend shape at a glance.
- Hours are in your Google Ads account's reporting timezone (the same timezone your spend reports use).
- Hourly spend is pulled daily alongside campaign spend, and on demand whenever you click Refresh on the Campaigns tab — so you can pull today's hours without waiting for the nightly sync.
- It's stored separately from daily spend, so it never affects your daily, campaign, or ROAS numbers.
> Revenue / ROAS by hour isn't shown yet: the spend hour is in the ad account's timezone while click and conversion timestamps are UTC, so an hour-of-day ROAS join would be timezone-incorrect. Spend, clicks, and CPC by hour are accurate today; revenue-by-hour is a planned follow-on.
---
Landing pages
To see revenue by landing page — which pages actually convert — open Reports → Landing pages. It shows revenue, conversions, and AOV (average order value) per landing page, sorted by revenue.
Like campaign ROAS, it needs one piece of setup: tag your ad links so the landing-page id lands on a Trcker sub param, then tell Trcker which sub holds it:
- In your ad's final URL (or tracking template), pass the landing-page id into a sub — e.g.
?sub2={lpurl}(Google) or your own page slug. - On Reports → Landing pages, set "Landing page tracked in" to that sub (
sub1–sub5).
Revenue is grouped from every tracked conversion (not just paid), so it reconciles with your Breakdown report.
> Cost / ROAS per landing page is deferred. Ad spend can't be split below campaign grain from the reporting API, so per-LP cost would need either a one-campaign-per-LP structure or a defined allocation rule. Revenue-by-LP ships first; cost-per-LP is a planned follow-on.
---
Keeping spend fresh
Both connectors share the same refresh model:
- Automatic: a daily sync pulls a trailing window and updates spend (so late corrections from the ad platform are picked up).
- On demand: refresh any time — click Sync now on a connect card under Integrations, or Refresh spend on Reports → Campaigns. Either one re-pulls every ad platform you've connected (ChatGPT Ads, Google Ads, …) in a single click, then updates the page.
If a sync fails (expired token, ad-platform outage), that connection shows an error status with a short message and your dashboard keeps showing the last good data — Trcker never displays partial or guessed spend. One platform erroring doesn't stop the others from refreshing.
---
Disconnecting
Click Disconnect on the connect card. For ChatGPT Ads this deletes the stored API key; for Google Ads it revokes the OAuth token. Your historical spend and campaign mappings are kept, so reconnecting later resumes where you left off.