Build-in-public post — the setup we use to let Claude read Meta ad data and prescribe the next move. Part of our AI workflows pillar.
The customer pain point
The weekly paid-ads ritual at most brands: export Ads Manager to a spreadsheet, eyeball which creative is fading, manually decide what to test next. It's slow, it's inconsistent week to week, and it's exactly the kind of pattern-recognition work a model does well — if it can actually see the data.
The blocker is access. Claude can't log into Ads Manager. So you either keep copy-pasting screenshots (lossy, slow) or you give it a clean pipe to the Meta Marketing API. This is the pipe.
Table of Contents
- What "Meta Ads CLI" Means Here
- Setup: Meta API Access
- Setup: The CLI Wrapper
- Connecting Claude (MCP or Pipe)
- Usage Prompts
- What to Watch For
Key Takeaways
| Point | Details |
|---|---|
| The pipe | A thin CLI over the Meta Marketing API → Claude reads campaign/ad-set/ad performance as structured data, not screenshots. |
| Setup | Meta system-user token + a CLI wrapper + connect to Claude via MCP or stdout pipe. ~1–2 hrs one-time. |
| What it does | Pulls performance, ranks creative by fatigue/ROAS, drafts the next creative test brief. |
| Guardrail | Read-only token. Claude analyzes and recommends; a human pushes changes to the ad account. |
What "Meta Ads CLI" Means Here
"Meta Ads CLI" = a small command-line tool you build that wraps the Meta Marketing API. It exposes a few commands:
ads:performance --account <id> --since 30d→ JSON of every ad's spend, ROAS, CTR, CPM, frequencyads:creative --account <id>→ creative metadata + thumbnails per adads:fatigue --account <id>→ frequency + declining-CTR flags per ad
Claude calls these, gets clean JSON, and reasons over it. No screenshots, no copy-paste, no lossy context.
Setup: Meta API Access
- Create a System User — business.facebook.com/settings/system-users → Add → name it
branva-ads-readonly. - Grant asset access — assign the ad account with read/analyze permission only. Do not grant ad-management write access for the analysis pipe (least privilege — see guardrails).
- Generate a token — System User → Generate New Token → select your app → scope
ads_readonly. Set expiry to Never (System User tokens can be permanent). - Store the token — environment variable, never in code.
META_ADS_TOKEN=...,META_AD_ACCOUNT_ID=act_....
Setup: The CLI Wrapper
A minimal Node/Python script that hits the Marketing API insights endpoint. The core call:
GET https://graph.facebook.com/v18.0/{ad_account_id}/insights
?level=ad
&fields=ad_name,spend,purchase_roas,ctr,cpm,frequency,impressions,actions
&date_preset=last_30d
&access_token={META_ADS_TOKEN}
Wrap that in a command that prints clean JSON to stdout:
node meta-ads.mjs performance --since 30d > /tmp/ads.json
Add a fatigue command that flags ads where frequency > 2.5 AND CTR has declined over the window — that's the creative-fatigue signal Claude will key off.
Connecting Claude (MCP or Pipe)
Option A — MCP server (most integrated). Wrap the CLI commands as MCP tools (ads_performance, ads_creative, ads_fatigue). Claude calls them mid-conversation, reasons, and returns recommendations in one loop. Best for recurring weekly reviews.
Option B — stdout pipe (fastest to start). Run the CLI, pipe the JSON into a Claude prompt. No MCP infra. Good for proving the workflow before investing in the server.
node meta-ads.mjs performance --since 30d | \
claude -p "$(cat prompts/weekly-ads-review.txt)"
Start with B, graduate to A once the weekly cadence is set.
Usage Prompts
Weekly creative review:
Here is the last 30 days of Meta ad performance (JSON):
{piped ads.json}
Business context: AOV ${X}, target ROAS {Y}x, monthly budget ${Z}.
Do three things:
1. Rank ads into WINNERS (scale), HOLDS (keep), CUT (kill this week)
based on ROAS vs target AND fatigue (frequency > 2.5 with
declining CTR). Be decisive — name the cuts.
2. For the top 2 winners, identify what they have in common (hook,
format, angle) — that's the pattern to make more of.
3. Draft 3 new creative test briefs for next week based on the winning
pattern. Each brief: hook, format, angle, and what it's testing
vs the current winner.
Be concrete. This goes straight into the creative pipeline.
Spend efficiency check:
From the same JSON: which ad sets are spending the most with the
worst ROAS? Give me the single reallocation that would most improve
blended ROAS this week — move $X from ad set A to ad set B, with the
reasoning. One recommendation, not five.
What to Watch For
- Read-only token. Always. The analysis pipe never needs write access. Claude recommends; a human makes the change in Ads Manager. A write-scoped token connected to an LLM is an unacceptable blast radius. (More on AI failure containment: what AI-run means when the AI screws up.)
- ROAS attribution is Meta's number, not truth. Meta over-attributes to itself. Feed Claude blended/MTA data alongside Meta's if you have it, and prompt it to treat Meta ROAS as relative (compare ads to each other), not absolute. See ecommerce analytics: the 7 KPIs.
- Frequency + declining CTR = the fatigue signal. Don't let Claude cut an ad purely on a ROAS dip — early-window noise looks like decline. The fatigue flag (frequency threshold + sustained CTR decline) is the reliable trigger.
- Token cost is trivial here. The JSON for a 30-day account is small (a few thousand tokens). The reasoning call is a few cents. This is a time-saver workflow, not a cost concern — the value is the consistent weekly decision, not compute.
- Keep a human in the creative loop. Claude drafts test briefs; a human (or our creative pipeline) produces and approves the actual creative. We covered why in what an AI marketing agency actually does.
Talk to Branva
We run weekly AI-assisted creative review + the creative pipeline that acts on it as part of transparent monthly marketing. Book a free call — we'll show you the weekly cadence on your own account.
Frequently Asked Questions
Is there an official "Meta Ads CLI"?
No single official one — "Meta Ads CLI" here means a thin wrapper you build over the official Marketing API. The API is official and documented; the CLI is your convenience layer so Claude gets clean structured data.
Why not just paste Ads Manager screenshots into Claude?
Screenshots are lossy (truncated columns, no exact numbers), not repeatable, and burn vision tokens. A JSON pipe gives Claude exact figures for every ad and makes the weekly review a one-command ritual.
Do I need MCP?
No. Start with the stdout-pipe version (run CLI, pipe JSON into a prompt). MCP makes it a seamless in-conversation loop but it's an optimization, not a requirement.
Should Claude be able to change my ad account?
No. Use a read-only (ads_read) token. The workflow's value is analysis and recommendation; the actual account changes stay with a human. Never connect a write-scoped ads token to an autonomous LLM loop.
What about Google Ads?
Same pattern — wrap the Google Ads API in a CLI, pipe to Claude, use an analogous prompt. The Meta version is documented here because it's where most Shopify brands' spend concentrates.
Related reading
- The AI Workflows pillar — every workflow we run.
- How Do I Use Claude to Analyze My Klaviyo Account? — the email-side equivalent.
- The Complete Guide to Paid Ads for Ecommerce — the strategy this analysis serves.
- Paid Growth pillar — creative testing velocity, CAC math, account structure.