Summary and recommendation
The Meta Marketing API (Graph API v20.0, base URL https://graph.facebook.com/v20.0) exposes full CRUD over Business Manager membership and asset-level ad account assignments.
Authentication is OAuth 2.0;
for server-to-server automation, System User Access Tokens are strongly preferred over User Access Tokens because they can be set to non-expiring, avoiding the ~60-day expiry that breaks unattended workflows.
The business_management and ads_management scopes require Meta App Review before use with users outside your own Business Manager - plan for this gate in any production timeline.
Integrating Meta Ads into an identity graph requires reconciling two separate user namespaces: business_user IDs (active members) and pending_user IDs (unaccepted invitations), which are distinct identifiers returned from different endpoints and must not be mixed.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 (User Access Token or System User Access Token) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Free (via Meta Work Accounts / Workplace) |
Authentication
Auth method: OAuth 2.0 (User Access Token or System User Access Token)
Setup steps
- Create a Meta App at developers.facebook.com and add the Marketing API product.
- Request the required permissions (scopes) via App Review if targeting users outside your own Business Manager.
- Generate a User Access Token via OAuth 2.0 authorization flow, or create a System User in Business Manager and generate a System User Access Token for server-to-server use.
- Pass the access token as the 'access_token' query parameter or in the Authorization header on all API requests.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| ads_management | Read and manage ad accounts, campaigns, ad sets, and ads. | Full ad account management operations |
| ads_read | Read-only access to ad account data and insights. | Reading ad account and campaign data without write access |
| business_management | Manage Business Manager assets including users, pages, and ad accounts. | Adding/removing users from Business Manager and assigning roles |
| read_insights | Read ad performance insights and reporting data. | Accessing Insights API endpoints |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string | Unique Meta user ID. | system-assigned | immutable | Returned on all user nodes. |
| name | string | Full name of the user. | read from Meta profile | not updatable via API | Pulled from the user's personal Meta account. |
| string | Email address of the user. | read from Meta profile | not updatable via API | Requires email permission; may not be returned for all users. | |
| role | enum | Role of the user within the Business Manager (ADMIN, EMPLOYEE, etc.). | required | updatable | Values: ADMIN, EMPLOYEE. Asset-level roles differ (e.g., ADVERTISER, ANALYST on ad accounts). |
| status | enum | Invitation or membership status (ACTIVE, PENDING). | system-assigned | read-only | PENDING until the invited user accepts. |
| finance_permission | enum | Finance role on the business (NONE, EDITOR, ANALYST). | optional | updatable | Controls access to billing and payment methods. |
| ip_permission | enum | IP/catalog permission level. | optional | updatable | Relevant for catalog and IP asset access. |
| tasks | array |
List of tasks the user can perform on a specific asset (e.g., ADVERTISE, ANALYZE, MANAGE). | required when assigning to asset | updatable | Used when assigning users to ad accounts or pages, not at the Business level. |
| created_time | datetime | Timestamp when the user was added to the business. | system-assigned | immutable | ISO 8601 format. |
Core endpoints
List Business Users
- Method: GET
- URL:
https://graph.facebook.com/v20.0/{business-id}/business_users - Watch out for: Requires business_management permission. Only returns users who have accepted their invitation (ACTIVE). Pending invitations appear under /pending_users.
Request example
GET /v20.0/{business-id}/business_users?fields=id,name,email,role,status&access_token={token}
Response example
{
"data": [
{"id":"123","name":"Jane Doe","role":"ADMIN","status":"ACTIVE"}
],
"paging": {"cursors":{"before":"abc","after":"xyz"}}
}
List Pending Users
- Method: GET
- URL:
https://graph.facebook.com/v20.0/{business-id}/pending_users - Watch out for: Pending users have a different node type and ID than active business_users. Do not mix IDs between the two endpoints.
Request example
GET /v20.0/{business-id}/pending_users?fields=id,email,role&access_token={token}
Response example
{
"data": [
{"id":"456","email":"new@example.com","role":"EMPLOYEE"}
]
}
Invite User to Business
- Method: POST
- URL:
https://graph.facebook.com/v20.0/{business-id}/business_users - Watch out for: Sends an email invitation; user must accept before becoming ACTIVE. The returned ID is the pending_user ID, not the final business_user ID.
Request example
POST /v20.0/{business-id}/business_users
{
"email": "user@example.com",
"role": "EMPLOYEE"
}
Response example
{
"id": "789",
"email": "user@example.com",
"status": "PENDING"
}
Update Business User Role
- Method: POST
- URL:
https://graph.facebook.com/v20.0/{business-id}/business_users - Watch out for: Role updates use POST (not PATCH) to the same endpoint. Requires the caller to be a Business Admin.
Request example
POST /v20.0/{business-id}/business_users
{
"user": "{user-id}",
"role": "ADMIN"
}
Response example
{
"success": true
}
Remove User from Business
- Method: DELETE
- URL:
https://graph.facebook.com/v20.0/{business-id}/business_users - Watch out for: Removes the user from the Business Manager entirely, including all asset access. This action is irreversible via API.
Request example
DELETE /v20.0/{business-id}/business_users?user={user-id}&access_token={token}
Response example
{
"success": true
}
Assign User to Ad Account
- Method: POST
- URL:
https://graph.facebook.com/v20.0/{business-id}/client_ad_accounts - Watch out for: Tasks array controls granular permissions on the ad account. Valid tasks: MANAGE, ADVERTISE, ANALYZE, DRAFT. User must already be a Business Member.
Request example
POST /v20.0/act_{ad-account-id}/assigned_users
{
"user": "{user-id}",
"tasks": ["ADVERTISE","ANALYZE"]
}
Response example
{
"success": true
}
List Users Assigned to Ad Account
- Method: GET
- URL:
https://graph.facebook.com/v20.0/act_{ad-account-id}/assigned_users - Watch out for: Ad account IDs must be prefixed with 'act_' (e.g., act_123456789).
Request example
GET /v20.0/act_{ad-account-id}/assigned_users?fields=id,name,tasks&access_token={token}
Response example
{
"data": [
{"id":"123","name":"Jane Doe","tasks":["ADVERTISE","ANALYZE"]}
]
}
Remove User from Ad Account
- Method: DELETE
- URL:
https://graph.facebook.com/v20.0/act_{ad-account-id}/assigned_users - Watch out for: Removes asset-level access only; does not remove the user from the Business Manager.
Request example
DELETE /v20.0/act_{ad-account-id}/assigned_users?user={user-id}&access_token={token}
Response example
{
"success": true
}
Rate limits, pagination, and events
- Rate limits: Meta Marketing API uses Business Use Case (BUC) rate limiting. Each API call consumes a score based on complexity. Limits are per app per ad account. The API returns rate limit usage in response headers.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: Rate limit usage is returned in the 'X-Business-Use-Case-Usage' response header (JSON-encoded). When throttled, the API returns error code 17 or 80000/80003. Docs do not explicitly document a Retry-After header; back-off strategy is recommended.
- Pagination method: cursor
- Default page size: 25
- Max page size: 100
- Pagination pointer: after / before (cursor), limit
| Plan | Limit | Concurrent |
|---|---|---|
| Development Access | Lower call volume; restricted to app users only | 0 |
| Standard Access | Higher BUC score allowance; granted after App Review | 0 |
- Webhooks available: No
- Webhook notes: Meta does not offer webhooks specifically for Business Manager user-management events (user added, role changed, removed). Meta Webhooks exist for other objects (Pages, Instagram, WhatsApp) but not for ad account or business user lifecycle events.
- Alternative event strategy: Poll the /business_users and /pending_users endpoints periodically to detect membership changes.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Free (via Meta Work Accounts / Workplace)
Endpoint: https://www.facebook.com/scim/v2/
Supported operations: GET /Users, GET /Users/{id}, POST /Users, PUT /Users/{id}, PATCH /Users/{id}, DELETE /Users/{id}
Limitations:
- SCIM provisioning applies to Meta Work Accounts (formerly Workplace by Meta), not directly to Meta Ads Business Manager user roles or ad account task assignments.
- SCIM does not provision ad account-level task permissions (ADVERTISE, ANALYZE, etc.); those must be managed via the Graph API.
- No major IdP (Okta, Entra, Google Workspace, OneLogin) has a pre-built Meta Ads SCIM connector listed in their official app catalogs as of the research date.
- SCIM token is generated within the Workplace/Work Accounts admin console, not via the Marketing API OAuth flow.
- Group provisioning support via SCIM is limited; check current Workplace SCIM documentation for group object support.
Common scenarios
Three automation scenarios cover the majority of lifecycle operations.
Onboarding: POST to /{business-id}/business_users to invite, poll /pending_users until acceptance, then re-fetch /business_users to obtain the final business_user ID before POSTing to /act_{ad-account-id}/assigned_users with the appropriate tasks array (MANAGE, ADVERTISE, ANALYZE, or DRAFT).
The ID change between pending and active state is a hard requirement - using the pending_user ID for asset assignment will fail.
Offboarding: DELETE to /{business-id}/business_users removes the user from the Business Manager and cascades revocation across all assigned assets in that business;
no per-asset DELETE calls are needed, but the action is irreversible via API.
Auditing: GET /business_users with fields=id,name,email,role,status for the membership list, then GET /act_{ad-account-id}/assigned_users with fields=id,name,tasks per ad account;
cross-reference to produce a full user-to-asset access matrix.
All three scenarios require cursor-based pagination (after/before params, default page size 25, max 100) - incomplete pagination is the most common cause of audit gaps in large Business Managers.
Onboard a new advertiser to a Business Manager and Ad Account
- POST /v20.0/{business-id}/business_users with email and role=EMPLOYEE to send an invitation.
- Wait for the user to accept the invitation (poll GET /v20.0/{business-id}/pending_users until the user no longer appears, then confirm via GET /v20.0/{business-id}/business_users).
- Once ACTIVE, POST to /v20.0/act_{ad-account-id}/assigned_users with the user ID and tasks=["ADVERTISE","ANALYZE"] to grant ad account access.
Watch out for: The user ID changes between the pending_user stage and the active business_user stage. Re-fetch the user list after acceptance to get the correct business_user ID before assigning to assets.
Revoke all access for a departing employee
- GET /v20.0/{business-id}/business_users to retrieve the user's business_user ID.
- DELETE /v20.0/{business-id}/business_users?user={user-id} to remove the user from the Business Manager.
- Verify removal by confirming the user no longer appears in the business_users list.
Watch out for: Removing a user from the Business Manager automatically revokes all asset-level access (ad accounts, pages, etc.) within that business. No need to individually remove from each asset, but this is irreversible via API.
Audit all users and their ad account permissions
- GET /v20.0/{business-id}/business_users?fields=id,name,email,role,status to list all active members.
- For each ad account, GET /v20.0/act_{ad-account-id}/assigned_users?fields=id,name,tasks to list per-account task assignments.
- Cross-reference the two datasets to produce a full access matrix of users vs. ad accounts and their tasks.
Watch out for: Pagination is cursor-based; iterate through all pages using the 'after' cursor before assuming you have a complete list. Large businesses with many users or ad accounts will require many paginated requests.
Why building this yourself is a trap
Several API behaviors create silent failure modes worth flagging explicitly. Rate limiting uses Business Use Case (BUC) scoring returned in the X-Business-Use-Case-Usage response header as a JSON-encoded object - not a simple integer - and must be parsed before back-off logic can act on it; error codes 17, 80000, and 80003 indicate throttling.
Meta deprecates API versions on a rolling ~2-year schedule, so hardcoding /v20.0/ in all request URLs is required to avoid silent fallback to the oldest supported version. SCIM 2.0 is available (endpoint: https://www.facebook.com/scim/v2/) but scoped to Meta Work Accounts (formerly Workplace), not to Business Manager ad account roles or task assignments;
SCIM provisioning does not populate the tasks array on ad accounts, meaning a separate Graph API integration is mandatory for any identity graph that needs to reflect actual ad account permissions.
No major IdP has a pre-built Meta Ads SCIM connector in their official catalog, and no webhook events exist for Business Manager user lifecycle changes - membership state must be tracked by polling /business_users and /pending_users on a defined schedule.
Automate Meta Ads workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.