Feature
Google Sheets custom metrics for Meta Ads rules
A custom metric in AdOps points at one Google Sheet — a spreadsheet ID, a sheet name, a lookup column and a value column — so a rule can test a live Meta Ads number against a figure a person maintains by hand, such as a target ROAS, a stock level or a margin.
A named metric bound to a Google Sheet, resolved at evaluation time, usable in conditions, budget values and campaign names.
In numbers
The figures behind this feature
- Data source
- Google Sheets The only custom metric source type offered in the builder
- Fields per metric
- 4 Spreadsheet ID, sheet name, column ID, value column
- In-memory cache window
- 2 minutes Measured from the last access, keyed by spreadsheet ID and sheet name
- Fetch order
- 3 layers CSV export first, Google Sheets API next, MongoDB mirror last
- CSV download timeout
- 30 seconds Applied to the direct CSV export request
- Parallel sheet reads
- 3 per chunk Batch fetches run three spreadsheets at a time
Sample data AdOps custom metrics let a rule compare Meta’s numbers against yours. A custom metric names a Google spreadsheet ID, a sheet name, a lookup column and a value column, and AdOps resolves it at evaluation time by finding the row whose lookup column matches the campaign and reading its value column. The result behaves like any other metric.
Why put a number in a spreadsheet at all?
Because some of the numbers that should govern a campaign never appear in Ads Manager. Margin after cost of goods. Stock on hand. A target ROAS that differs by SKU because the gross margin differs by SKU. A media buyer who maintains those figures already maintains them in a sheet.
The alternative is hardcoding a threshold into every rule and re-editing every rule when the number changes. A custom metric moves that number out of the rule and into the sheet, where the person who owns it can change it without touching automation.
What does a custom metric look like?
Creating one takes a name, an optional description, and four binding fields.
| Field | Example | What it is |
|---|---|---|
| Google Spreadsheet ID | the id in the sheet URL | Which spreadsheet to open |
| Sheet Name | Sheet1 | Which tab inside it |
| Column ID | Date | The lookup column matched against the campaign |
| Value Column | Revenue | The column whose number is returned |
The builder places the hint that the spreadsheet ID is found in the spreadsheet URL, and the data source type select currently offers Google Sheets. When you save, AdOps opens the sheet and confirms that both named columns exist. If either is missing you get a red Google Sheets Validation Failed modal listing every problem and a how-to-fix checklist, rather than a rule that silently evaluates against nothing at 3 a.m.
The worked example: a target ROAS per SKU
Keep a sheet your buyer owns. One row per campaign, keyed by campaign ID in the lookup column, with the target ROAS for the SKU that campaign sells in the value column.
Then write one rule for the whole account. Set a condition on Purchase ROAS over Last 7 days, switch its compare mode to metric, and point the compared side at the custom metric. The threshold is no longer a number you typed. It is whatever the buyer put in the sheet this morning, per campaign.
Pair it with a Pause action and a name tag, and one rule now enforces per-SKU economics across an account where every SKU has a different break-even. When the merchandising team drops a price, they edit a cell, and the automation follows.
The same metric can drive a budget change directly. A budget action’s value type accepts custom_metric, so the amount written to Meta can come from a column that already holds the number your finance team agreed to.
How does AdOps read the sheet without hammering Google?
Three layers, in order.
- CSV export. AdOps hits Google’s direct CSV export endpoint with a 30-second timeout and parses it with a quote-aware parser. That parser tracks quoting state, handles commas inside quoted cells and escaped doubled quotes, trims header names, skips blank lines, and drops any row whose column count does not match the header. Real back-office sheets contain currency, addresses and product names with commas in them, and a naive comma split shifts every value one column left.
- Google Sheets API. Permission and not-found errors are surfaced immediately rather than retried; other failures fall through to the slower API download.
- MongoDB mirror. Every successful fetch is written to a
google_sheetscollection. If Google is unreachable entirely, the engine serves the last known rows from that mirror.
On top of that sits an in-memory cache keyed by spreadsheet and sheet name. A hit within 2 minutes of the last access is served from memory, so hundreds of campaigns in one batch share a single spreadsheet read. Batch fetches run 3 sheets in parallel per chunk.
Be aware of what that cache window actually means. The 2 minutes is measured from the last access, not from the last fetch. Under continuous access, which is exactly the large-batch case, the same snapshot can be served for longer than 2 minutes. Edit the sheet before a run, not in the middle of one, and treat the value as the number your team last agreed rather than a live feed.
Capabilities
What this feature does, item by item.
Each line is a behaviour you can reproduce in the product today.
- Four fields, one number
- A custom metric names a Google spreadsheet ID, a sheet name, a lookup column and a value column. AdOps finds the row whose lookup column matches the campaign and reads the value column.
- Validated before you save
- On save the builder reads the sheet and checks that both columns really exist. A failure opens a Google Sheets Validation Failed modal listing every problem plus a how to fix checklist.
- Usable in four places
- A custom metric can be the metric in a condition, the metric a condition compares against, the value driving a budget change, or a token expanded into a campaign name.
- Cached in memory, mirrored in the database
- Rows are held in memory for 2 minutes from last use, keyed by spreadsheet and sheet name, and every successful fetch is mirrored to MongoDB so a Google outage does not stop a run.
- A CSV parser that survives real sheets
- The fast path uses Google's CSV export with a quote-aware parser that handles commas inside quoted cells and drops rows whose column count does not match the header.
Questions
Asked about this feature.
Direct answers, with the numbers attached.
What is an AdOps custom metric?
An AdOps custom metric is a named number that AdOps reads out of a Google Sheet at evaluation time. The metric stores a spreadsheet ID, a sheet name, a lookup column and a value column, and AdOps returns the value column of the row whose lookup column matches the campaign being evaluated.
Where can a custom metric be used in AdOps?
A custom metric can be used in four places: as the metric in a rule condition, as the metric a condition compares against, as the value type driving an increase, decrease or set budget action, and as a token expanded into a campaign name.
What happens if Google Sheets is unreachable?
AdOps tries Google's direct CSV export first and falls back to the Google Sheets API. If both fail, it serves the last successful copy of the rows from its own MongoDB mirror, so a spreadsheet-driven rule keeps evaluating rather than silently skipping.
How fresh is the spreadsheet data a rule sees?
AdOps holds a sheet's rows in memory for 2 minutes measured from the last time they were used, so a sheet read continuously across a large batch can be served from the same snapshot for longer than 2 minutes. Edit the sheet before the run, not during it.
Does AdOps validate my sheet before saving a custom metric?
Yes. When you save a custom metric, AdOps reads the spreadsheet and confirms that both the lookup column and the value column exist. If either is missing, it opens a Google Sheets Validation Failed modal listing each problem alongside a how to fix checklist.
Nearby
Features that work with this one
Write this rule against your own account.
Every feature on this page is available on every plan. Connect one ad account, copy a template, and read the log after the first run.