Summary and recommendation
Campaign Monitor exposes a REST API at https://api.createsend.com/api/v3.3 supporting HTTP Basic Auth (API key as username, any non-empty string as password) and OAuth 2.0 for third-party integrations acting on behalf of other accounts.
The API covers subscriber lifecycle management, list operations, campaign creation, and client (sub-account) administration, but has no SCIM 2.0 endpoint and no documented identity provisioning surface.
For teams building automated provisioning pipelines across a broader tool stack, Stitchflow's MCP server with ~100 deep IT/identity integrations offers a faster path than assembling per-app API clients - Campaign Monitor included. The native API remains the right layer for subscriber-level automation within Campaign Monitor itself.
API quick reference
| Has user API | Yes |
| Auth method | HTTP Basic Auth with API key (API key as username, any string as password) or OAuth 2.0 for third-party integrations |
| Base URL | Official docs |
| SCIM available | No |
Authentication
Auth method: HTTP Basic Auth with API key (API key as username, any string as password) or OAuth 2.0 for third-party integrations
Setup steps
- Log in to Campaign Monitor and navigate to Account Settings > API Keys.
- Generate or copy your API key.
- For direct API calls: use HTTP Basic Auth with the API key as the username and any non-empty string (e.g., 'x') as the password.
- For third-party app integrations: implement OAuth 2.0 authorization code flow using the Campaign Monitor OAuth endpoints to obtain an access token on behalf of a user.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| ViewReports | Read access to campaign and list reports. | Reading subscriber and campaign analytics |
| CreateCampaigns | Ability to create and send campaigns. | Campaign creation |
| SendCampaigns | Ability to send campaigns. | Sending campaigns |
| AddSubscribersToList | Add or update subscribers on a list. | Subscriber creation and updates |
| ImportSubscribers | Bulk import subscribers. | Bulk subscriber operations |
| ManageSubscribersForAllLists | Manage subscribers across all lists. | Cross-list subscriber management |
| AdministerAccount | Full account administration including client management. | Client/account-level user management |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| EmailAddress | string | Subscriber's email address (primary identifier). | required | required | Used as the unique key for a subscriber within a list. |
| Name | string | Subscriber's full name. | optional | optional | |
| Date | string (ISO 8601) | Date the subscriber was added to the list. | system-set | read-only | Returned in responses; not settable on create. |
| State | string | Subscription state: Active, Unsubscribed, Bounced, Deleted, or Unconfirmed. | system-set | read-only | Controlled by subscribe/unsubscribe actions. |
| CustomFields | array of objects | List of custom field key-value pairs for the subscriber. | optional | optional | Each object has Key and Value properties. Fields must be pre-defined on the list. |
| ResubscribeBehavior | string | Controls behavior when adding an existing subscriber: OnlySubscribed, OnlyUnsubscribed, or AlwaysSubscribe. | optional | optional | Used in add/update subscriber requests. |
| ConsentToTrack | string | Consent status for tracking: Yes, No, or Unchanged. | required (in some regions) | optional | Required for GDPR compliance in applicable regions. |
| ListID | string | The ID of the list the subscriber belongs to. | required (in URL) | required (in URL) | Passed as a URL path parameter, not in the body. |
Core endpoints
Add or update a subscriber
- Method: POST
- URL:
https://api.createsend.com/api/v3.3/subscribers/{listId}.json - Watch out for: If the subscriber already exists and Resubscribe is false, the call succeeds but the subscriber is not re-added.
Request example
POST /api/v3.3/subscribers/abc123.json
{
"EmailAddress": "user@example.com",
"Name": "Jane Doe",
"ConsentToTrack": "Yes",
"CustomFields": [{"Key": "City", "Value": "Sydney"}],
"Resubscribe": true
}
Response example
"user@example.com"
Get subscriber details
- Method: GET
- URL:
https://api.createsend.com/api/v3.3/subscribers/{listId}.json?email={email} - Watch out for: Email must be URL-encoded in the query string.
Request example
GET /api/v3.3/subscribers/abc123.json?email=user%40example.com
Response example
{
"EmailAddress": "user@example.com",
"Name": "Jane Doe",
"Date": "2024-01-15 10:00:00",
"State": "Active",
"CustomFields": [{"Key": "City", "Value": "Sydney"}]
}
Unsubscribe a subscriber
- Method: POST
- URL:
https://api.createsend.com/api/v3.3/subscribers/{listId}/unsubscribe.json - Watch out for: This unsubscribes from the specific list only, not globally.
Request example
POST /api/v3.3/subscribers/abc123/unsubscribe.json
{
"EmailAddress": "user@example.com"
}
Response example
HTTP 200 OK (empty body)
Delete a subscriber
- Method: DELETE
- URL:
https://api.createsend.com/api/v3.3/subscribers/{listId}.json?email={email} - Watch out for: Deletion is permanent and removes the subscriber from the list entirely.
Request example
DELETE /api/v3.3/subscribers/abc123.json?email=user%40example.com
Response example
HTTP 200 OK (empty body)
Bulk import subscribers
- Method: POST
- URL:
https://api.createsend.com/api/v3.3/subscribers/{listId}/import.json - Watch out for: Maximum 1,000 subscribers per import request. Larger batches must be split.
Request example
POST /api/v3.3/subscribers/abc123/import.json
{
"Subscribers": [
{"EmailAddress": "a@example.com", "Name": "Alice", "ConsentToTrack": "Yes"},
{"EmailAddress": "b@example.com", "Name": "Bob", "ConsentToTrack": "No"}
],
"Resubscribe": true
}
Response example
{
"TotalUniqueEmailsSubmitted": 2,
"TotalExistingSubscribers": 0,
"TotalNewSubscribers": 2,
"DuplicateEmailsInSubmission": [],
"FailureDetails": []
}
Get active subscribers for a list
- Method: GET
- URL:
https://api.createsend.com/api/v3.3/lists/{listId}/active.json - Watch out for: Max page size is 1,000. Iterate pages using the page parameter.
Request example
GET /api/v3.3/lists/abc123/active.json?page=1&pagesize=1000&orderfield=email&orderdirection=asc
Response example
{
"Results": [
{"EmailAddress": "user@example.com", "Name": "Jane", "Date": "2024-01-15", "State": "Active"}
],
"ResultsOrderedBy": "email",
"TotalNumberOfRecords": 1,
"PageNumber": 1,
"PageSize": 1000
}
Create a client (sub-account)
- Method: POST
- URL:
https://api.createsend.com/api/v3.3/clients.json - Watch out for: Requires AdministerAccount OAuth scope or account-level API key. Returns the new ClientID.
Request example
POST /api/v3.3/clients.json
{
"CompanyName": "Acme Corp",
"Country": "Australia",
"TimeZone": "(GMT+10:00) Canberra, Melbourne, Sydney"
}
Response example
"a1b2c3d4e5f6"
Get client details
- Method: GET
- URL:
https://api.createsend.com/api/v3.3/clients/{clientId}.json - Watch out for: Only accessible with account-level (agency) API key or appropriate OAuth scope.
Request example
GET /api/v3.3/clients/a1b2c3d4e5f6.json
Response example
{
"BasicDetails": {
"ClientID": "a1b2c3d4e5f6",
"CompanyName": "Acme Corp",
"Country": "Australia",
"TimeZone": "(GMT+10:00) Canberra, Melbourne, Sydney"
}
}
Rate limits, pagination, and events
- Rate limits: Campaign Monitor enforces rate limits per API key. The documented limit is 1 request per second for most endpoints. Bulk import endpoints have separate limits.
- Rate-limit headers: No
- Retry-After header: No
- Rate-limit notes: HTTP 429 is returned when the rate limit is exceeded. The API does not document explicit rate-limit response headers. Retry with exponential backoff is recommended.
- Pagination method: offset
- Default page size: 1000
- Max page size: 1000
- Pagination pointer: page, pagesize
| Plan | Limit | Concurrent |
|---|---|---|
| All plans | 1 request/second per API key | 1 |
- Webhooks available: Yes
- Webhook notes: Campaign Monitor supports list-level webhooks that fire on subscriber lifecycle events. Webhooks are configured per list via the API or UI and POST a JSON payload to a specified URL.
- Alternative event strategy: Polling the /lists/{listId}/active.json, /lists/{listId}/unsubscribed.json, and /lists/{listId}/bounced.json endpoints for changes.
- Webhook events: Subscribe, Deactivate (Unsubscribe or Bounce), Update (subscriber details changed)
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Not documented
- Endpoint: Not documented
Limitations:
- Campaign Monitor does not offer a SCIM 2.0 API for identity provisioning.
- No documented SAML SSO or SCIM support across any pricing tier.
Common scenarios
Three scenarios cover the majority of programmatic use cases:
Provisioning a subscriber: POST to /subscribers/{listId}.json with EmailAddress, Name, ConsentToTrack, and optional CustomFields. Set Resubscribe: true to reactivate previously unsubscribed contacts. Omitting ConsentToTrack in GDPR-applicable regions returns a 400 error.
Deprovisioning across lists: There is no global unsubscribe or delete endpoint. Retrieve all lists via GET /clients/{clientId}/lists.json, then call POST /subscribers/{listId}/unsubscribe.json or DELETE /subscribers/{listId}.json?email={email} per list. Confirm removal with a follow-up GET; expect a 404 or Deleted state.
Bulk import: POST batches of up to 1,000 records to /subscribers/{listId}/import.json. Inspect FailureDetails in the response for per-record errors. Batches exceeding 1,000 must be split - the API rejects larger payloads outright.
Provision a new subscriber to a list
- Obtain the target list ID from the Campaign Monitor UI or GET /clients/{clientId}/lists.json.
- POST to /subscribers/{listId}.json with EmailAddress, Name, ConsentToTrack, and any CustomFields.
- Set Resubscribe: true to re-activate previously unsubscribed contacts if needed.
- Check the response for the confirmed email address or handle 400 errors for validation failures.
Watch out for: If ConsentToTrack is omitted and the account is in a GDPR region, the request will fail with a 400 error.
Sync and deprovision a subscriber across lists
- Retrieve all lists for the client via GET /clients/{clientId}/lists.json.
- For each list, call POST /subscribers/{listId}/unsubscribe.json with the subscriber's email to unsubscribe them.
- Optionally call DELETE /subscribers/{listId}.json?email={email} to fully remove the record from each list.
- Verify removal by calling GET /subscribers/{listId}.json?email={email} and confirming a 404 or Deleted state.
Watch out for: Unsubscribe and delete must be performed per-list; there is no single global deprovision endpoint.
Bulk import subscribers from an external system
- Prepare subscriber data in batches of up to 1,000 records.
- POST each batch to /subscribers/{listId}/import.json with the Subscribers array and Resubscribe flag.
- Inspect the FailureDetails array in the response to identify and retry failed records.
- Use webhooks (Subscribe event) on the list to confirm successful additions in near-real-time.
Watch out for: Batches exceeding 1,000 subscribers must be split; the API will reject larger payloads.
Why building this yourself is a trap
The rate limit of approximately 1 request/second per API key with no rate-limit response headers creates silent backpressure: HTTP 429 is returned on breach, but without Retry-After guidance, callers must implement their own exponential backoff logic. At scale, cross-list deprovision loops (one API call per list per subscriber) will hit this ceiling quickly.
Subscriber deletion via the API is irreversible - there is no soft-delete or recoverable state. Client management endpoints (creating or reading sub-accounts) require an account-level API key, not a client-level key; using the wrong credential returns an auth error with no clear diagnostic.
OAuth 2.0 is mandatory for any integration acting on behalf of a third-party Campaign Monitor account, and the AdministerAccount scope must be explicitly requested - it is not bundled with narrower scopes.
Automate Campaign Monitor 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.