Summary and recommendation
Magento's REST API exposes two distinct user domains: storefront customer accounts under `/rest/V1/customers` and backend admin users under `/rest/V1/users`. These are separate object models with separate authentication requirements and separate ACL scopes. Admin token auth (POST `/V1/integration/admin/token`) is the fastest path for server-side integrations; tokens expire after 4 hours by default.
OAuth 1.0a (not OAuth 2.0) is used for third-party Integration tokens created via System > Integrations - do not conflate the two flows.
All search endpoints require a `searchCriteria` query parameter; a bare GET without it returns HTTP 400. Pagination uses `searchCriteria[pageSize]` (max 300) and `searchCriteria[currentPage]`. For high-volume writes, use the async bulk endpoint (`/rest/async/bulk/V1/customers`) and poll the returned `bulkUuid` for status rather than issuing sequential PUTs.
Multi-store deployments must prefix the base URL with the store code (e.g., `/rest/en_US/V1/`) to scope results correctly; omitting the store code returns data from the default scope, which may silently return incomplete result sets.
There is no native SCIM 2.0 endpoint. Adobe Admin Console supports SCIM provisioning only for Azure AD and Google Workspace via the Adobe Identity Management System, and only for IMS-enabled Commerce Cloud admin identities - not for storefront customer accounts. Third-party Marketplace extensions exist but are not officially supported by Adobe.
Any identity graph built on top of Magento must account for this gap and rely on the REST API directly for provisioning and deprovisioning workflows.
API quick reference
| Has user API | Yes |
| Auth method | Bearer token (Admin Token via POST /V1/integration/admin/token) or OAuth 1.0a (for third-party integrations) |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Enterprise |
Authentication
Auth method: Bearer token (Admin Token via POST /V1/integration/admin/token) or OAuth 1.0a (for third-party integrations)
Setup steps
- For admin token auth: POST to /rest/V1/integration/admin/token with admin username and password to receive a bearer token.
- For customer token auth: POST to /rest/V1/integration/customer/token with customer credentials.
- For OAuth 1.0a: Create an Integration in Magento Admin (System > Integrations), grant required resource permissions, and complete the OAuth handshake to obtain access token and secret.
- Include the token in the Authorization header as 'Bearer
' on all subsequent requests. - For multi-store setups, prefix the base URL with the store code: /rest/
/V1/.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| Magento_Customer::manage | Create, read, update, and delete customer accounts. | All customer CRUD operations |
| Magento_Customer::group | Read and manage customer groups. | GET /V1/customerGroups, assigning customers to groups |
| Magento_User::acl_users | Manage admin users (backend accounts). | Admin user CRUD via /V1/users endpoints |
| Magento_User::acl_roles | Manage admin roles and ACL assignments. | GET/POST /V1/roles |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| id | integer | Unique customer ID assigned by Magento. | auto-generated | read-only | Used as path parameter in /V1/customers/{id} |
| string | Customer email address; used as login identifier. | required | optional | Must be unique per website scope. | |
| firstname | string | Customer first name. | required | optional | |
| lastname | string | Customer last name. | required | optional | |
| middlename | string | Customer middle name. | optional | optional | |
| prefix | string | Name prefix (e.g., Mr., Dr.). | optional | optional | |
| suffix | string | Name suffix. | optional | optional | |
| dob | string (date) | Date of birth in YYYY-MM-DD format. | optional | optional | |
| gender | integer | Gender code: 1=Male, 2=Female. | optional | optional | |
| group_id | integer | Customer group ID (e.g., 1=General, 2=Wholesale). | optional | optional | Defaults to General group if omitted. |
| store_id | integer | Store view ID the customer is associated with. | optional | optional | |
| website_id | integer | Website ID scope for the customer account. | required (if multi-website) | optional | Customer uniqueness is scoped per website_id. |
| is_active | boolean | Whether the customer account is active. | optional | optional | Defaults to true. |
| created_at | string (datetime) | Account creation timestamp. | auto-generated | read-only | |
| updated_at | string (datetime) | Last update timestamp. | auto-generated | auto-updated | |
| addresses | array of Address objects | Customer billing/shipping addresses. | optional | optional | Each address has street, city, region, postcode, country_id, telephone. |
| custom_attributes | array | EAV custom attributes defined for the customer entity. | optional | optional | Array of {attribute_code, value} objects. |
| password | string | Plain-text password (write-only, used only on create). | optional (if omitted, welcome email with reset link is sent) | not applicable | Sent in the top-level 'password' field, not inside the customer object. |
Core endpoints
List customers (search)
- Method: GET
- URL:
/rest/V1/customers/search - Watch out for: Requires searchCriteria parameter even for unfiltered requests; omitting it returns a 400 error.
Request example
GET /rest/V1/customers/search?searchCriteria[pageSize]=20&searchCriteria[currentPage]=1
Authorization: Bearer <admin_token>
Response example
{
"items": [{"id":1,"email":"jane@example.com","firstname":"Jane","lastname":"Doe"}],
"search_criteria": {"page_size":20,"current_page":1},
"total_count": 1
}
Get customer by ID
- Method: GET
- URL:
/rest/V1/customers/{customerId} - Watch out for: Returns 404 if customer does not exist in the requested store scope.
Request example
GET /rest/V1/customers/42
Authorization: Bearer <admin_token>
Response example
{
"id": 42,
"email": "jane@example.com",
"firstname": "Jane",
"lastname": "Doe",
"website_id": 1,
"group_id": 1
}
Create customer
- Method: POST
- URL:
/rest/V1/customers - Watch out for: If password is omitted, Magento sends a welcome email with a password reset link. Password must meet configured complexity rules or the request fails with 400.
Request example
POST /rest/V1/customers
Authorization: Bearer <admin_token>
{
"customer": {"email":"new@example.com","firstname":"New","lastname":"User","website_id":1},
"password": "Str0ng!Pass"
}
Response example
{
"id": 101,
"email": "new@example.com",
"firstname": "New",
"lastname": "User",
"created_at": "2024-01-15 10:00:00"
}
Update customer
- Method: PUT
- URL:
/rest/V1/customers/{customerId} - Watch out for: PUT replaces the full customer object. The customer id must be included in the request body and must match the path parameter.
Request example
PUT /rest/V1/customers/42
Authorization: Bearer <admin_token>
{
"customer": {"id":42,"firstname":"Jane","lastname":"Smith","email":"jane@example.com","website_id":1}
}
Response example
{
"id": 42,
"email": "jane@example.com",
"firstname": "Jane",
"lastname": "Smith"
}
Delete customer
- Method: DELETE
- URL:
/rest/V1/customers/{customerId} - Watch out for: Returns boolean true on success. Deletion is permanent and cascades to orders/quotes association references.
Request example
DELETE /rest/V1/customers/42
Authorization: Bearer <admin_token>
Response example
true
Get current authenticated customer (self)
- Method: GET
- URL:
/rest/V1/customers/me - Watch out for: Requires a customer bearer token, not an admin token.
Request example
GET /rest/V1/customers/me
Authorization: Bearer <customer_token>
Response example
{
"id": 42,
"email": "jane@example.com",
"firstname": "Jane",
"lastname": "Doe"
}
List admin users
- Method: GET
- URL:
/rest/V1/users - Watch out for: Only accessible with an admin token that has Magento_User::acl_users ACL resource permission.
Request example
GET /rest/V1/users?searchCriteria[pageSize]=20
Authorization: Bearer <admin_token>
Response example
{
"items": [{"id":1,"username":"admin","email":"admin@example.com","firstname":"Admin","is_active":true}],
"total_count": 1
}
Create admin user
- Method: POST
- URL:
/rest/V1/users - Watch out for: role_id must reference an existing admin role. Password must meet Magento admin password complexity requirements (min 7 chars, mixed case, digit).
Request example
POST /rest/V1/users
Authorization: Bearer <admin_token>
{
"user": {"username":"newadmin","firstname":"New","lastname":"Admin","email":"newadmin@example.com","password":"Str0ng!Pass","role_id":1,"is_active":true}
}
Response example
{
"id": 5,
"username": "newadmin",
"email": "newadmin@example.com",
"is_active": true
}
Rate limits, pagination, and events
- Rate limits: Magento does not enforce built-in REST API rate limits at the application layer by default. Rate limiting is typically handled at the web server (Nginx/Apache) or CDN/WAF layer. Adobe Commerce Cloud environments may apply infrastructure-level throttling.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: No standard rate-limit response headers are emitted by Magento core. Operators must configure throttling externally. Bulk API endpoints (/async/bulk) are recommended for high-volume operations.
- Pagination method: offset
- Default page size: 20
- Max page size: 300
- Pagination pointer: searchCriteria[pageSize] and searchCriteria[currentPage]
| Plan | Limit | Concurrent |
|---|---|---|
| Open Source / On-premise | No default application-level rate limit; server-dependent | 0 |
| Adobe Commerce Cloud | Infrastructure-level limits apply; configurable via Fastly or Nginx | 0 |
- Webhooks available: Yes
- Webhook notes: Adobe Commerce (2.4.4+) introduced a native Webhooks module that allows subscribing to commerce events and sending HTTP POST payloads to external URLs. Magento Open Source does not include this natively.
- Alternative event strategy: For Magento Open Source, use third-party extensions (e.g., Magento Marketplace webhook extensions) or implement custom observers that POST to external endpoints.
- Webhook events: observer.customer_save_after_data_object, observer.customer_register_success, observer.customer_delete_commit_after, observer.customer_login, observer.customer_logout, plugin.magento.customer.api.account_management_interface.create_account
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Enterprise
- Endpoint: Not documented
Limitations:
- Magento/Adobe Commerce has no native SCIM 2.0 endpoint.
- Adobe Admin Console (which manages Adobe Commerce Cloud admin identities) supports SCIM provisioning only for Azure AD and Google Workspace via the Adobe Identity Management System, not for Magento storefront customer accounts.
- Third-party marketplace extensions may provide SCIM-like provisioning but are not officially supported by Adobe.
Common scenarios
Provisioning a new storefront customer: POST /rest/V1/customers with email, firstname, lastname, and website_id. Omitting password triggers a welcome email with a password-set link; including it enforces server-side complexity rules (failure returns 400). The website_id must match an existing website in the instance - mismatches also return 400 with no further detail in the default error response.
Bulk customer group reassignment: GET /rest/V1/customers/search with a group_id filter, paginate with searchCriteria[currentPage] until total_count is exhausted, then issue updates via POST /rest/async/bulk/V1/customers. Critical caveat: PUT on /rest/V1/customers/{id} is a full object replacement, not a partial update. Always fetch the current customer object before writing; omitting fields like addresses will silently remove them.
Creating an admin user via API: GET /rest/V1/roles to retrieve valid role_id values, then POST /rest/V1/users with username, firstname, lastname, email, password, role_id, and is_active: true. The calling token must carry both Magento_User::acl_users and Magento_User::acl_roles ACL permissions. Admin password complexity requirements are stricter than customer requirements (minimum 7 characters, mixed case, digit); the API returns a generic 400 on failure without specifying which rule was violated.
Provision a new storefront customer account
- Obtain an admin bearer token: POST /rest/V1/integration/admin/token with {username, password}.
- POST /rest/V1/customers with the customer object (email, firstname, lastname, website_id) and optional password field.
- If password is omitted, the customer receives a welcome email with a password-set link.
- Store the returned customer id for future updates or lookups.
Watch out for: website_id must match an existing website in the Magento instance; mismatches cause a 400 error. Password complexity rules are enforced server-side.
Search and bulk-update customer group assignments
- GET /rest/V1/customers/search?searchCriteria[filter_groups][0][filters][0][field]=group_id&searchCriteria[filter_groups][0][filters][0][value]=1&searchCriteria[pageSize]=100 to retrieve customers in group 1.
- Iterate pages using searchCriteria[currentPage] until total_count is exhausted.
- For each customer, PUT /rest/V1/customers/{id} with the full customer object and updated group_id.
- For high volume, use POST /rest/async/bulk/V1/customers with an array of customer update payloads, then poll /rest/V1/bulk/{bulkUuid}/status.
Watch out for: PUT requires the full customer object; partial updates will overwrite existing fields with nulls if omitted. Always fetch the current customer object before updating.
Create an admin user and assign a role via API
- Obtain an admin bearer token with Magento_User::acl_users and Magento_User::acl_roles permissions.
- GET /rest/V1/roles to retrieve available role IDs.
- POST /rest/V1/users with {username, firstname, lastname, email, password, role_id, is_active:true}.
- Confirm creation by GET /rest/V1/users/{userId} and verify role assignment.
Watch out for: Admin user passwords must meet stricter complexity requirements than customer passwords. The role_id must be a valid existing role; there is no inline role creation in the same request.
Why building this yourself is a trap
The most common integration trap is treating Magento's REST API as a SCIM-compatible provisioning layer - it is not. There is no /scim/v2/Users endpoint, no standardized active flag for deprovisioning, and no group-push mechanism.
Any identity graph that needs to reflect Magento admin or customer state must be built on top of the raw REST endpoints with custom logic for lifecycle events.
A second trap is the dual-domain problem: customer accounts (/V1/customers) and admin accounts (/V1/users) are separate object models with different field schemas, different auth scopes, and different password policies.
An integration that conflates the two - or assumes a single token can manage both - will fail silently or with opaque 403 errors depending on which ACL resources the token was granted.
Finally, webhook support for customer lifecycle events (create, update, delete, login) is available natively only in Adobe Commerce 2.4.4+; Magento Open Source requires third-party extensions or custom observers.
Any event-driven provisioning architecture must verify the Commerce edition and version before relying on native webhooks, and must implement a fallback polling strategy against /V1/customers/search with a updated_at filter for environments where webhooks are unavailable.
Automate Magento 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.