Stitchflow
GitHub logo

GitHub User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 5, 2026

Summary and recommendation

GitHub exposes user and org management via REST (base URL: https://api.github.com) and a SCIM 2.0 API restricted to Enterprise Managed Users (EMU) on Enterprise Cloud. Auth options are OAuth 2.0, classic or fine-grained PATs, and GitHub App installation tokens.

Key scope requirements: read:org for read-only membership queries, admin:org for write operations on org membership, and admin:enterprise for all SCIM endpoints.

Rate limits are tiered: authenticated OAuth/PAT tokens receive 5,000 requests/hour; GitHub Enterprise Cloud users receive 15,000/hour; SCIM endpoints are capped at approximately 100 requests/minute per enterprise with no documented hard ceiling published by GitHub. All limits are surfaced via X-RateLimit-* response headers; HTTP 429 with a Retry-After header signals primary limit exhaustion.

Secondary abuse-detection limits return HTTP 403 with a separate Retry-After header and are triggered independently of primary limits.

Pagination uses per_page (max 100) and page parameters for most endpoints, with Link headers (rel="next", rel="last") for cursor-style traversal. The SCIM filter parameter uses SCIM 2.0 syntax (e.g., userName eq "user@example.com"), not standard query string syntax.

API quick reference

Has user APIYes
Auth methodOAuth 2.0 (token-based); Personal Access Token (PAT classic or fine-grained); GitHub App installation token
Base URLOfficial docs
SCIM availableYes
SCIM plan requiredGitHub Enterprise Cloud with Enterprise Managed Users (EMU) enabled

Authentication

Auth method: OAuth 2.0 (token-based); Personal Access Token (PAT classic or fine-grained); GitHub App installation token

Setup steps

  1. For OAuth 2.0: Register an OAuth App at github.com/settings/developers, obtain client_id and client_secret, redirect user to https://github.com/login/oauth/authorize with required scopes, exchange code for access_token via POST https://github.com/login/oauth/access_token.
  2. For Personal Access Token (PAT): Navigate to github.com/settings/tokens, generate a classic or fine-grained PAT with required scopes, pass as Authorization: Bearer or Authorization: token header.
  3. For GitHub App: Create a GitHub App at github.com/settings/apps, install it on the target org/repo, generate a JWT signed with the app's private key, exchange JWT for an installation access token via POST https://api.github.com/app/installations/{installation_id}/access_tokens.
  4. For SCIM (EMU): Use a personal access token scoped to the enterprise with admin:enterprise scope, or a provisioning token issued by the IdP (Okta, Entra ID). Pass as Authorization: Bearer .

Required scopes

Scope Description Required for
read:user Read-only access to a user's profile data. GET /user, GET /users/{username}
user Full read/write access to profile information. PATCH /user (update authenticated user profile)
user:email Read access to a user's email addresses. GET /user/emails
admin:org Full control of orgs and teams, read and write org projects. PUT /orgs/{org}/memberships/{username}, DELETE /orgs/{org}/members/{username}
read:org Read-only access to org membership, org projects, and team membership. GET /orgs/{org}/members, GET /orgs/{org}/memberships/{username}
admin:enterprise Full control of enterprises; required for SCIM provisioning on EMU. SCIM /scim/v2/enterprises/{enterprise}/Users endpoints

User object / data model

Field Type Description On create On update Notes
login string The user's GitHub username. Set by user during registration; not settable via API. Not updatable via REST API. Unique across GitHub. Used as path parameter in most endpoints.
id integer Unique numeric identifier for the user. Auto-assigned. Immutable. Stable identifier; login can change but id does not.
node_id string Global node ID for use with GraphQL API. Auto-assigned. Immutable. Base64-encoded GraphQL node identifier.
name string User's display name. Optional. PATCH /user { "name": "..." } Can be null.
email string Publicly visible email address. Optional. PATCH /user { "email": "..." } May be null if user has not set a public email. Use /user/emails for full list.
company string User's company name. Optional. PATCH /user { "company": "..." } Can be null.
blog string URL of user's blog or website. Optional. PATCH /user { "blog": "..." } Can be null.
location string User's geographic location. Optional. PATCH /user { "location": "..." } Can be null.
bio string Short biography. Optional. PATCH /user { "bio": "..." } Max 160 characters.
hireable boolean Whether the user is open to job opportunities. Optional. PATCH /user { "hireable": true } Can be null.
twitter_username string User's Twitter/X handle. Optional. PATCH /user { "twitter_username": "..." } Can be null.
avatar_url string URL of the user's avatar image. Auto-assigned (Gravatar or uploaded). Not directly updatable via REST. Read-only via API.
type string Account type: 'User' or 'Organization' or 'Bot'. Auto-assigned. Immutable. Used to distinguish user vs org accounts.
site_admin boolean Whether the user is a GitHub site administrator (GitHub Enterprise Server only). N/A. Managed via GHES admin API. Always false on GitHub.com for non-staff.
suspended_at string (ISO 8601) Timestamp when the user was suspended (EMU/GHES). N/A. Set via SCIM or GHES admin API. Null if not suspended. EMU-specific for GHEC.
created_at string (ISO 8601) Timestamp of account creation. Auto-assigned. Immutable. Read-only.
updated_at string (ISO 8601) Timestamp of last profile update. Auto-assigned. Auto-updated. Read-only.
role string Org membership role: 'admin' or 'member'. Set when inviting to org. PUT /orgs/{org}/memberships/{username} { "role": "admin" } Org-scoped field, not on the base user object.
two_factor_authentication boolean Whether the user has 2FA enabled. N/A. Not settable via API. Only visible to org admins via GET /orgs/{org}/members?filter=2fa_disabled.
public_repos integer Number of public repositories owned by the user. Auto-assigned. Read-only. Included in full user object response.

Core endpoints

Get authenticated user

  • Method: GET
  • URL: https://api.github.com/user
  • Watch out for: Returns only the authenticated user's own profile. Requires at least read:user scope. Email may be null if not public; use GET /user/emails for all addresses.

Request example

GET /user
Authorization: Bearer <token>
Accept: application/vnd.github+json

Response example

{
  "login": "octocat",
  "id": 1,
  "name": "The Octocat",
  "email": "octocat@github.com",
  "type": "User",
  "created_at": "2011-01-25T18:44:36Z"
}

Get a user by username

  • Method: GET
  • URL: https://api.github.com/users/{username}
  • Watch out for: Returns a reduced public profile. Private fields (email, 2FA status) are not exposed unless the requester is an org admin and the user is an org member.

Request example

GET /users/octocat
Authorization: Bearer <token>
Accept: application/vnd.github+json

Response example

{
  "login": "octocat",
  "id": 1,
  "name": "The Octocat",
  "public_repos": 8,
  "type": "User"
}

Update authenticated user

  • Method: PATCH
  • URL: https://api.github.com/user
  • Watch out for: Only the authenticated user can update their own profile. Requires user scope. Cannot change login or id.

Request example

PATCH /user
Authorization: Bearer <token>
Content-Type: application/json

{"name":"New Name","bio":"Updated bio"}

Response example

{
  "login": "octocat",
  "name": "New Name",
  "bio": "Updated bio",
  "updated_at": "2024-01-15T10:00:00Z"
}

List organization members

  • Method: GET
  • URL: https://api.github.com/orgs/{org}/members
  • Watch out for: Returns only public members unless the requester is an org member. Use filter=2fa_disabled to find members without 2FA (requires admin:org scope). Paginate via Link header.

Request example

GET /orgs/my-org/members?per_page=100&page=1
Authorization: Bearer <token>
Accept: application/vnd.github+json

Response example

[
  {"login":"user1","id":101,"type":"User"},
  {"login":"user2","id":102,"type":"User"}
]

Add or update org membership

  • Method: PUT
  • URL: https://api.github.com/orgs/{org}/memberships/{username}
  • Watch out for: Creates a pending invitation if the user is not already a member. The user must accept the invitation. Requires admin:org scope. Role can be 'member' or 'admin'.

Request example

PUT /orgs/my-org/memberships/newuser
Authorization: Bearer <token>
Content-Type: application/json

{"role":"member"}

Response example

{
  "url": "https://api.github.com/orgs/my-org/memberships/newuser",
  "state": "pending",
  "role": "member",
  "user": {"login":"newuser"}
}

Remove org member

  • Method: DELETE
  • URL: https://api.github.com/orgs/{org}/members/{username}
  • Watch out for: Immediately removes the user from the org and all its teams. Requires admin:org scope. Does not delete the GitHub account itself.

Request example

DELETE /orgs/my-org/members/olduser
Authorization: Bearer <token>
Accept: application/vnd.github+json

Response example

HTTP 204 No Content

List org memberships for authenticated user

  • Method: GET
  • URL: https://api.github.com/user/memberships/orgs
  • Watch out for: Requires read:org scope. Filter by state=active|pending. Only returns orgs the authenticated user belongs to.

Request example

GET /user/memberships/orgs?state=active
Authorization: Bearer <token>
Accept: application/vnd.github+json

Response example

[
  {
    "state": "active",
    "role": "admin",
    "organization": {"login":"my-org"}
  }
]

SCIM: List provisioned users (EMU)

  • Method: GET
  • URL: https://api.github.com/scim/v2/enterprises/{enterprise}/Users
  • Watch out for: Only available for Enterprise Managed Users (EMU) on GitHub Enterprise Cloud. Requires admin:enterprise scoped PAT or IdP provisioning token. Standard SCIM filter syntax supported (e.g., filter=userName eq "user@example.com").

Request example

GET /scim/v2/enterprises/my-enterprise/Users?startIndex=1&count=100
Authorization: Bearer <provisioning-token>
Accept: application/scim+json

Response example

{
  "schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults":2,
  "Resources":[
    {"id":"abc123","userName":"user@example.com","active":true}
  ]
}

Rate limits, pagination, and events

  • Rate limits: GitHub REST API enforces per-user and per-app rate limits. Authenticated requests are tracked per token. SCIM endpoints have separate, lower limits.
  • Rate-limit headers: Yes
  • Retry-After header: Yes
  • Rate-limit notes: Headers returned: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix timestamp), X-RateLimit-Used, X-RateLimit-Resource. When limit is exceeded, HTTP 429 is returned with Retry-After header. Secondary rate limits (abuse detection) may return 403 with a Retry-After header. Search API has a separate 30 req/min limit for authenticated users.
  • Pagination method: cursor
  • Default page size: 30
  • Max page size: 100
  • Pagination pointer: per_page (size), page (offset-based for most endpoints); Link header with rel="next"/"prev"/"last" for cursor-style traversal
Plan Limit Concurrent
Unauthenticated 60 requests/hour per originating IP 0
Authenticated (OAuth / PAT) 5,000 requests/hour per authenticated user 0
GitHub App installation token 5,000 requests/hour; scales to 15,000/hour for orgs with ≥20 users (max 12,500 for the app) 0
GitHub Enterprise Cloud (GHEC) 15,000 requests/hour for authenticated users in an enterprise 0
SCIM API Approximately 100 requests/minute per enterprise (undocumented hard cap; GitHub recommends throttling provisioning calls) 0
  • Webhooks available: Yes
  • Webhook notes: GitHub supports organization and enterprise webhooks that fire on user-related events such as membership changes, org invitations, and team membership updates. Webhooks are configured at the org or enterprise level and deliver HTTP POST payloads to a specified URL.
  • Alternative event strategy: GitHub REST API polling via GET /orgs/{org}/members or GET /orgs/{org}/audit-log for audit events. GraphQL subscriptions are not available for user-management events.
  • Webhook events: membership (added/removed from team), organization (member_added, member_removed, member_invited), org_block (blocked/unblocked), team (added_to_repository, removed_from_repository, created, deleted), enterprise (member_added, member_removed) - enterprise webhooks only

SCIM API status

  • SCIM available: Yes

  • SCIM version: 2.0

  • Plan required: GitHub Enterprise Cloud with Enterprise Managed Users (EMU) enabled

  • Endpoint: https://api.github.com/scim/v2/enterprises/{enterprise}/Users

  • Supported operations: GET /scim/v2/enterprises/{enterprise}/Users (list users), GET /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} (get user), POST /scim/v2/enterprises/{enterprise}/Users (provision user), PUT /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} (replace user), PATCH /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} (update user / suspend), DELETE /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} (deprovision user), GET /scim/v2/enterprises/{enterprise}/Groups (list groups/teams), POST /scim/v2/enterprises/{enterprise}/Groups (create group), PATCH /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} (update group membership), DELETE /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} (delete group)

Limitations:

  • Only available for Enterprise Managed Users (EMU); not available for standard GitHub orgs or GitHub Enterprise Server SCIM via this endpoint.
  • SCIM provisioning requires SAML SSO to be configured and enforced via an IdP (Okta or Microsoft Entra ID officially supported).
  • Deprovisioning via DELETE suspends the EMU account; the account cannot be reactivated by the user.
  • SCIM userName must match the SAML NameID attribute from the IdP.
  • Rate limits on SCIM endpoints are lower than standard REST API; GitHub recommends spacing out bulk provisioning calls.
  • SCIM Groups map to GitHub Teams within the enterprise; group display names must be unique.
  • Google Workspace and OneLogin are not officially supported IdPs for EMU SCIM.
  • The enterprise slug (not org name) is required in the SCIM endpoint path.

Common scenarios

Provisioning a new EMU user requires a POST to /scim/v2/enterprises/{enterprise}/Users with a SCIM 2. 0 payload.

The userName field must exactly match the SAML NameID the IdP will assert during SSO; a mismatch causes authentication failures at login. EMU usernames are automatically suffixed with the enterprise slug.

For standard org membership, PUT /orgs/{org}/memberships/{username} creates a pending invitation - the user must accept before they appear as an active member. This async acceptance step breaks any provisioning flow that assumes immediate membership and must be handled with a polling or webhook-based confirmation pattern.

Auditing members missing 2FA is available via GET /orgs/{org}/members?filter=2fa_disabled, but only for org admins using admin:org scope. This filter does not exist at the enterprise level; enterprise-wide 2FA reporting requires the audit log API instead.

For identity graph construction, the user object exposes login (GitHub username), id (stable numeric identifier), node_id (GraphQL global ID), email (public only; use GET /user/emails for all addresses), suspended_at (EMU/GHES suspension timestamp), and type (User, Organization, or Bot). Private fields such as 2FA status are not exposed in the public user object even with elevated scopes.

Provision a new EMU user via SCIM

  1. Ensure GitHub Enterprise Cloud with EMU is configured and SAML SSO is enforced via Okta or Entra ID.
  2. Generate a PAT with admin:enterprise scope (or use the IdP's provisioning token).
  3. POST https://api.github.com/scim/v2/enterprises/{enterprise}/Users with body: {"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],"userName":"user@example.com","name":{"givenName":"Jane","familyName":"Doe"},"emails":[{"value":"user@example.com","primary":true}],"active":true}
  4. GitHub creates the EMU account; the user receives an email to set their password.
  5. Assign the user to SCIM Groups (GitHub Teams) via PATCH /scim/v2/enterprises/{enterprise}/Groups/{group_id} to grant org/repo access.

Watch out for: The userName must exactly match the SAML NameID the IdP will send during SSO. Mismatch causes authentication failures. EMU usernames are suffixed with the enterprise slug (e.g., jane_doe_myenterprise).

Remove a user from an organization

  1. Authenticate with a PAT or OAuth token with admin:org scope.
  2. Verify current membership: GET https://api.github.com/orgs/{org}/memberships/{username}
  3. Remove the member: DELETE https://api.github.com/orgs/{org}/members/{username}
  4. Confirm removal: GET /orgs/{org}/members and verify the user is absent.
  5. For EMU: also PATCH the SCIM user to set active=false to suspend the enterprise account.

Watch out for: DELETE /orgs/{org}/members removes the user from all teams in the org immediately but does not delete their GitHub account or revoke access to repos they have direct collaborator access to outside the org.

Audit org members missing 2FA

  1. Authenticate with a PAT with admin:org scope.
  2. GET https://api.github.com/orgs/{org}/members?filter=2fa_disabled&per_page=100
  3. Paginate through all pages using the Link: <...>; rel="next" header until rel="next" is absent.
  4. Collect the list of login values for users without 2FA.
  5. Optionally use PUT /orgs/{org}/memberships/{username} to demote or DELETE /orgs/{org}/members/{username} to remove non-compliant users.

Watch out for: The filter=2fa_disabled parameter only works for org admins. It is not available to regular members. This endpoint does not exist for enterprise-level queries; use the audit log API for enterprise-wide 2FA reporting.

Why building this yourself is a trap

The most significant API trap is the invitation-acceptance gap: PUT /orgs/{org}/memberships/{username} does not provision access - it sends an invitation. Any pipeline that treats a 200 response from this endpoint as confirmation of active membership will produce false positives.

Pair this call with webhook listeners on the organization event (member_added) or poll GET /orgs/{org}/memberships/{username} for state=active.

Fine-grained PATs use different scope syntax than classic PATs and do not yet support all org-level endpoints. Mixing token types across an integration without explicit endpoint compatibility checks will produce unexpected 403 responses.

SCIM is only available for EMU; calling SCIM endpoints against a standard Enterprise Cloud org returns errors. The enterprise slug (not the org name) is required in every SCIM endpoint path - using the org name is a silent misconfiguration that produces 404s.

Additionally, DELETE on a SCIM user suspends the EMU account but does not delete it; the account cannot be reactivated outside the IdP, making deprovisioning effectively irreversible from the GitHub side.

Automate GitHub 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.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 5, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

15Five logo

15Five

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

15Five uses a fixed role-based permission model with six predefined roles: Account Admin, HR Admin, Billing Admin, Group Admin, Manager, and Employee. No custom roles can be constructed. User management lives at Settings gear → People → Manage people p

1Password logo

1Password

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

1Password's admin console at my.1password.com covers the full user lifecycle — invitations, group assignments, vault access, suspension, and deletion — without any third-party tooling. Like every app that mixes role-based and resource-level permissions

8x8 logo

8x8

Full API + SCIM
AutomationAPI + SCIM
Last updatedFeb 2026

8x8 Admin Console supports full lifecycle user management — create, deactivate, and delete — across its X Series unified communications platform. Every app a user can access (8x8 Work desktop, mobile, web, Agent Workspace) is gated by license assignmen