Summary and recommendation
Iterable's REST API (base URL: https://api.iterable.com/api) authenticates via API key passed as the `Api-Key` HTTP header. Keys are project-scoped - a key issued in one Iterable project cannot read or write data in another. The API supports user profile upserts, bulk updates, list subscription management, event tracking, and permanent deletion.
There is no SCIM endpoint; automated provisioning and deprovisioning require direct REST integration. When building an identity graph across systems, Iterable's dual-identifier model (email as default primary key, with optional `userId` via `preferUserId=true`) must be handled consistently across every call - mixing identifier strategies across requests will produce split profiles.
API quick reference
| Has user API | Yes |
| Auth method | API Key (passed as header: Api-Key) |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Enterprise |
Authentication
Auth method: API Key (passed as header: Api-Key)
Setup steps
- Log in to Iterable and navigate to Integrations > API Keys.
- Click 'New API Key' and select the appropriate key type (Standard, Server-side, Mobile, or Web).
- Assign the required permissions/scopes to the key.
- Copy the generated API key value.
- Include the key in all requests via the HTTP header:
Api-Key: <your_api_key>.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| users.read | Read user profile data | GET /users/getByEmail, GET /users/{id} |
| users.write | Create or update user profiles | POST /users/update, POST /users/bulkUpdate |
| users.delete | Delete user profiles | DELETE /users/{email} |
| lists.read | Read static list membership | GET /lists/getUsers |
| lists.write | Subscribe/unsubscribe users to/from lists | POST /lists/subscribe, POST /lists/unsubscribe |
| events.write | Track custom events for users | POST /events/track |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| string | Primary identifier for the user profile | required | required (or userId) | Case-insensitive; used as the default user identifier | |
| userId | string | Optional external user ID; can be used as an alternate identifier | optional | optional | If provided, can be used instead of email in some endpoints |
| dataFields | object | Key-value map of custom profile attributes (e.g., firstName, lastName, plan, etc.) | optional | optional | Merged with existing dataFields by default; use mergeNestedObjects flag to control nested merging |
| preferUserId | boolean | If true, Iterable uses userId as the primary identifier instead of email | optional | optional | Relevant when userId is set and email may change |
| mergeNestedObjects | boolean | Controls whether nested objects in dataFields are merged or replaced | optional | optional | Default is false (replace nested objects) |
| emailListIds | array |
List IDs to subscribe the user to on update | optional | optional | Used in /users/update to manage list subscriptions inline |
| unsubscribedChannelIds | array |
Channel IDs from which the user is unsubscribed | optional | optional | Manages channel-level suppression |
| unsubscribedMessageTypeIds | array |
Message type IDs from which the user is unsubscribed | optional | optional | Manages message-type-level suppression |
| subscribedMessageTypeIds | array |
Message type IDs to which the user is explicitly subscribed | optional | optional | Used for opt-in message types |
| profileUpdatedAt | string (ISO 8601) | Timestamp of last profile update | system-set | system-set | Read-only; set by Iterable |
| signupDate | string (ISO 8601) | Date the user signed up; stored in dataFields by convention | optional | optional | Custom dataField; not a reserved system field |
Core endpoints
Create or update a user
- Method: POST
- URL:
https://api.iterable.com/api/users/update - Watch out for: Upserts by default - creates the user if they do not exist. Does not return the full user object.
Request example
POST /api/users/update
{
"email": "user@example.com",
"dataFields": {
"firstName": "Jane",
"plan": "pro"
}
}
Response example
{
"msg": "User updated",
"code": "Success",
"params": {}
}
Bulk create or update users
- Method: POST
- URL:
https://api.iterable.com/api/users/bulkUpdate - Watch out for: Maximum 1,000 users per request. Partial failures are not surfaced per-record in the response.
Request example
POST /api/users/bulkUpdate
{
"users": [
{"email": "a@example.com", "dataFields": {"plan": "free"}},
{"email": "b@example.com", "dataFields": {"plan": "pro"}}
]
}
Response example
{
"msg": "Users updated",
"code": "Success",
"params": {}
}
Get user by email
- Method: GET
- URL:
https://api.iterable.com/api/users/{email} - Watch out for: Email must be URL-encoded. Returns 404 if user does not exist.
Request example
GET /api/users/user@example.com
Response example
{
"user": {
"email": "user@example.com",
"userId": "u_123",
"dataFields": {"firstName": "Jane"},
"profileUpdatedAt": "2024-01-15T10:00:00Z"
}
}
Get user by userId
- Method: GET
- URL:
https://api.iterable.com/api/users/byUserId/{userId} - Watch out for: Only works if userId was set on the profile. Returns 404 if not found.
Request example
GET /api/users/byUserId/u_123
Response example
{
"user": {
"email": "user@example.com",
"userId": "u_123",
"dataFields": {}
}
}
Delete user by email
- Method: DELETE
- URL:
https://api.iterable.com/api/users/{email} - Watch out for: Permanently deletes the user profile and all associated data. Irreversible.
Request example
DELETE /api/users/user@example.com
Response example
{
"msg": "User deleted",
"code": "Success",
"params": {}
}
Update user email address
- Method: POST
- URL:
https://api.iterable.com/api/users/updateEmail - Watch out for: The old email address is retired; all profile data and history migrate to the new email.
Request example
POST /api/users/updateEmail
{
"currentEmail": "old@example.com",
"newEmail": "new@example.com"
}
Response example
{
"msg": "Email updated",
"code": "Success",
"params": {}
}
Subscribe users to a list
- Method: POST
- URL:
https://api.iterable.com/api/lists/subscribe - Watch out for: Maximum 1,000 subscribers per request. Users are created if they do not already exist.
Request example
POST /api/lists/subscribe
{
"listId": 12345,
"subscribers": [
{"email": "user@example.com"}
]
}
Response example
{
"successCount": 1,
"failCount": 0,
"invalidEmails": []
}
Get users in a list
- Method: GET
- URL:
https://api.iterable.com/api/lists/getUsers - Watch out for: Returns only email addresses, not full profile data. For large lists, use the Data Export API instead.
Request example
GET /api/lists/getUsers?listId=12345
Response example
{
"getUsers": {
"listId": 12345,
"emails": ["user@example.com", "other@example.com"]
}
}
Rate limits, pagination, and events
- Rate limits: Rate limits are enforced per API key per endpoint category. Limits vary by endpoint type. Exceeding limits returns HTTP 429.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: HTTP 429 is returned when limits are exceeded. Iterable recommends exponential backoff. Bulk endpoints (e.g., /users/bulkUpdate) accept up to 1,000 records per call and are preferred over looping single-user calls.
- Pagination method: token
- Default page size: 0
- Max page size: 0
- Pagination pointer: startDateTime / endDateTime for export endpoints; /lists/getUsers returns all users in a list without pagination (use export API for large sets)
| Plan | Limit | Concurrent |
|---|---|---|
| Standard (all plans) | 500 requests/second for /users/update; 100 requests/second for most other endpoints | 0 |
| Bulk endpoints | Up to 1,000 users per bulk request; overall throughput subject to account-level limits | 0 |
- Webhooks available: Yes
- Webhook notes: Iterable supports outbound webhooks (called 'Webhooks' in the platform) that fire on messaging events such as email sends, opens, clicks, bounces, and unsubscribes. These are configured in Integrations > Webhooks.
- Alternative event strategy: Iterable also supports a Data Export API (CSV/JSON streaming) for bulk historical event and user data retrieval.
- Webhook events: emailSend, emailOpen, emailClick, emailBounce, emailUnsubscribe, emailComplaint, smsSend, smsReceived, pushSend, pushOpen, inAppSend, inAppOpen, inAppClick, customEvent
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Enterprise
- Endpoint: Not documented
Limitations:
- Iterable does not support SCIM provisioning as of the current documentation.
- SAML SSO with Just-in-Time (JIT) provisioning is available on Enterprise plans.
- User provisioning must be handled via the REST API or manual invitation.
Common scenarios
Three integration patterns cover the majority of lifecycle automation use cases.
First, provisioning: POST /api/users/update (upsert) to create or update a profile, then POST /api/lists/subscribe to add the user to the relevant onboarding segment, and optionally POST /api/events/track to fire a custom event for journey triggering.
Second, bulk CRM sync: batch records into groups of up to 1,000 and POST /api/users/bulkUpdate per batch; note that bulk responses do not surface per-record failure details, so request payloads should be logged independently for reconciliation.
Third, GDPR deletion: confirm the profile exists via GET /api/users/{email} (URL-encode the address), then DELETE /api/users/{email} - this is permanent, removes all historical event data, and cannot be undone.
Rate limits are enforced per API key: 500 req/s on /users/update, 100 req/s on most other endpoints; HTTP 429 responses should trigger exponential backoff.
Provision a new user and subscribe to onboarding list
- POST /api/users/update with email, userId, and relevant dataFields (e.g., firstName, plan, signupDate).
- POST /api/lists/subscribe with the onboarding listId and the user's email to add them to the onboarding segment.
- Optionally POST /api/events/track to record a 'userSignedUp' custom event for journey triggering.
Watch out for: /users/update is an upsert; if the email already exists, the profile will be updated, not duplicated. Ensure dataFields are correct before calling.
Update user profile attributes in bulk after CRM sync
- Batch updated user records into groups of up to 1,000.
- POST /api/users/bulkUpdate for each batch with the email and changed dataFields.
- Monitor response for 'code: Success'; implement exponential backoff on HTTP 429 responses.
Watch out for: bulkUpdate does not return per-record success/failure details. Log request payloads to diagnose issues if counts don't match expectations.
Deprovision a user (GDPR deletion)
- Confirm the user's email address via GET /api/users/{email} to verify the profile exists.
- DELETE /api/users/{email} to permanently remove the user profile and all associated event data.
- If the user has a userId alias, also call DELETE /api/users/byUserId/{userId} if needed (verify current API docs - deletion by email removes all aliases).
Watch out for: Deletion is irreversible and removes all historical event data for the user. There is no soft-delete or archive option. Confirm compliance requirements before executing.
Why building this yourself is a trap
The upsert behavior of POST /api/users/update is the most common source of unintended data side effects: any call with a new email address silently creates a profile, which can inflate contact counts and trigger unwanted journey enrollments.
The /lists/getUsers endpoint returns only email addresses - not full profile data - so any integration that needs complete user records must route through the Data Export API instead, which is a meaningfully different operational pattern.
Deleting a user by email removes all aliases and all associated event history with no soft-delete or archive option; this is irreversible and should be gated behind a confirmation step in any automated workflow.
Finally, because Iterable has no SCIM, any identity graph that relies on IdP deprovisioning signals to cascade access removal will have a gap at the Iterable layer - REST API calls for deletion must be explicitly orchestrated.
Automate Iterable 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.