How to Publish Meta Ads to Multiple Ad Accounts Without Losing Your Mind

Agency operators juggle 20+ Meta ad accounts. Native Ads Manager makes that painful. Here is the workflow we built to launch the same campaign across many accounts in one click — and the rate-limit math behind it.

If you run an agency, you have probably built a spreadsheet that maps clients to Meta ad accounts. You probably also have a Google Doc with the steps your media buyers follow on launch day. By account 20, the doc is wrong, the buyer is exhausted, and the campaign rolled out at 2 PM instead of 9 AM. This is the article we wish someone had handed us when we started Whathead.

The native Ads Manager problem

Meta Ads Manager is built for a single ad account at a time. The "Duplicate" feature can copy a campaign across accounts, but only if you click through three modals per account. Multiplied by 20 clients, that is 60 manual clicks per launch — every single one a chance to mis-select a Page, lose a UTM, or apply the wrong budget.

There is no batch API in the public Marketing API for "create the same campaign in N accounts". You have to issue N separate calls, manage N different access tokens, respect each account's independent rate limit budget, and roll back gracefully when the third account fails because its Page is not linked.

The right scope for rate limits

Meta's Business Use Case (BUC) header tracks usage per ad account, not per app or per user. That is the most important fact in this article. If you naively rate-limit "10 requests per second per user", a buyer launching to 20 accounts will hit the limit on the first account and queue 19 others.

Instead, key your throttle on ad_account_id. Each account gets its own bucket — and Meta gives you a clean 9000-point BUC budget per account before it starts slowing you down. We track the X-Business-Use-Case-Usage header on every response and adapt our send rate when the score crosses 75%.

A worked example

For a typical campaign — 1 campaign + 3 ad sets + 12 ads = 16 mutations — the math looks like this:

• Per-account: 16 mutations × 50 ms minimum delay = 0.8 seconds floor

• Network round-trip on Meta's API: ~500-1500 ms each

• Total per account: ~10-15 seconds end-to-end

Across 20 accounts in parallel: still ~10-15 seconds, because each account is its own bucket. Run them serial and you wait 4 minutes for nothing.

Idempotency is non-negotiable

When you fan out to 20 accounts, something will fail. The fourth account has a banned Page; the seventh hit a budget cap; the tenth's access token expired between minute 3 and minute 7 of your launch. The wrong response is to surface a single "publish failed" toast and discard the work.

We attach an idempotency key to every publish job (sha-256 of the payload + a per-attempt nonce). If the buyer hits "Retry" after an outage, the API recognizes the key and replays the cached response for accounts that already succeeded — no double-spend, no duplicate campaigns. The 3 that genuinely failed get retried, the 17 that worked are not touched.

Permissions per account, not per buyer

A buyer who can publish to 20 accounts is a security risk if all 20 sit under one OAuth grant. We model permissions at the workspace level: each ad account is a separate connection, with its own token, scopes, and audit trail. Revoking a buyer's access removes them from all 20 accounts at once, but the tokens themselves stay tied to the workspace owner.

What this looks like inside Whathead

On the canvas, a buyer drags a campaign card and selects 20 ad accounts from a dropdown (the workspace owner already connected them). One click on Publish fans out to all 20. The results panel shows per-account status in real time: 17 succeeded, 3 failed with reasons. Failed accounts can be retried independently, and the buyer never re-enters anything.

TL;DR

• Throttle per ad_account_id, not per user.

• Track X-Business-Use-Case-Usage and slow down at 75%.

• Make every publish idempotent with a per-payload key.

• Fan out in parallel across accounts; serialize within a single account.

• Track per-account permissions separately from the buyer.