The pitch for Meta MCP is that you point Claude, ChatGPT, or your own agent at your ad account and it can pull insights, spin up campaigns, and iterate on creative. The pitch nobody puts on the landing page: the same agent, given the same tools, can also spam Meta's Graph API into a 24-hour rate-limit block, push undisclosed AI creative that trips a policy review, or fire so many rapid-fire writes that Meta's abuse detection flags the account for manual review.
We run this in production for our own paying users — advertisers with real spend on real ad accounts. The rest of this post is the six-layer guard system that sits between an agent and graph.facebook.com. Thresholds are the ones we ship.
What Meta actually enforces
Three enforcement systems your agent has to respect — and only the first one is a hard failure most people ever see:
- Rate-limit error codes. Codes
4, 17, 32, 613, 80004, 80014and HTTP429mean you're already past a limit. Some hit at the app level, some per-account. Business-use-case subcode2446079is the ad-management-specific version. - Proactive usage headers. Every Graph response includes
X-App-Usage,X-Ad-Account-Usage, andX-Business-Use-Case-Usage. Each carries a percentage (0–100). Meta expects you to slow down at 75% and stop at 95%. Most integrations never read them. - AI-content policy. Meta requires an AI-generated-content disclosure on ad creative that is generated or meaningfully edited by AI. Undeclared AI creative pulled at scale is the fastest way to end up under manual review.
Hitting any of these once is a warning. Hitting them repeatedly on the same account is how the account gets flagged.
Layer 1 — Circuit breaker
The first line is a per-tenant circuit breaker with three phases: NORMAL, READ_ONLY, and FULL. Two rate-limit trips in a rolling hour flip the tenant into FULL lockdown for 7 days, then 8 days of READ_ONLY before returning to normal. Rate-limit trips include every code above plus BUC subcode 2446079 and HTTP 429.
While in lockdown, every write tool returns a structured refusal before it touches the Graph API. Reads are still permitted in READ_ONLY so dashboards and reporting don't go dark — but nothing can be created, updated, or duplicated.
Layer 2 — Per-user quotas
Layered on top of the circuit breaker: per-user quotas keyed by user_id. Every write tool call runs through a precheck that enforces four numbers — writes/hour, reads/hour, a burst limit, and a daily cap. Bursts are the one that matters most: an agent in a loop will happily fire 40 writes in 90 seconds unless something says no. We do.
A quota block returns a JSON error with retry_after_seconds so the agent can back off gracefully instead of hammering.
Layer 3 — Proactive usage governor
This is the layer most people miss. Meta hands you a signal on every response saying how close you are to the ceiling. Reading it is trivial. We read all three headers, key state by (kind, subject) where kind is app, account, or buc, and enforce a two-band throttle:
# usage_governor.py — thresholds we ship
SOFT_PCT = 75.0 # linear back-off starts here
HARD_PCT = 95.0 # block outbound calls
MAX_BACKOFF_SECS = 5.0
DEFAULT_REGAIN_SECS = 600
# X-App-Usage carries call_count / total_time / total_cputime;
# we track the max of the three.
# X-Ad-Account-Usage arrives in either single ({acc_id_util_pct: …})
# or multi-account ({act_1: {…}, act_2: {…}}) shape — both parsed.
# X-Business-Use-Case-Usage is keyed by business_id + type, and its
# estimated_time_to_regain_access field is honoured verbatim.Between 75% and 95% the governor sleeps a linearly-scaled amount before the outbound call fires — so a stampede of tools sharing the same account converges instead of piling up. At 95% and above the call short-circuits with an error carrying retry_after_seconds. Meta's own estimated_time_to_regain_access is honoured verbatim when present.
The important detail: we record headers on error responses too. A 400 with X-App-Usage: 100 is exactly the moment Meta is telling you "back off," and it's the one moment most code paths skip parsing headers because the response body already looks broken.
Layer 4 — AI-content declaration gate
Meta's AI-content disclosure policy applies to creative generated or meaningfully edited by AI. Pushing an image asset, video asset, or full ad creative without declaring it is the risk vector for accounts running any kind of automated creative pipeline.
Three creative-push tools carry the gate: ads_create_creative, ads_creative_upload_image, and ads_creative_upload_video. Every call is required to pass an ai_content_declaration parameter with one of: none, human_created, ai_generated, or ai_edited.
Enforcement is a single env knob so you can pick your policy:
META_AI_LABEL_ENFORCEMENT=off # accept anything, no logging
META_AI_LABEL_ENFORCEMENT=warn # default — log missing, allow
META_AI_LABEL_ENFORCEMENT=block # refuse creative push without declarationThe gate lives on both paths — the direct Graph writer and the Meta MCP proxy relay — so it can't be routed around by picking a different tool. Invalid declarations refuse regardless of mode. When Meta stabilizes a Graph field name for the declaration, we wire it once and every guarded tool stamps the value.
Layer 5 — Safety-first write mode
Every campaign, adset, and ad an agent creates ships in PAUSED status. Nothing an AI creates spends money until a human toggles it live in Ads Manager. This isn't a preference — it's forced at the parameter layer, above the tool signatures. An agent asking for status=ACTIVE getsPAUSED back. If you want an agent to unpause on its own, that's an ads_activate_entity call — separate tool, separate audit line, separate rate-limit budget.
Layer 6 — Audit trail
Every Meta write — tool name, arguments, resulting object ID, duration, error code if any — lands in a per-tenant audit log before the response returns to the caller. If an account does get flagged, the audit trail is the first thing you need. It's also the input that feeds the rate-limit trip detector, which is how the circuit breaker knows to flip.
Why all six matter, and not just the last one
A single guardrail feels sufficient right up until it fails. The circuit breaker catches the loud failure — Meta explicitly saying you're blocked. Quotas catch the runaway loop before it reaches Meta at all. The usage governor catches the slow slide toward a block, using signal Meta gives you for free. The AI label gate catches the policy failure that has nothing to do with rate. Safety-first write mode catches the "the agent thought it was helping" failure. The audit trail is the diff when something does go wrong.
Different failure surfaces. Different signals. Same account. If you're evaluating a Meta MCP integration, the question isn't does it have rate limiting. It's which of the six failure surfaces does it cover, and which of them does it leave to you.
An account restriction takes minutes to trigger and weeks to undo. The whole point of building on Meta MCP is that an agent can operate over long horizons — a system that lets an agent lose the account on day three isn't safe to leave running.
— BrandMov
Connect an agent to your Meta ads — without the risk
Brandmov MCP wraps every Meta write behind the six guardrails above so your agent can iterate without touching a live budget or tripping a policy review. Bring your own agent — Claude, ChatGPT, or a custom one.
Try Brandmov MCPNo credit card required.