Summary and recommendation
Metabase exposes a REST user-management API under `https://<your-metabase-host>/api/user`. Authentication is via API key (`x-api-key` header, introduced in v0.47) or session token (`X-Metabase-Session` from `POST /api/session`). API keys are preferred for programmatic access; session tokens expire and require re-authentication.
All user-management endpoints require admin privileges except `GET /api/user/current` and self-directed `PUT /api/user/:id`. The API key's effective permissions are scoped to the group it was assigned at creation - keys do not inherit superuser rights by default.
For identity graph use cases, the `login_attributes` field on the user object carries key-value pairs used to drive row-level data sandboxing. This field is accepted on all plans but is only functional on Pro/Enterprise; on lower plans it is silently ignored.
Group membership is managed via `group_ids` (array of integers) on create and update - sending `group_ids` on a `PUT` replaces all memberships atomically, so callers must always supply the full desired set.
API quick reference
| Has user API | Yes |
| Auth method | API Key (x-api-key header) or Session Token (X-Metabase-Session header from POST /api/session) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Pro or Enterprise (cloud and self-hosted); SSO must be configured as a prerequisite |
Authentication
Auth method: API Key (x-api-key header) or Session Token (X-Metabase-Session header from POST /api/session)
Setup steps
- Navigate to Metabase Admin → Settings → Authentication → API Keys.
- Click 'Create API Key', assign a name and group, copy the generated key.
- Include the key in all requests as the HTTP header: x-api-key:
. - Alternatively, POST credentials to /api/session to receive a session token, then pass it as X-Metabase-Session:
on subsequent requests. - Session tokens expire; API keys are preferred for programmatic/long-lived access.
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique user ID | auto-assigned | immutable | |
| string | User's email address (login identifier) | required | optional | Must be unique | |
| first_name | string | User's first name | required | optional | |
| last_name | string | User's last name | required | optional | |
| password | string | User's password (write-only) | required (unless SSO) | optional | Not returned in responses |
| is_superuser | boolean | Whether user has admin privileges | optional (default false) | optional | Only admins can set this |
| is_active | boolean | Whether the user account is active | auto true | optional | Set false to deactivate |
| locale | string|null | User's preferred locale (e.g. 'en') | optional | optional | |
| group_ids | array[integer] | IDs of groups the user belongs to | optional | optional | Replaces all group memberships on update |
| login_attributes | object | Key-value attributes used for row-level data sandboxing | optional | optional | Pro/Enterprise feature |
| date_joined | datetime | Timestamp when user was created | auto-assigned | immutable | ISO 8601 |
| last_login | datetime|null | Timestamp of last login | null | system-managed | |
| updated_at | datetime | Timestamp of last update | auto-assigned | system-managed | |
| common_name | string | Computed display name (first + last) | derived | derived | Read-only |
| has_invited_second_user | boolean | Whether user has invited another user | false | system-managed | |
| personal_collection_id | integer|null | ID of user's personal collection | auto-assigned | immutable |
Core endpoints
List users
- Method: GET
- URL:
/api/user - Watch out for: Returns only active users by default. Pass ?status=all to include deactivated users. No pagination params; all matching users returned.
Request example
GET /api/user?status=active
x-api-key: <key>
Response example
{
"data": [{"id":1,"email":"admin@example.com","first_name":"Ada","last_name":"Lovelace","is_superuser":true,"is_active":true}],
"total": 1
}
Get user by ID
- Method: GET
- URL:
/api/user/:id - Watch out for: Non-admin users can only fetch their own record (/api/user/current). Fetching another user's record requires admin.
Request example
GET /api/user/42
x-api-key: <key>
Response example
{
"id": 42,
"email": "user@example.com",
"first_name": "Grace",
"last_name": "Hopper",
"is_superuser": false,
"is_active": true,
"group_ids": [1, 5]
}
Get current user
- Method: GET
- URL:
/api/user/current - Watch out for: Useful for validating API key identity. Returns the user associated with the API key or session token.
Request example
GET /api/user/current
x-api-key: <key>
Response example
{
"id": 7,
"email": "me@example.com",
"first_name": "Alan",
"last_name": "Turing",
"is_superuser": false
}
Create user
- Method: POST
- URL:
/api/user - Watch out for: Requires admin. An invite email is sent to the new user by default. If SSO is enforced, password field may be ignored.
Request example
POST /api/user
x-api-key: <key>
Content-Type: application/json
{"first_name":"Grace","last_name":"Hopper","email":"grace@example.com","password":"s3cur3!"}
Response example
{
"id": 43,
"email": "grace@example.com",
"first_name": "Grace",
"last_name": "Hopper",
"is_active": true,
"is_superuser": false
}
Update user
- Method: PUT
- URL:
/api/user/:id - Watch out for: Sending group_ids replaces all group memberships atomically. Omitting group_ids leaves memberships unchanged.
Request example
PUT /api/user/43
x-api-key: <key>
Content-Type: application/json
{"first_name":"Grace","last_name":"Murray Hopper","is_superuser":true}
Response example
{
"id": 43,
"email": "grace@example.com",
"first_name": "Grace",
"last_name": "Murray Hopper",
"is_superuser": true
}
Deactivate (soft-delete) user
- Method: DELETE
- URL:
/api/user/:id - Watch out for: Metabase does not hard-delete users via API. DELETE sets is_active=false. Reactivate by PUT /api/user/:id with {"is_active": true}.
Request example
DELETE /api/user/43
x-api-key: <key>
Response example
{"success": true}
Reactivate user
- Method: PUT
- URL:
/api/user/:id/reactivate - Watch out for: Dedicated reactivation endpoint; also achievable via PUT /api/user/:id with is_active:true.
Request example
PUT /api/user/43/reactivate
x-api-key: <key>
Response example
{
"id": 43,
"is_active": true,
"email": "grace@example.com"
}
Update user password
- Method: PUT
- URL:
/api/user/:id/password - Watch out for: old_password is required when a non-admin user changes their own password. Admins can omit it. Not applicable when SSO/LDAP is the auth provider.
Request example
PUT /api/user/43/password
x-api-key: <key>
Content-Type: application/json
{"password":"newP@ss!","old_password":"s3cur3!"}
Response example
{"success": true}
Rate limits, pagination, and events
Rate limits: Metabase does not publish explicit rate-limit tiers in official documentation. Rate limiting is not enforced by default in open-source or cloud deployments; heavy usage may be throttled at the infrastructure level.
Rate-limit headers: No
Retry-After header: No
Rate-limit notes: No documented per-plan rate limits found. Self-hosted instances inherit limits from the host infrastructure. Cloud plans may apply undocumented throttling.
Pagination method: none
Default page size: 0
Max page size: 0
Pagination pointer: Not documented
Webhooks available: No
Webhook notes: Metabase does not offer native outbound webhooks for user lifecycle events as of the latest official documentation.
Alternative event strategy: Poll GET /api/user on a schedule to detect changes, or use SCIM provisioning for event-driven user sync via your IdP.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Pro or Enterprise (cloud and self-hosted); SSO must be configured as a prerequisite
Endpoint: https://
/api/ee/scim/v2 Supported operations: GET /Users – list users, GET /Users/:id – get user, POST /Users – provision user, PUT /Users/:id – replace user, PATCH /Users/:id – update user (activate/deactivate), GET /Groups – list groups, GET /Groups/:id – get group, POST /Groups – create group, PUT /Groups/:id – replace group members, PATCH /Groups/:id – update group, DELETE /Groups/:id – delete group
Limitations:
- SSO (SAML or JWT) must be enabled before SCIM can be activated.
- SCIM token is generated once in Admin → Authentication → SCIM; losing it requires regeneration.
- User DELETE via SCIM deactivates (soft-delete) rather than permanently removing the record.
- Group membership sync depends on IdP group push support (Okta, Entra ID supported).
- Available on Pro ($500/mo) and Enterprise plans only; not available on Starter or OSS.
Common scenarios
Provision a user and assign to a group: Fetch available group IDs from GET /api/permissions/group, then POST /api/user with {first_name, last_name, email, password, group_ids:[<id>]}. An invite email fires automatically unless outbound email is disabled in instance settings. Confirm membership by inspecting group_ids on the returned user object.
Deactivate a departed user: Resolve the user's id via GET /api/user?status=active filtered by email. Issue DELETE /api/user/:id - this is a soft delete that sets is_active: false. The user record, their questions, dashboards, and collections all persist. Content reassignment must be handled separately; there is no API endpoint to bulk-transfer ownership.
Bulk sync via SCIM from Okta or Entra ID: SCIM 2.0 is available at https://<your-metabase-host>/api/ee/scim/v2 on Pro/Enterprise with SSO active. The /ee/ path segment means the endpoint returns 404 on OSS or Starter instances. Generate the SCIM bearer token once in Admin → Authentication → SCIM - it is shown only at generation time. Configure Okta attribute mappings (userName→email, givenName, familyName) and enable group push to sync Metabase group memberships from the IdP. SCIM DELETE operations are soft-deletes, consistent with the REST API behavior.
Provision a new user and assign to a group
- GET /api/permissions/group to retrieve available group IDs.
- POST /api/user with {first_name, last_name, email, password, group_ids:[
]} and x-api-key header. - Verify response contains the new user's id and is_active: true.
- Optionally confirm group membership via GET /api/user/:id and inspect group_ids.
Watch out for: group_ids in POST /api/user replaces all memberships; always include all desired group IDs. An invite email is sent automatically unless email is disabled in settings.
Deactivate a departed user
- GET /api/user?status=active to find the user's id by email.
- DELETE /api/user/:id to soft-deactivate the account.
- Confirm with GET /api/user/:id that is_active is now false.
Watch out for: DELETE does not remove the user or their content. Their questions, dashboards, and collections remain. Reassign or archive content separately if needed.
Bulk sync users via SCIM from Okta
- Ensure Pro/Enterprise plan is active and SAML SSO is configured in Metabase Admin.
- In Metabase Admin → Authentication → SCIM, enable SCIM and copy the SCIM endpoint URL and bearer token.
- In Okta, add the Metabase SCIM app, enter the endpoint URL and token, and configure attribute mappings (userName→email, givenName, familyName).
- Enable Okta group push to sync group memberships to Metabase groups.
- Test provisioning by assigning a user in Okta and verifying they appear in Metabase Admin → People.
Watch out for: SCIM token is shown only once at generation time. If lost, regenerate in Admin and update Okta immediately to avoid provisioning failures.
Why building this yourself is a trap
The most consequential API caveat is pagination: GET /api/user returns all users in a single response with no pagination parameters. On large instances this payload can be substantial; callers should not assume the response is paginated and must handle the full result set in memory.
Metabase publishes no explicit rate-limit tiers in official documentation. Cloud deployments may apply undocumented throttling at the infrastructure level; self-hosted instances inherit limits from the host. There are no Retry-After or rate-limit headers to parse - callers should implement conservative backoff without relying on header signals.
Two additional traps affect identity graph integrations specifically: login_attributes is silently ignored below Pro/Enterprise, so sandboxing logic built against this field will appear to work (no error returned) but produce no access restriction on lower plans.
And DELETE /api/user/:id never hard-deletes - if downstream identity graph reconciliation logic treats a 200 response from DELETE as confirmation of removal, it will be wrong; the record remains queryable via GET /api/user?status=all.
Automate Metabase 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.