Summary and recommendation
Mixpanel exposes two distinct API surfaces relevant to identity and user management: the Engage API (user profile CRUD) and the SCIM 2.0 API (org-level provisioning, Enterprise only). These use different base URLs - `api.mixpanel.com` for ingestion and `mixpanel.com/api/2.0` for queries - and mixing them produces 404s, not auth errors, which makes misconfiguration harder to diagnose.
Authentication is credential-type-dependent. Service Account credentials (Basic Auth, Base64-encoded username:secret) are used for org-level management. Project Secret (Basic Auth) is required for the Engage Query API.
The SCIM endpoint uses a separate Bearer token generated in Organization Settings > Security > SCIM. Never use the public Project Token for server-side management calls.
For teams building an identity graph, the Engage Query API (`POST /api/2.0/engage`) is the primary mechanism for pulling structured user profile data - including `$distinct_id`, `$email`, `$last_seen`, and arbitrary custom properties - that can be joined against records in other systems.
Pagination uses a `session_id` + incrementing `page` integer; the session expires after roughly 10 minutes of inactivity, so large exports must be completed without interruption or restarted from page 0.
API quick reference
| Has user API | Yes |
| Auth method | Service Account credentials (Basic Auth with username:secret) or Project Secret (Basic Auth). SCIM uses a dedicated SCIM OAuth token. |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: Service Account credentials (Basic Auth with username:secret) or Project Secret (Basic Auth). SCIM uses a dedicated SCIM OAuth token.
Setup steps
- Navigate to Organization Settings > Service Accounts and create a Service Account with appropriate role.
- Copy the Service Account username and secret.
- Encode credentials as Base64(username:secret) and pass in the Authorization header as 'Basic
'. - For project-level Engage/Ingestion API, use the Project Secret found under Project Settings > Access Keys.
- For SCIM provisioning, generate a SCIM token in Organization Settings > Security > SCIM and use it as a Bearer token.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| Organization Admin | Full access to organization settings, member management, and SCIM provisioning. | SCIM user provisioning, organization member management |
| Project Owner | Full access to a specific project including data ingestion and user profile management. | Engage API (user profile create/update/delete) |
| Project Admin | Administrative access to a project; can manage project members and settings. | Project member management endpoints |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| $distinct_id | string | Unique identifier for the user profile. | required | immutable | Used as the primary key for all Engage operations. |
| string | User's email address. | optional | optional | Standard Mixpanel reserved property. | |
| $first_name | string | User's first name. | optional | optional | Reserved property; displayed in Mixpanel UI. |
| $last_name | string | User's last name. | optional | optional | Reserved property; displayed in Mixpanel UI. |
| $name | string | Full display name of the user. | optional | optional | Overrides $first_name/$last_name in display if set. |
| $phone | string | User's phone number. | optional | optional | Reserved property. |
| $avatar | string (URL) | URL to user's avatar image. | optional | optional | Reserved property. |
| $created | string (ISO 8601) | Timestamp when the user profile was created. | optional | optional | Set manually; Mixpanel does not auto-populate. |
| $city | string | User's city, typically geo-resolved. | optional | optional | Can be set manually or auto-resolved from IP. |
| $country_code | string | ISO 3166-1 alpha-2 country code. | optional | optional | Auto-resolved from IP if not set. |
| $timezone | string | User's timezone (e.g., 'America/New_York'). | optional | optional | Reserved property. |
| $last_seen | string (ISO 8601) | Timestamp of last event recorded for the user. | system | system | Managed by Mixpanel; not writable directly. |
| custom properties | any (string, number, boolean, list, date) | Arbitrary key-value pairs for user attributes. | optional | optional | Avoid reserved $ prefix for custom properties. |
Core endpoints
Set / Create User Profile
- Method: POST
- URL:
https://api.mixpanel.com/engage#profile-set - Watch out for: Uses $set operation; properties not included are left unchanged. Use $set_once to avoid overwriting existing values.
Request example
POST /engage
Content-Type: application/json
{"$token":"PROJECT_TOKEN","$distinct_id":"user_123","$set":{"$email":"user@example.com","$name":"Jane Doe","plan":"pro"}}
Response example
{"status": 1, "error": null}
Update User Profile (Append/Increment)
- Method: POST
- URL:
https://api.mixpanel.com/engage#profile-numerical-add - Watch out for: $add only works on numeric properties. Use $append for list properties.
Request example
POST /engage
{"$token":"PROJECT_TOKEN","$distinct_id":"user_123","$add":{"login_count":1}}
Response example
{"status": 1, "error": null}
Delete User Profile
- Method: POST
- URL:
https://api.mixpanel.com/engage#profile-delete - Watch out for: Deletes the profile but does NOT delete associated event data. GDPR deletion requires a separate deletion API call.
Request example
POST /engage
{"$token":"PROJECT_TOKEN","$distinct_id":"user_123","$delete":""}
Response example
{"status": 1, "error": null}
Batch Ingest User Profiles
- Method: POST
- URL:
https://api.mixpanel.com/engage - Watch out for: Max 2,000 records per batch request. Prefer /import endpoint for large-scale ingestion.
Request example
POST /engage
[{"$token":"TOKEN","$distinct_id":"u1","$set":{"$email":"a@b.com"}},{"$token":"TOKEN","$distinct_id":"u2","$set":{"$email":"c@d.com"}}]
Response example
{"status": 1, "error": null}
Query User Profiles (Engage Query)
- Method: POST
- URL:
https://mixpanel.com/api/2.0/engage - Watch out for: Uses a different base URL (mixpanel.com not api.mixpanel.com). Requires Project Secret auth. Paginate using session_id + incrementing page param.
Request example
POST /api/2.0/engage
Authorization: Basic <base64(secret:)>
{"where":"properties[\"$email\"] == \"user@example.com\"","page_size":100}
Response example
{"status":"ok","results":[{"$distinct_id":"user_123","$properties":{"$email":"user@example.com"}}],"session_id":"abc123","page":0,"total":1}
GDPR Delete User Data
- Method: POST
- URL:
https://mixpanel.com/api/app/data-deletions/v3.0/ - Watch out for: Deletion is asynchronous; poll the task status endpoint. Deletes both events and profiles. Irreversible.
Request example
POST /api/app/data-deletions/v3.0/
Authorization: Basic <base64(secret:)>
{"distinct_ids":["user_123"],"compliance_type":"GDPR"}
Response example
{"status":"ok","results":{"task_id":"del_abc123"}}
SCIM List Users
- Method: GET
- URL:
https://mixpanel.com/api/app/scim/v2/Users - Watch out for: SCIM token is separate from Service Account credentials. Enterprise plan + SSO required.
Request example
GET /api/app/scim/v2/Users?startIndex=1&count=100
Authorization: Bearer <SCIM_TOKEN>
Response example
{"schemas":["urn:ietf:params:scim:api:messages:2.0:ListResponse"],"totalResults":2,"Resources":[{"id":"u1","userName":"user@example.com","active":true}]}
SCIM Create / Provision User
- Method: POST
- URL:
https://mixpanel.com/api/app/scim/v2/Users - Watch out for: Provisioned users receive an email invitation. Role assignment via SCIM is limited; use IDP group mappings for role control.
Request example
POST /api/app/scim/v2/Users
Authorization: Bearer <SCIM_TOKEN>
{"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],"userName":"new@example.com","name":{"givenName":"New","familyName":"User"},"active":true}
Response example
{"id":"u_new","userName":"new@example.com","active":true,"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}
Rate limits, pagination, and events
- Rate limits: Mixpanel enforces rate limits per project and per endpoint family. Ingestion (track/engage) limits differ from query API limits.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: HTTP 429 is returned when limits are exceeded. Retry-After header indicates wait time. Batch ingestion via /import is preferred for bulk operations to reduce request overhead.
- Pagination method: cursor
- Default page size: 1000
- Max page size: 1000
- Pagination pointer: session_id + page (Engage query API uses session_id returned in first response, then page=0,1,2...)
| Plan | Limit | Concurrent |
|---|---|---|
| Free / Growth | Ingestion: up to 2,000 events/second per project; Query API: 60 queries/hour | 5 |
| Enterprise | Ingestion: higher throughput negotiated; Query API: 120 queries/hour default, adjustable | 10 |
- Webhooks available: No
- Webhook notes: Mixpanel does not offer native outbound webhooks for user-management or profile events.
- Alternative event strategy: Use Mixpanel's Cohort Sync or Data Pipelines (Enterprise) to export user data to external systems. For real-time triggers, poll the Engage Query API or use a CDP integration.
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://mixpanel.com/api/app/scim/v2
Supported operations: GET /Users (list users), GET /Users/{id} (get user), POST /Users (create/provision user), PUT /Users/{id} (replace user), PATCH /Users/{id} (update user, including deactivate via active=false), DELETE /Users/{id} (deprovision user), GET /Groups (list groups), POST /Groups (create group), PATCH /Groups/{id} (update group membership), DELETE /Groups/{id} (delete group)
Limitations:
- Requires SSO to be configured before SCIM can be enabled.
- Enterprise plan required; not available on Free or Growth.
- SCIM token is a single org-level token; rotation requires manual regeneration in UI.
- Role assignment granularity via SCIM is limited; project-level roles must be managed separately or via IDP group-to-role mappings.
- Deprovisioning sets user to inactive but may not immediately revoke active sessions without SSO enforcement.
- Supported IdPs with documented integration: Okta, Microsoft Entra ID (Azure AD), OneLogin.
Common scenarios
Three integration patterns cover the majority of programmatic use cases:
Bulk CRM profile import: Transform CRM records into Engage payloads keyed on
$distinct_id, chunk into batches of ≤2,000, and POST tohttps://api.mixpanel.com/engage. For volumes in the millions, use the/importendpoint instead - it is optimized for throughput and avoids the silent record-drop behavior that occurs when the 2,000-record batch limit is exceeded.SCIM + Okta provisioning/deprovisioning: Requires Enterprise plan and a fully configured SAML SSO setup before the SCIM token can be generated. Okta sends
POST /Usersto provision andPATCH /Users/{id}withactive=falseto deprovision. Project-level role assignments are not controlled via SCIM; assign them manually post-provisioning or via Okta group-to-role mappings where supported.GDPR right-to-erasure: Identify the user's
$distinct_idvia the Engage Query API, submit a deletion task toPOST /api/app/data-deletions/v3.0/, and poll the task status untilCOMPLETE. The$deleteEngage operation removes the profile record immediately, but event data deletion is asynchronous and can take days - do not confirm erasure until the task status is verified.
Bulk-import user profiles from a CRM
- Obtain the Project Token from Project Settings > Access Keys.
- Transform CRM records into Mixpanel Engage payload format with $distinct_id, $email, $name, and custom properties.
- Chunk records into batches of ≤2,000 and POST each batch to https://api.mixpanel.com/engage as a JSON array.
- Check response for {"status":1} per batch; log and retry any failed batches with exponential backoff.
- Verify profiles appear in Mixpanel UI under Users > Explore.
Watch out for: Use the /import endpoint for very large volumes (millions of records) as it is optimized for bulk ingestion and has higher throughput limits than /engage.
Provision and deprovision employees via SCIM + Okta
- Confirm Enterprise plan is active and SSO (SAML) is configured in Mixpanel Organization Settings.
- Generate a SCIM token in Organization Settings > Security > SCIM Provisioning.
- In Okta, add the Mixpanel SCIM app, set Base URL to https://mixpanel.com/api/app/scim/v2, and paste the SCIM token as the API token.
- Enable provisioning features: Push New Users, Push Profile Updates, Deactivate Users.
- Assign Okta users/groups to the Mixpanel app; Okta will POST /Users to provision them.
- When an employee is offboarded in Okta, Okta sends PATCH /Users/{id} with active=false, deprovisioning the Mixpanel account.
Watch out for: Project-level role assignments are not fully controlled via SCIM; use Okta group-to-Mixpanel-role mappings where supported, or assign roles manually after provisioning.
GDPR right-to-erasure: delete all data for a user
- Identify the user's $distinct_id(s) - query via POST /api/2.0/engage with a where clause filtering by $email.
- POST to https://mixpanel.com/api/app/data-deletions/v3.0/ with {"distinct_ids":["
"],"compliance_type":"GDPR"} using Project Secret Basic Auth. - Record the returned task_id.
- Poll GET /api/app/data-deletions/v3.0/?task_id=
until status is 'COMPLETE'. - Also POST to /engage with $delete operation to remove the user profile immediately (event deletion is async via the deletion task).
Watch out for: The $delete Engage operation removes the profile record but event data persists until the async deletion task completes (can take days). Do not confirm erasure to the user until the task status is COMPLETE.
Why building this yourself is a trap
The most common integration trap is conflating the two Engage endpoints. The ingestion endpoint (POST https://api.mixpanel.com/engage) authenticates with the Project Token and writes profiles. The query endpoint (POST https://mixpanel.com/api/2.0/engage) authenticates with the Project Secret and reads profiles.
Using the wrong credential on either endpoint returns an auth error that does not clearly indicate the URL mismatch as the root cause.
A second trap is profile deletion scope. Calling $delete on the Engage ingestion endpoint removes the user profile record but leaves all associated event data intact. GDPR-compliant full erasure requires a separate, asynchronous call to the Data Deletion API - a two-step process that is easy to implement incompletely.
For teams using SCIM: the SCIM token is a single org-level credential with no scoped permissions and no automatic rotation. Rotation requires manual regeneration in the UI, which immediately invalidates the existing token and breaks any active IDP sync until the new token is updated.
Plan token rotation as a coordinated change, not a background task.
Automate Mixpanel 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.