The first agent-native Meta Ads CLI with local SQLite history and compound queries the live API cannot answer.
Read-only insights CLI for Meta Marketing API focused on creative-fatigue detection, audience overlap analysis, and attribution forensics. Daily insights land in a local SQLite store so commands like fatigue, decay, learning, and reconcile can join across history. Single META_ACCESS_TOKEN env var with ads_read scope.
Learn more at Meta Ads.
Printed by @sdhilip200 (Dhilip Subramanian).
Authentication
Authenticate with a Meta access token in the META_ACCESS_TOKEN env var. Generate from Graph API Explorer (User Access Token, 1-2h lifespan) for ad-hoc use, or Business Manager System Users (no expiry) for repeat use. Grant ads_read scope only — never ads_management or any write permission. Token discovery via /me/adaccounts lists every account accessible to the token; no account ID configuration required.
Quick Start
# Verify the token and config are wired correctly before any API calls.
meta-ads-pp-cli doctor --dry-run
# List every accessible ad account (returns paged data array with id, name, currency, etc.).
meta-ads-pp-cli me --agent
# List campaigns for the test account. Run live; --data-source auto caches results in the local SQLite store for fatigue/decay/learning/reconcile.
meta-ads-pp-cli campaigns act_4327210487520472 --agent
# Surface ads with creative-fatigue signals across the last 14 days.
meta-ads-pp-cli fatigue --account act_4327210487520472 --window 14d --agent
# Group every ad by effective_status, flagging configured-vs-effective mismatches.
meta-ads-pp-cli inventory --account act_4327210487520472 --by effective_status --agent
Unique Features
These capabilities aren't available in any other tool for this API.
Local state that compounds
-
fatigue — Detect ads whose CPM is drifting up, frequency is climbing, and CTR is falling across a configurable window.
Use this to decide which ads to retire. Spending on a fatigued creative is the highest-leverage waste in any account.
meta-ads-pp-cli fatigue --campaign 23847265 --window 14d --agent
-
decay — Compare a creative's first-impression CTR against its current CTR, with projected dead-date.
Use for retire/refresh decisions on a single creative. The slope and projected dead-date are the deciding factor.
meta-ads-pp-cli decay --creative-id 120208734567 --agent
-
overlap — Pairwise overlap percentages across custom audiences, with cannibalization recommendations.
Use when ad strategy is mature and ROAS feels diluted across similar audiences. Surfaces consolidate-or-exclude decisions.
meta-ads-pp-cli overlap --audience 23847001 --audience 23847002 --audience 23847003 --agent
-
learning — Surface adsets stuck in algorithmic learning >N days with root-cause hint (budget too low, audience too narrow, events too sparse).
Use when ROAS collapses across multiple adsets simultaneously. Stuck-in-learning adsets eat budget without optimizing.
meta-ads-pp-cli learning --account act_4327210487520472 --min-days 7 --agent
Forensic queries
-
reconcile — Per-day diff between Meta-reported account spend and sum-of-insights spend; flags days where attribution drift exceeds threshold.
Use monthly for attribution audits. Surfaces specific days where Meta and insights disagree, usually delayed conversion attribution.
meta-ads-pp-cli reconcile --account act_4327210487520472 --since 30d --threshold 5 --agent
-
bottleneck — Highest-spend adsets with worst ROAS, ranked with 'why' column joining learning state and effective status.
Use weekly to decide what to pause. Highest-leverage targets surface first.
meta-ads-pp-cli bottleneck --account act_4327210487520472 --limit 10 --agent
Account hygiene
-
stale — Active ads with zero impressions in N days.
Use quarterly for account hygiene. Active ads with zero impressions are usually misconfigured or post-deletion zombies.
meta-ads-pp-cli stale --days 90 --agent
-
inventory — Group every ad in an account by effective_status, surface ads where configured status='ACTIVE' but effective_status='WITH_ISSUES' or 'DISAPPROVED'.
Use first thing every morning. DISAPPROVED-but-still-ACTIVE ads cost real spend without delivering.
meta-ads-pp-cli inventory --account act_4327210487520472 --by effective_status --agent
Recipes
Triage creative fatigue across a campaign
meta-ads-pp-cli fatigue --campaign 23847265 --window 14d --agent --select ad_id,ad_name,cpm_slope,frequency_now,ctr_slope,verdict
Returns one row per ad with CPM slope, current frequency, CTR slope, and a retire/keep verdict. Narrow output by selecting only the fatigue-relevant fields to keep agent context bounded.
Find audience cannibalization risk
meta-ads-pp-cli overlap --audience 23847001 --audience 23847002 --audience 23847003 --agent
Returns pairwise overlap percentages and a recommendation per pair. Anything above 30% is a candidate for consolidation or exclusion.
Spot stuck-in-learning adsets
meta-ads-pp-cli learning --account act_4327210487520472 --min-days 7 --agent
Surfaces adsets where learning_stage_info has been LEARNING for >7 days. The 'why' column hints at budget/audience/event sparsity.
Daily inventory roll-up first thing
meta-ads-pp-cli inventory --account act_4327210487520472 --by effective_status --agent
Groups every ad by effective_status. The DISAPPROVED-but-configured-ACTIVE class is the highest-leverage daily finding.
Monthly spend reconciliation
meta-ads-pp-cli reconcile --account act_4327210487520472 --since 30d --threshold 5 --agent
Per-day diff between Meta-reported account spend and sum-of-insights spend over the last 30 days. Flags days where drift exceeds 5%.
Usage
Run meta-ads-pp-cli --help for the full command reference and flag list.
Commands
adcreatives
Manage adcreatives
meta-ads-pp-cli adcreatives <adId> - Get creatives attached to an ad
ads
Ad-level read operations
meta-ads-pp-cli ads <adAccountId> - List ads in an ad account
adsets
Ad set-level read operations
meta-ads-pp-cli adsets <adAccountId> - Returns ad sets with targeting summary, budget, optimization goal, and
bid strategy. Includes learning_phase metadata when adsets are still
exiting the algorithm's learning window.
campaigns
Campaign-level read operations
meta-ads-pp-cli campaigns <adAccountId> - Returns campaigns with status, objective, and budget metadata. Use
effective_status (not status) for the live delivery state - status is
what you set; effective_status is what Meta is actually doing.
customaudiences
Manage customaudiences
meta-ads-pp-cli customaudiences <adAccountId> - List custom audiences in an ad account
delivery-estimate
Manage delivery estimate
meta-ads-pp-cli delivery-estimate <adAccountId> - Get reach/impressions delivery estimate
insights
Performance and delivery insights (the creative-fatigue observatory)
meta-ads-pp-cli insights get-account - Returns aggregated insights across the entire ad account. Requires
either time_range or date_preset - there is no default time window.
Breakdowns are mutually exclusive in groups (age+gender works,
age+placement does not).
meta-ads-pp-cli insights get-ad - Ad-level insights with frequency, CPM, CTR over time. With
time_increment=1 and a date_preset like last_30d, this returns the
daily series that powers creative-fatigue detection.
meta-ads-pp-cli insights get-ad-set - Get ad set-level insights
meta-ads-pp-cli insights get-campaign - Get campaign-level insights
me
Manage me
meta-ads-pp-cli me - Returns every ad account the access token has been granted access to.
Use this on first run to discover which accounts you can query.
Output Formats
# Human-readable table (default in terminal, JSON when piped)
meta-ads-pp-cli ads mock-value
# JSON for scripting and agents
meta-ads-pp-cli ads mock-value --json
# Filter to specific fields
meta-ads-pp-cli ads mock-value --json --select id,name,status
# Dry run — show the request without sending
meta-ads-pp-cli ads mock-value --dry-run
# Agent mode — JSON + compact + no prompts in one flag
meta-ads-pp-cli ads mock-value --agent
Agent Usage
This CLI is designed for AI agent consumption:
- Non-interactive - never prompts, every input is a flag
- Pipeable -
--json output to stdout, errors to stderr
- Filterable -
--select id,name returns only fields you need
- Previewable -
--dry-run shows the request without sending
- Read-only by default - this CLI does not create, update, delete, publish, send, or mutate remote resources
- Offline-friendly - sync/search commands can use the local SQLite store when available
- Agent-safe by default - no colors or formatting unless
--human-friendly is set
Exit codes: 0 success, 2 usage error, 3 not found, 4 auth error, 5 API error, 7 rate limited, 10 config error.
Health Check
meta-ads-pp-cli doctor
Verifies configuration, credentials, and connectivity to the API.
Configuration
Config file: ~/.config/meta-marketing-pp-cli/config.toml
Static request headers can be configured under headers; per-command header overrides take precedence.
Environment variables:
| Name | Kind | Required | Description |
|---|
META_ACCESS_TOKEN | per_call | Yes | Set to your API credential. |
agentcookie (optional)
If you use agentcookie to sync secrets across machines, this CLI auto-adopts agentcookie-managed credentials with no extra setup. When the daemon writes to this CLI's config, meta-ads-pp-cli doctor reports agentcookie: detected and auth-status labels the source as agentcookie. Skip this section if you don't use agentcookie - the CLI works the same as any other.
Troubleshooting
Authentication errors (exit code 4)
- Run
meta-ads-pp-cli doctor to check credentials
- Verify the environment variable is set:
echo $META_ACCESS_TOKEN
Not found errors (exit code 3)
- Check the resource ID is correct
- Run the
list command to see available items
API-specific
- Empty ad_accounts list with a valid token — Business Manager assignment missing. Open Business Settings → Ad Accounts → Add People and assign your user (or System User) with View permission.
- OAuth error code 190 'session has expired' — Token expired. Regenerate a User Access Token in Graph API Explorer, or generate a System User token in Business Manager for long-lived use.
- OAuth error code 200 'API access deactivated' — The Meta App backing this token is deactivated. Reactivate at developers.facebook.com/apps/, or generate a token from a different active app.
- Empty insights despite valid token and active ads — Insights require time_range or date_preset; there is no default window. Pass --date-preset last_7d or --time-range {"since":"2026-01-01","until":"2026-01-31"}.
- Breakdown returns an error — Some breakdown combinations are mutually exclusive. age+gender works; age+placement does not. Use one breakdown group at a time, or split into multiple calls.
Sources & Inspiration
This CLI was built by studying these projects and resources:
Generated by CLI Printing Press