Summary and recommendation
Netlify's REST API (base: https://api.netlify.com/api/v1) covers the full member lifecycle: list, get, invite, update role, and delete, all under the /accounts/{account_id}/members resource. Authentication is via Personal Access Token passed as a Bearer token; PATs are full-scope with no granular permission selection available.
The account_id path parameter accepts either the numeric team ID or the team slug - prefer the numeric ID for reliability, as slug behavior is inconsistent across endpoints. Pagination is offset-based using page and per_page query parameters (max 100 per page, 1-indexed).
Rate limits exist but are not publicly documented; implement exponential backoff on HTTP 429 and check response headers for rate limit state.
API quick reference
| Has user API | Yes |
| Auth method | Personal Access Token (Bearer token); OAuth 2.0 also supported for third-party integrations |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: Personal Access Token (Bearer token); OAuth 2.0 also supported for third-party integrations
Setup steps
- Log in to Netlify and navigate to User Settings > Applications.
- Under 'Personal access tokens', click 'New access token', provide a description, and generate.
- Copy the token immediately (shown only once).
- Pass the token in the Authorization header: 'Authorization: Bearer
'. - For OAuth 2.0 app integrations, register an OAuth application in Netlify's developer portal and follow the authorization code flow.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| N/A – Personal Access Tokens are full-access | Netlify PATs grant full API access scoped to the authenticated user's permissions; no granular scope selection is available for PATs. | All API operations |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | string | Unique identifier for the team member | system-generated | immutable | Used in member-specific API paths |
| string | Email address of the team member | required (for invite) | not updatable via API | Used as the invite target | |
| role | string | Team role: Owner, Collaborator, or Billing Admin | optional (defaults to Collaborator) | updatable | Role names are case-sensitive |
| avatar | string (URL) | URL to the member's avatar image | system-set from Netlify profile | not updatable via API | |
| connected_accounts | object | Linked third-party accounts (e.g., GitHub, GitLab) | not applicable | not applicable | Read-only; set by the member |
| slug | string | URL-safe identifier for the team/account | system-generated | immutable | Used in team-scoped API paths |
| full_name | string | Display name of the member | set by member on their profile | not updatable by team admin via API |
Core endpoints
List team members
- Method: GET
- URL:
https://api.netlify.com/api/v1/accounts/{account_id}/members - Watch out for: account_id can be the team slug or numeric ID. Pagination uses ?page=1&per_page=100.
Request example
GET /api/v1/accounts/my-team/members
Authorization: Bearer <token>
Response example
[
{
"id": "abc123",
"email": "user@example.com",
"role": "Collaborator",
"full_name": "Jane Doe"
}
]
Get team member
- Method: GET
- URL:
https://api.netlify.com/api/v1/accounts/{account_id}/members/{member_id} - Watch out for: member_id is the Netlify user ID, not the email address.
Request example
GET /api/v1/accounts/my-team/members/abc123
Authorization: Bearer <token>
Response example
{
"id": "abc123",
"email": "user@example.com",
"role": "Collaborator",
"full_name": "Jane Doe"
}
Invite team member
- Method: POST
- URL:
https://api.netlify.com/api/v1/accounts/{account_id}/members - Watch out for: Sends an email invitation; the user must accept before they appear as an active member.
Request example
POST /api/v1/accounts/my-team/members
Authorization: Bearer <token>
Content-Type: application/json
{"email": "newuser@example.com", "role": "Collaborator"}
Response example
{
"id": "def456",
"email": "newuser@example.com",
"role": "Collaborator"
}
Update team member role
- Method: PUT
- URL:
https://api.netlify.com/api/v1/accounts/{account_id}/members/{member_id} - Watch out for: Only role can be updated. Changing to Owner requires the caller to already be an Owner.
Request example
PUT /api/v1/accounts/my-team/members/abc123
Authorization: Bearer <token>
Content-Type: application/json
{"role": "Owner"}
Response example
{
"id": "abc123",
"email": "user@example.com",
"role": "Owner"
}
Remove team member
- Method: DELETE
- URL:
https://api.netlify.com/api/v1/accounts/{account_id}/members/{member_id} - Watch out for: Cannot remove the last Owner of a team. Removing a member does not delete their Netlify account.
Request example
DELETE /api/v1/accounts/my-team/members/abc123
Authorization: Bearer <token>
Response example
HTTP 204 No Content
List accounts (teams) for current user
- Method: GET
- URL:
https://api.netlify.com/api/v1/accounts - Watch out for: Returns all accounts the authenticated token's user belongs to, including personal accounts.
Request example
GET /api/v1/accounts
Authorization: Bearer <token>
Response example
[
{
"id": "team-abc",
"slug": "my-team",
"name": "My Team",
"type": "team"
}
]
Get account details
- Method: GET
- URL:
https://api.netlify.com/api/v1/accounts/{account_id} - Watch out for: Plan name in response may not match marketing names exactly.
Request example
GET /api/v1/accounts/my-team
Authorization: Bearer <token>
Response example
{
"id": "team-abc",
"slug": "my-team",
"name": "My Team",
"plan": "pro"
}
List audit log events
- Method: GET
- URL:
https://api.netlify.com/api/v1/accounts/{account_id}/audit - Watch out for: Audit log availability may depend on plan tier. Not all member events are guaranteed to appear.
Request example
GET /api/v1/accounts/my-team/audit
Authorization: Bearer <token>
Response example
[
{
"id": "evt_001",
"action": "member_added",
"actor_email": "admin@example.com",
"timestamp": "2025-01-15T10:00:00Z"
}
]
Rate limits, pagination, and events
- Rate limits: Netlify enforces API rate limits but does not publish exact per-plan numeric limits in official documentation.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: HTTP 429 is returned when limits are exceeded. Netlify recommends exponential backoff. Specific header names for rate limit state are not documented publicly.
- Pagination method: offset
- Default page size: 100
- Max page size: 100
- Pagination pointer: page and per_page query parameters (1-indexed pages)
| Plan | Limit | Concurrent |
|---|---|---|
| Free | Not publicly documented | 0 |
| Pro | Not publicly documented | 0 |
| Enterprise | Not publicly documented; higher limits negotiated | 0 |
- Webhooks available: Yes
- Webhook notes: Netlify supports outbound webhooks (called 'Outgoing Notifications') scoped to site-level events such as deploy lifecycle events. There are no native webhooks for team membership changes (member added, removed, role changed).
- Alternative event strategy: Poll the /accounts/{account_id}/members endpoint or use SCIM provisioning events via your IdP (Okta, Entra ID) for membership change notifications.
- Webhook events: deploy_building, deploy_created, deploy_failed, deploy_locked, deploy_unlocked, form_submission, split_test_activated, split_test_deactivated
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: Provided by WorkOS; the SCIM base URL is supplied in the Netlify Enterprise SSO configuration UI after enabling Directory Sync
Supported operations: Create user (provision), Update user attributes, Deactivate/deprovision user, List users, Get user by ID, Group push (team membership sync)
Limitations:
- SCIM is only available on the Enterprise plan
- SAML SSO must be configured before enabling SCIM Directory Sync
- Netlify uses WorkOS as the underlying SCIM/SSO infrastructure; the SCIM endpoint URL is WorkOS-hosted
- Supported IdPs: Okta and Microsoft Entra ID (Azure AD); Google Workspace and OneLogin are not officially supported
- Group-to-team-role mapping may require manual configuration in the IdP
- Deprovisioned users lose access but their Netlify account is not deleted
Common scenarios
Three scenarios cover the majority of programmatic identity management needs on Netlify.
For onboarding, POST to /api/v1/accounts/{account_id}/members with the invitee's email and role; the member is not active or billable until they accept the invitation email, so do not treat a 201 response as confirmation of active membership.
For non-SCIM offboarding, retrieve the member's numeric ID by filtering GET /api/v1/accounts/{account_id}/members by email, then DELETE /api/v1/accounts/{account_id}/members/{member_id} - this removes team access immediately but does not invalidate the user's PATs for other teams or delete their Netlify account.
team admins cannot revoke another user's personal tokens via API. For Enterprise SCIM automation, the provisioning endpoint is WorkOS-hosted (not api.
netlify. com) and is surfaced only after enabling Directory Sync in the Netlify UI; supported IdPs are Okta and Microsoft Entra ID - Google Workspace and OneLogin are not officially supported.
Building an identity graph across Netlify team membership requires correlating the user object's id, email, role, and connected_accounts fields.
the connected_accounts object exposes linked Git provider identities (GitHub, GitLab), which is the primary cross-system join key for reconciling Netlify members against a broader identity graph.
Onboard a new team member via API
- Authenticate with a PAT belonging to a team Owner.
- POST to /api/v1/accounts/{account_id}/members with {"email": "newuser@example.com", "role": "Collaborator"}.
- Netlify sends an invitation email to the specified address.
- User accepts the invitation; they then appear in GET /api/v1/accounts/{account_id}/members as an active member.
- Optionally verify membership with GET /api/v1/accounts/{account_id}/members/{member_id}.
Watch out for: The member is not active (and not billed) until they accept the invitation. Polling the member list immediately after invite will not show them as active.
Deprovision a departing employee (non-SCIM)
- Retrieve the member's ID via GET /api/v1/accounts/{account_id}/members, filtering by email.
- DELETE /api/v1/accounts/{account_id}/members/{member_id} to remove them from the team.
- Verify removal with a subsequent GET to the members list.
- Advise the user to revoke their own PATs from their Netlify user settings, as team admins cannot revoke another user's PATs via API.
Watch out for: Deleting a member removes team access but does not delete their Netlify account or invalidate their personal tokens for other teams they belong to.
Automate user lifecycle with SCIM (Enterprise)
- Ensure the team is on the Enterprise plan with SAML SSO configured via WorkOS.
- In the Netlify team settings, enable Directory Sync and copy the SCIM base URL and bearer token provided.
- Configure your IdP (Okta or Entra ID) with the SCIM base URL and token.
- Assign users and groups in the IdP; provisioning events will create/update/deactivate Netlify team members automatically.
- Test by assigning a user in the IdP and confirming they appear in Netlify team members.
Watch out for: The SCIM endpoint is WorkOS-hosted, not api.netlify.com. Do not attempt to call it directly outside of IdP SCIM client configuration. SSO must be active before SCIM will function.
Why building this yourself is a trap
Several API behaviors create silent failure modes worth flagging explicitly. Inviting via API triggers an email flow - polling the members list immediately after a POST will not reflect the pending invite, which can cause duplicate invite logic in automation scripts that check for existence before inviting.
The audit log endpoint (GET /api/v1/accounts/{account_id}/audit) is plan-gated and does not guarantee coverage of all membership events, making it unreliable as a sole source of truth for access change history.
There are no webhooks for membership events (add, remove, role change) - the only supported outbound notifications are site-level deploy lifecycle events, so any membership-aware automation must poll. SCIM and SSO configuration has no public API surface; it must be completed through the Netlify UI, meaning it cannot be scripted into infrastructure-as-code pipelines.
Finally, removing a member via DELETE does not revoke their deploy keys or PATs - those remain valid for any other context the user belongs to and must be managed out-of-band.
Automate Netlify 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.