Summary and recommendation
LinkedIn Ads exposes user management through the `/rest/adAccountUsers` collection under the Marketing API, authenticated via OAuth 2.0 with the `rw_ads` scope for write operations and `r_ads` for reads.
All requests must include a `LinkedIn-Version: YYYYMM` header;
omitting it risks deprecated behavior.
The API supports GET, POST, PATCH, DELETE, and a `bulkCreate` action, but has no SCIM 2.0 endpoint and no webhook support for user events - polling is the only change-detection mechanism available.
Integrating LinkedIn Ads into an identity graph requires treating it as a poll-only, URN-keyed system with no email-based lookup path in the Marketing API.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | N/A |
Authentication
Auth method: OAuth 2.0
Setup steps
- Register an application at https://www.linkedin.com/developers/apps and obtain a Client ID and Client Secret.
- Request the required OAuth 2.0 scopes (e.g., rw_ads, r_ads_reporting) during app configuration.
- Redirect the user to LinkedIn's authorization endpoint: https://www.linkedin.com/oauth/v2/authorization with response_type=code, client_id, redirect_uri, scope, and state parameters.
- Exchange the returned authorization code for an access token via POST to https://www.linkedin.com/oauth/v2/accessToken.
- Include the access token in all API requests as a Bearer token in the Authorization header.
- For server-to-server flows, use the 2-legged OAuth (client credentials) flow if your app is approved for it.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| r_ads | Read ad account data including account users and roles. | GET operations on adAccounts and adAccountUsers |
| rw_ads | Read and write ad account data including creating and updating account users. | POST, PATCH, DELETE operations on adAccountUsers |
| r_ads_reporting | Read ad performance reporting data. | Analytics and reporting endpoints (not user management) |
| r_organization_social | Read organization data; sometimes required for organization-linked ad accounts. | Fetching organization-linked account context |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| user | URN string | LinkedIn member URN identifying the user (e.g., urn:li:person:{id}). | required | immutable | Must be a valid LinkedIn member URN. |
| account | URN string | Ad account URN the user is being associated with (e.g., urn:li:sponsoredAccount:{id}). | required | immutable | Composite key with user URN. |
| role | enum string | Role assigned to the user on the ad account. Values: ACCOUNT_BILLING_ADMIN, ACCOUNT_MANAGER, CAMPAIGN_MANAGER, CREATIVE_MANAGER, VIEWER. | required | updatable | Only one role per user per account is supported. |
| changeAuditStamps.created.time | long (epoch ms) | Timestamp when the user association was created. | system-set | immutable | Read-only. |
| changeAuditStamps.lastModified.time | long (epoch ms) | Timestamp of the last modification to the user record. | system-set | system-set | Read-only. |
Core endpoints
List users on an ad account
- Method: GET
- URL:
https://api.linkedin.com/rest/adAccountUsers?q=accounts&accounts=List(urn:li:sponsoredAccount:{id}) - Watch out for: The accounts parameter must be URL-encoded. Multiple accounts can be passed as a comma-separated list inside List().
Request example
GET /rest/adAccountUsers?q=accounts&accounts=List(urn%3Ali%3AsponsoredAccount%3A123456)
Authorization: Bearer {access_token}
LinkedIn-Version: 202406
Response example
{
"elements": [
{
"user": "urn:li:person:abc123",
"account": "urn:li:sponsoredAccount:123456",
"role": "CAMPAIGN_MANAGER"
}
],
"paging": {"start":0,"count":10,"total":1}
}
Get a specific ad account user
- Method: GET
- URL:
https://api.linkedin.com/rest/adAccountUsers/(account:urn:li:sponsoredAccount:{id},user:urn:li:person:{personId}) - Watch out for: The compound key in the URL path must be fully URL-encoded.
Request example
GET /rest/adAccountUsers/(account:urn%3Ali%3AsponsoredAccount%3A123456,user:urn%3Ali%3Aperson%3Aabc123)
Authorization: Bearer {access_token}
LinkedIn-Version: 202406
Response example
{
"user": "urn:li:person:abc123",
"account": "urn:li:sponsoredAccount:123456",
"role": "CAMPAIGN_MANAGER"
}
Add a user to an ad account
- Method: POST
- URL:
https://api.linkedin.com/rest/adAccountUsers - Watch out for: The user must have an existing LinkedIn account and must have previously accepted a connection or have a valid member URN resolvable by your app. You cannot invite net-new LinkedIn members via this API.
Request example
POST /rest/adAccountUsers
Authorization: Bearer {access_token}
LinkedIn-Version: 202406
Content-Type: application/json
{"account":"urn:li:sponsoredAccount:123456","user":"urn:li:person:abc123","role":"CAMPAIGN_MANAGER"}
Response example
HTTP 201 Created
X-RestLi-Id: (account:urn%3Ali%3AsponsoredAccount%3A123456,user:urn%3Ali%3Aperson%3Aabc123)
Update a user's role on an ad account
- Method: PATCH
- URL:
https://api.linkedin.com/rest/adAccountUsers/(account:urn:li:sponsoredAccount:{id},user:urn:li:person:{personId}) - Watch out for: LinkedIn REST API PATCH uses the JSON Patch-like $set syntax, not standard JSON Merge Patch.
Request example
PATCH /rest/adAccountUsers/(account:urn%3Ali%3AsponsoredAccount%3A123456,user:urn%3Ali%3Aperson%3Aabc123)
Authorization: Bearer {access_token}
LinkedIn-Version: 202406
Content-Type: application/json
{"patch":{"$set":{"role":"ACCOUNT_MANAGER"}}}
Response example
HTTP 204 No Content
Remove a user from an ad account
- Method: DELETE
- URL:
https://api.linkedin.com/rest/adAccountUsers/(account:urn:li:sponsoredAccount:{id},user:urn:li:person:{personId}) - Watch out for: Deleting the last ACCOUNT_BILLING_ADMIN will fail. Ensure at least one billing admin remains.
Request example
DELETE /rest/adAccountUsers/(account:urn%3Ali%3AsponsoredAccount%3A123456,user:urn%3Ali%3Aperson%3Aabc123)
Authorization: Bearer {access_token}
LinkedIn-Version: 202406
Response example
HTTP 204 No Content
List ad accounts accessible to the authenticated user
- Method: GET
- URL:
https://api.linkedin.com/rest/adAccounts?q=search - Watch out for: Only accounts the authenticated user has access to are returned. Use this to discover account URNs before managing users.
Request example
GET /rest/adAccounts?q=search&search=(status:(values:List(ACTIVE)))
Authorization: Bearer {access_token}
LinkedIn-Version: 202406
Response example
{
"elements": [
{
"id": 123456,
"name": "My Ad Account",
"status": "ACTIVE",
"type": "BUSINESS"
}
]
}
Batch create ad account users
- Method: POST
- URL:
https://api.linkedin.com/rest/adAccountUsers?action=bulkCreate - Watch out for: Bulk operations return per-element status codes. Partial failures are possible; check each element's status individually.
Request example
POST /rest/adAccountUsers?action=bulkCreate
Authorization: Bearer {access_token}
LinkedIn-Version: 202406
Content-Type: application/json
{"elements":[{"account":"urn:li:sponsoredAccount:123456","user":"urn:li:person:abc123","role":"VIEWER"}]}
Response example
HTTP 200 OK
{
"results": {
"0": {"status": 201}
},
"errors": {}
}
Rate limits, pagination, and events
- Rate limits: LinkedIn Marketing APIs enforce daily and per-minute rate limits per application. Limits vary by endpoint category and are enforced at the application level, not per user.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: LinkedIn returns X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers on responses. A 429 status code is returned when limits are exceeded. The official docs do not explicitly document a Retry-After header for Marketing APIs.
- Pagination method: offset
- Default page size: 10
- Max page size: 100
- Pagination pointer: start / count
| Plan | Limit | Concurrent |
|---|---|---|
| Standard Marketing API access | 500 calls/day per app for most endpoints; some endpoints have per-minute limits (e.g., 100 calls/minute) | 0 |
- Webhooks available: No
- Webhook notes: LinkedIn Marketing APIs do not offer native webhook support for ad account user events as of the current documentation.
- Alternative event strategy: Poll the adAccountUsers endpoint periodically to detect changes in user membership or roles.
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: N/A
- Endpoint: Not documented
Limitations:
- LinkedIn Ads does not expose a SCIM 2.0 endpoint for ad account user management.
- LinkedIn's enterprise SSO/SCIM (for LinkedIn.com employee accounts) is separate from LinkedIn Ads account user management and does not apply to ad account roles.
- No IdP connectors (Okta, Entra ID, etc.) are available for LinkedIn Ads ad account user provisioning via SCIM.
Common scenarios
Three core automation scenarios are supported by the API.
First, provisioning: POST to /rest/adAccountUsers with the target account URN, the user's urn:li:person:{id} URN, and the desired role enum
but the member URN must be known in advance, as there is no Marketing API endpoint to resolve a user by email address.
Second, role updates: PATCH to the compound key (account:urn:li:sponsoredAccount:{id},user:urn:li:person:{personId}) using LinkedIn's $set patch syntax, not standard JSON Merge Patch.
Third, cross-account auditing: paginate GET /rest/adAccountUsers per account using start and count parameters (max page size 100), but note that each paginated request counts against the 500 calls/day application-level rate limit
plan pagination budgets carefully across large account portfolios.
Bulk provisioning via ?action=bulkCreate is available but returns per-element status codes;
partial failures are possible and must be checked individually.
Provision a new user as Campaign Manager on an ad account
- Obtain the target ad account URN by calling GET /rest/adAccounts?q=search and locating the account by name or ID.
- Obtain the LinkedIn member URN for the user (urn:li:person:{id}) via the Profile API or from a prior OAuth flow where the user authenticated.
- Call POST /rest/adAccountUsers with the account URN, user URN, and role: CAMPAIGN_MANAGER.
- Verify the 201 Created response and store the compound key for future updates or removal.
Watch out for: If the member URN is unknown, there is no Marketing API endpoint to look up a user by email. The user must authenticate via OAuth to expose their URN, or you must use the People Search API (requires separate approval).
Update a user's role from Viewer to Account Manager
- Construct the compound key: (account:urn:li:sponsoredAccount:{id},user:urn:li:person:{personId}).
- Call PATCH /rest/adAccountUsers/(compound-key) with body {"patch":{"$set":{"role":"ACCOUNT_MANAGER"}}}.
- Confirm 204 No Content response.
- Optionally call GET on the same compound key to verify the updated role.
Watch out for: Use the $set patch syntax specific to LinkedIn's REST framework. Standard JSON Merge Patch format will be rejected.
Audit all users across multiple ad accounts
- Call GET /rest/adAccounts?q=search to retrieve all accessible ad account URNs.
- For each account URN, call GET /rest/adAccountUsers?q=accounts&accounts=List(urn:li:sponsoredAccount:{id}) with pagination (start, count).
- Iterate through paging.total to collect all users, incrementing start by count until all records are retrieved.
- Aggregate results by account and role for reporting.
Watch out for: Each paginated request counts against your daily rate limit. For accounts with many users, plan pagination carefully to avoid exhausting the 500 calls/day limit across all operations.
Why building this yourself is a trap
The most significant integration trap is URN resolution: there is no Marketing API endpoint to look up a LinkedIn member URN by email, meaning any identity graph that stores users by email must either require users to complete an OAuth flow to expose their URN or obtain separate approval for the People Search API.
A second trap is the compound key URL-encoding requirement - urn:li:sponsoredAccount:123456 must be percent-encoded in path parameters, and malformed keys return opaque errors. OAuth access tokens expire after 60 days in 3-legged flows; refresh token handling (365-day validity) must be implemented explicitly or automations will silently fail.
Finally, Marketing API production access requires LinkedIn application review and approval; sandbox access is limited, which constrains pre-production testing of provisioning workflows.
Automate LinkedIn 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.