Summary and recommendation
Drip exposes a REST API at https://api.getdrip.com/v2, authenticated via HTTP Basic Auth (API token as username, empty password) or OAuth 2.0 for third-party app flows. All resource URLs require the account_id as a path segment - omitting it returns a 404 with no further detail.
The API operates on a subscriber model, not an internal user/seat model; there is no SCIM endpoint and no IdP integration such as Okta or Entra is documented.
For teams building an identity graph, Drip subscriber records - keyed by email and enriched with custom_fields and tags - are the primary joinable entity across downstream systems.
Rate limits are enforced at 3,600 requests/hour per token with HTTP 429 on breach; headers X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset are returned on every response. The batch subscriber endpoint (POST /v2/{account_id}/subscribers/batches) accepts up to 1,000 records per call and counts as a single request - use it aggressively when syncing large lists.
Pagination uses offset-based page and per_page params with a maximum page size of 1,000.
API quick reference
| Has user API | Yes |
| Auth method | HTTP Basic Auth using API token as username, empty password; or Bearer token via OAuth 2.0 for third-party apps |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | N/A |
Authentication
Auth method: HTTP Basic Auth using API token as username, empty password; or Bearer token via OAuth 2.0 for third-party apps
Setup steps
- Log in to Drip and navigate to Settings > User Settings > API.
- Copy your personal API token.
- For Basic Auth: Base64-encode '
:' and pass as Authorization: Basic . - For OAuth 2.0 (third-party integrations): Register an OAuth application at developer.drip.com, obtain client_id and client_secret, redirect users through the authorization flow to obtain an access token.
- Include the account_id (found in Settings > General) in all resource URLs.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| read | Read access to subscribers, tags, custom fields, and account data. | GET operations on subscribers and account resources |
| write | Create and update subscribers, apply tags, record events. | POST/PUT operations on subscribers |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| string | Subscriber's email address. Primary identifier. | required | optional (used to look up record) | Must be unique per account. | |
| id | string | Drip-assigned unique subscriber ID. | system-generated | read-only | Can be used as lookup key instead of email. |
| first_name | string | Subscriber's first name. | optional | optional | |
| last_name | string | Subscriber's last name. | optional | optional | |
| address1 | string | Street address line 1. | optional | optional | |
| city | string | City. | optional | optional | |
| state | string | State or region. | optional | optional | |
| zip | string | Postal/ZIP code. | optional | optional | |
| country | string | Country (ISO 3166-1 alpha-2 recommended). | optional | optional | |
| phone | string | Phone number. | optional | optional | |
| tags | array[string] | List of tag names applied to the subscriber. | optional | optional | Tags are additive on update unless remove_tags is used. |
| remove_tags | array[string] | Tags to remove from the subscriber on update. | n/a | optional | Only valid in update/upsert payloads. |
| custom_fields | object | Key-value map of custom field identifiers to values. | optional | optional | Keys must match existing custom field identifiers in the account. |
| status | string | Subscription status: active, unsubscribed, or undeliverable. | optional (defaults to active) | optional | Setting to unsubscribed opts the subscriber out globally. |
| time_zone | string | IANA time zone string (e.g., America/New_York). | optional | optional | |
| ip_address | string | IP address at time of opt-in. | optional | optional | |
| user_agent | string | Browser/user agent at time of opt-in. | optional | optional | |
| initial_status | string | Forces subscriber status on creation (active or unsubscribed). | optional | n/a | Useful for importing pre-existing unsubscribes. |
| created_at | ISO 8601 datetime | Timestamp when subscriber was created. | system-generated | read-only | |
| updated_at | ISO 8601 datetime | Timestamp of last update. | system-generated | system-generated |
Core endpoints
List subscribers
- Method: GET
- URL:
https://api.getdrip.com/v2/{account_id}/subscribers - Watch out for: Returns up to 1,000 per page. Deleted/unsubscribed records are included unless filtered with status param.
Request example
GET /v2/9999999/subscribers?page=1&per_page=100
Authorization: Basic <base64(api_token:)>
Response example
{
"subscribers": [{"id":"abc123","email":"user@example.com","status":"active"}],
"meta": {"page":1,"count":1,"total_pages":1,"total_count":1}
}
Fetch a subscriber
- Method: GET
- URL:
https://api.getdrip.com/v2/{account_id}/subscribers/{subscriber_id_or_email} - Watch out for: Email must be URL-encoded when used as path parameter.
Request example
GET /v2/9999999/subscribers/user%40example.com
Authorization: Basic <base64(api_token:)>
Response example
{
"subscribers": [{
"id": "abc123",
"email": "user@example.com",
"status": "active",
"tags": ["customer"]
}]
}
Create or update a subscriber (upsert)
- Method: POST
- URL:
https://api.getdrip.com/v2/{account_id}/subscribers - Watch out for: This endpoint upserts by email. Existing subscribers are updated, not duplicated. Returns 201 on create, 200 on update.
Request example
POST /v2/9999999/subscribers
Content-Type: application/json
{
"subscribers": [{
"email": "user@example.com",
"first_name": "Jane",
"tags": ["lead"]
}]
}
Response example
{
"subscribers": [{
"id": "abc123",
"email": "user@example.com",
"status": "active"
}]
}
Batch create or update subscribers
- Method: POST
- URL:
https://api.getdrip.com/v2/{account_id}/subscribers/batches - Watch out for: Max 1,000 subscribers per batch. Response is 201 with no body - errors are not surfaced per-record synchronously.
Request example
POST /v2/9999999/subscribers/batches
Content-Type: application/json
{
"batches": [{
"subscribers": [
{"email":"a@example.com","tags":["vip"]},
{"email":"b@example.com"}
]
}]
}
Response example
HTTP 201 No Content
Update a subscriber
- Method: PUT
- URL:
https://api.getdrip.com/v2/{account_id}/subscribers/{subscriber_id} - Watch out for: custom_fields are merged, not replaced. To clear a field, pass null as the value.
Request example
PUT /v2/9999999/subscribers/abc123
Content-Type: application/json
{
"subscribers": [{"email":"user@example.com","custom_fields":{"plan":"pro"}}]
}
Response example
{
"subscribers": [{"id":"abc123","email":"user@example.com","status":"active"}]
}
Unsubscribe a subscriber
- Method: POST
- URL:
https://api.getdrip.com/v2/{account_id}/subscribers/{subscriber_id}/unsubscribe - Watch out for: Unsubscribing is global across all campaigns for that account. Cannot be reversed via API - subscriber must re-opt-in.
Request example
POST /v2/9999999/subscribers/abc123/unsubscribe
Response example
{
"subscribers": [{"id":"abc123","email":"user@example.com","status":"unsubscribed"}]
}
Delete a subscriber
- Method: DELETE
- URL:
https://api.getdrip.com/v2/{account_id}/subscribers/{subscriber_id} - Watch out for: Deletion is permanent and irreversible. Deleted subscribers cannot be recovered via API.
Request example
DELETE /v2/9999999/subscribers/abc123
Authorization: Basic <base64(api_token:)>
Response example
HTTP 204 No Content
Tag a subscriber
- Method: POST
- URL:
https://api.getdrip.com/v2/{account_id}/tags - Watch out for: Tags are created automatically if they don't exist. Tag names are case-sensitive.
Request example
POST /v2/9999999/tags
Content-Type: application/json
{
"tags": [{"email":"user@example.com","tag":"vip"}]
}
Response example
HTTP 201 No Content
Rate limits, pagination, and events
- Rate limits: Drip enforces rate limits per API token. The documented limit is 3,600 requests per hour (1 request/second sustained). Exceeding the limit returns HTTP 429.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: Rate limit headers include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Batch endpoints (e.g., batch subscriber upsert) count as a single request and support up to 1,000 records per call - use these to stay within limits.
- Pagination method: offset
- Default page size: 100
- Max page size: 1000
- Pagination pointer: page and per_page
| Plan | Limit | Concurrent |
|---|---|---|
| All paid plans | 3,600 requests/hour | 0 |
- Webhooks available: Yes
- Webhook notes: Drip supports webhooks (called 'Notifications') that POST JSON payloads to a configured URL when subscriber events occur. Configured under Settings > Integrations > Webhooks in the Drip UI.
- Alternative event strategy: Polling the subscribers list endpoint with updated_at filter as a fallback for event detection.
- Webhook events: subscriber.created, subscriber.subscribed_to_campaign, subscriber.removed_from_campaign, subscriber.completed_campaign, subscriber.applied_tag, subscriber.removed_tag, subscriber.updated_custom_field, subscriber.unsubscribed_globally, subscriber.unsubscribed_from_campaign, subscriber.bounced, subscriber.marked_as_spam, subscriber.became_lead, subscriber.became_customer, email.sent, email.opened, email.clicked, order.placed, order.paid, order.refunded
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: N/A
- Endpoint: Not documented
Limitations:
- Drip does not offer a SCIM API. User/subscriber management is handled exclusively through the REST API.
- No SSO or IdP integration (Okta, Entra, etc.) is documented.
Common scenarios
Three integration patterns cover the majority of production use cases.
Bulk import and tag: Chunk records into batches of up to 1,000 and POST to /subscribers/batches. The endpoint returns HTTP 201 with no body - per-record errors are not surfaced synchronously. Always verify a sample by GETting /subscribers?tags[]=imported after each batch and resubmit failures as individual POSTs for error diagnosis.
Subscriber attribute sync from external system: On a lifecycle event such as a plan upgrade, PUT /subscribers/{subscriber_id} with updated custom_fields and a tags payload. Include remove_tags in the same request to atomically swap tags. Note that custom_fields are merged, not replaced - pass null explicitly to clear a field value.
Unsubscribe event propagation to CRM: Configure a webhook under Settings > Integrations > Webhooks targeting your endpoint and subscribe to subscriber.unsubscribed_globally. Extract the subscriber email from the payload and update CRM opt-out status. Respond with HTTP 200 within 10 seconds and implement idempotency on the event id field, as Drip will retry on non-2xx responses.
Bulk import and tag new subscribers
- Chunk subscriber list into batches of up to 1,000 records.
- POST each batch to /v2/{account_id}/subscribers/batches with email, name, custom_fields, and tags in the payload.
- Receive HTTP 201; wait briefly then GET /v2/{account_id}/subscribers?tags[]=imported to verify records were created.
- Handle any missing records by re-submitting individual POSTs to /v2/{account_id}/subscribers for error diagnosis.
Watch out for: Batch endpoint does not return per-record errors. Always verify a sample of records after batch import.
Sync subscriber status changes (e.g., plan upgrade) from external system
- On plan upgrade event in external system, PUT /v2/{account_id}/subscribers/{subscriber_id} with updated custom_fields (e.g., {"plan": "pro"}) and new tags (e.g., ["pro-customer"]).
- Include remove_tags in the same payload to remove old plan tags (e.g., ["free-customer"]).
- Confirm response contains updated custom_fields and tag list.
Watch out for: custom_fields are merged not replaced; explicitly pass null to clear a field value.
Listen for unsubscribe events and update external CRM
- Configure a webhook in Drip Settings > Integrations > Webhooks pointing to your endpoint.
- Subscribe to the subscriber.unsubscribed_globally event.
- On receipt of webhook payload, extract subscriber email and update CRM opt-out status.
- Respond with HTTP 200 within 10 seconds to acknowledge receipt.
Watch out for: Drip may retry webhook delivery on non-2xx responses. Implement idempotency using the event's id field to avoid duplicate CRM updates.
Why building this yourself is a trap
The primary API trap is the batch endpoint's silent failure mode: POST /subscribers/batches always returns HTTP 201 with an empty body regardless of per-record outcomes. Teams that treat the 201 as confirmation of successful writes will silently drop records.
A secondary trap is the upsert behavior of the single-subscriber POST - it matches on email and updates the existing record rather than creating a duplicate, which will overwrite fields if the payload is not scoped carefully. Deletion is permanent and irreversible with no soft-delete or recovery endpoint.
Finally, the API token auth uses Basic Auth with an empty password string rather than a Bearer token, which is a common misconfiguration point when integrating with HTTP client libraries that default to Bearer schemes.
Automate Drip 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.