Stitchflow
ConvertKit logo

ConvertKit User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 9, 2026

Summary and recommendation

ConvertKit's v4 API (base URL: `https://api.convertkit.com/v4`) supports Bearer token auth via API Secret or OAuth 2.0. OAuth is preferred for third-party integrations; access tokens do not expire by default but can be revoked, so a refresh token flow should be implemented for long-lived integrations.

The API covers subscriber lifecycle management, tag operations, custom fields, and webhook registration - but has no SCIM endpoint and no team-member provisioning surface whatsoever.

For identity graph use cases, subscriber state (`active`, `inactive`, `bounced`, `complained`, `cancelled`) combined with tags and custom fields is the closest available construct for modeling user identity and entitlements within ConvertKit.

API quick reference

Has user APIYes
Auth methodAPI Key (Bearer token) or OAuth 2.0 (v4)
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredN/A

Authentication

Auth method: API Key (Bearer token) or OAuth 2.0 (v4)

Setup steps

  1. For personal/server-side use: navigate to Settings > Advanced > API in your ConvertKit account and copy your API Secret.
  2. Pass the API Secret as a Bearer token in the Authorization header: 'Authorization: Bearer '.
  3. For OAuth 2.0 (third-party integrations): register an OAuth application at developers.convertkit.com, obtain client_id and client_secret.
  4. Redirect users to https://app.convertkit.com/oauth/authorize with response_type=code, client_id, redirect_uri, and scope parameters.
  5. Exchange the authorization code for an access token via POST to https://api.convertkit.com/oauth/token.
  6. Use the returned access_token as a Bearer token in subsequent API requests.

Required scopes

Scope Description Required for
public Read-only access to public account data Listing forms, sequences, tags
write_subscribers Create, update, and unsubscribe subscribers Subscriber create/update/delete operations
read_subscribers Read subscriber data including email and custom fields Listing and fetching subscriber details
write_forms Manage form subscriptions Adding subscribers to forms
write_tags Create tags and tag/untag subscribers Tag management operations

User object / data model

Field Type Description On create On update Notes
id integer Unique subscriber identifier auto-generated immutable Used as path parameter for subscriber-specific endpoints
email_address string Subscriber's email address required updatable Must be unique per account
first_name string Subscriber's first name optional updatable
state string Subscription state: active, inactive, bounced, complained, cancelled auto-set to active updatable via unsubscribe endpoint Cannot be set directly to 'active' via update; use re-subscribe flow
created_at ISO 8601 datetime Timestamp when subscriber was created auto-generated immutable
fields object Key-value map of custom field values keyed by field slug optional updatable Custom fields must be pre-created in the account before use
tagged_at ISO 8601 datetime Timestamp when a tag was applied (returned in tag-subscriber context) auto-generated on tag immutable Only present in tag-subscriber list responses

Core endpoints

List subscribers

  • Method: GET
  • URL: https://api.convertkit.com/v4/subscribers
  • Watch out for: Default page size is 500; use 'after' cursor from pagination.end_cursor for subsequent pages.

Request example

GET /v4/subscribers?per_page=100&status=active
Authorization: Bearer <api_secret>

Response example

{
  "subscribers": [{"id":1,"email_address":"user@example.com","state":"active","fields":{}}],
  "pagination": {"has_previous_page":false,"has_next_page":true,"start_cursor":"abc","end_cursor":"xyz"}
}

Get subscriber

  • Method: GET
  • URL: https://api.convertkit.com/v4/subscribers/{id}
  • Watch out for: Lookup by email is not supported on this endpoint; use GET /v4/subscribers?email_address= instead.

Request example

GET /v4/subscribers/123456
Authorization: Bearer <api_secret>

Response example

{
  "subscriber": {
    "id": 123456,
    "email_address": "user@example.com",
    "state": "active",
    "fields": {"last_name": "Smith"}
  }
}

Create/update subscriber (upsert)

  • Method: POST
  • URL: https://api.convertkit.com/v4/subscribers
  • Watch out for: This endpoint upserts: if the email already exists, it updates the subscriber rather than creating a duplicate.

Request example

POST /v4/subscribers
Authorization: Bearer <api_secret>
{"email_address":"user@example.com","first_name":"Jane","fields":{"plan":"pro"}}

Response example

{
  "subscriber": {
    "id": 123456,
    "email_address": "user@example.com",
    "state": "active"
  }
}

Update subscriber

  • Method: PUT
  • URL: https://api.convertkit.com/v4/subscribers/{id}
  • Watch out for: Custom fields not included in the request are NOT cleared; only provided fields are updated.

Request example

PUT /v4/subscribers/123456
Authorization: Bearer <api_secret>
{"first_name":"Jane","fields":{"plan":"enterprise"}}

Response example

{
  "subscriber": {
    "id": 123456,
    "email_address": "user@example.com",
    "first_name": "Jane"
  }
}

Unsubscribe subscriber

  • Method: POST
  • URL: https://api.convertkit.com/v4/subscribers/{id}/unsubscribe
  • Watch out for: Unsubscribing sets state to 'inactive'. There is no API endpoint to reactivate an unsubscribed contact.

Request example

POST /v4/subscribers/123456/unsubscribe
Authorization: Bearer <api_secret>

Response example

{
  "subscriber": {
    "id": 123456,
    "state": "inactive"
  }
}

Tag a subscriber

  • Method: POST
  • URL: https://api.convertkit.com/v4/tags/{tag_id}/subscribers
  • Watch out for: Tag must exist before tagging; use POST /v4/tags to create it first.

Request example

POST /v4/tags/789/subscribers
Authorization: Bearer <api_secret>
{"email_address":"user@example.com"}

Response example

{
  "subscriber": {
    "id": 123456,
    "email_address": "user@example.com",
    "state": "active"
  }
}

Remove tag from subscriber

  • Method: DELETE
  • URL: https://api.convertkit.com/v4/tags/{tag_id}/subscribers/{subscriber_id}
  • Watch out for: Returns 204 on success with no body. Returns 404 if the tag-subscriber relationship does not exist.

Request example

DELETE /v4/tags/789/subscribers/123456
Authorization: Bearer <api_secret>

Response example

HTTP 204 No Content

List tags

  • Method: GET
  • URL: https://api.convertkit.com/v4/tags
  • Watch out for: Tags are account-level; all tags are returned without pagination (typically small sets).

Request example

GET /v4/tags
Authorization: Bearer <api_secret>

Response example

{
  "tags": [
    {"id": 789, "name": "customer", "created_at": "2024-01-01T00:00:00Z"}
  ]
}

Rate limits, pagination, and events

  • Rate limits: ConvertKit enforces per-account rate limits on API v4. Requests exceeding the limit receive HTTP 429 responses.
  • Rate-limit headers: Yes
  • Retry-After header: Yes
  • Rate-limit notes: HTTP 429 is returned when the limit is exceeded. The Retry-After header indicates when to resume. Bulk operations (e.g., bulk tag) count as single requests.
  • Pagination method: cursor
  • Default page size: 500
  • Max page size: 1000
  • Pagination pointer: after (cursor-based, returned as next cursor in response metadata)
Plan Limit Concurrent
All plans 120 requests per minute 0
  • Webhooks available: Yes
  • Webhook notes: ConvertKit supports webhooks (called 'Rules' or automation triggers) that POST event payloads to a configured URL when subscriber events occur.
  • Alternative event strategy: Webhooks can also be created programmatically via POST /v4/webhooks endpoint with event and target_url parameters.
  • Webhook events: subscriber.subscriber_activate, subscriber.subscriber_unsubscribe, subscriber.subscriber_bounce, subscriber.subscriber_complain, subscriber.form_subscribe, subscriber.course_subscribe, subscriber.course_complete, subscriber.link_click, subscriber.product_purchase, subscriber.tag_add, subscriber.tag_remove

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: N/A
  • Endpoint: Not documented

Limitations:

  • ConvertKit does not support SCIM provisioning on any plan.
  • No SSO (SAML/OIDC) integration is available.
  • Team member management is done via in-app invitations only.
  • Creator Pro plan allows unlimited team members but no automated provisioning.

Common scenarios

Three integration patterns cover the majority of programmatic use cases.

First, subscriber provisioning: POST /v4/subscribers upserts on email address - if the record exists, it updates rather than duplicates; custom field slugs not matching account configuration are silently dropped, which is a common source of data loss.

Second, deprovisioning: there is no hard-delete; POST /v4/subscribers/{id}/unsubscribe sets state to inactive and cannot be reversed via API - treat this as a one-way operation. Third, bulk sync: use GET /v4/subscribers with per_page=1000 and cursor-based pagination (after param from `pagination.

end_cursor); for incremental syncs on large accounts (100k+ subscribers), scope requests with the updated_afterfilter to avoid full-list pagination latency. Supplement polling with webhooks registered viaPOST /v4/webhooks for real-time delta events (subscriber.

subscriber_activate, subscriber. subscriber_unsubscribe, subscriber.

tag_add, subscriber. tag_remove`).

Provision a new user (subscriber) with custom attributes

  1. POST /v4/subscribers with email_address, first_name, and fields object containing custom field slugs and values.
  2. If the email already exists, the endpoint upserts and returns the existing subscriber with updated fields.
  3. Optionally POST /v4/tags/{tag_id}/subscribers to apply a role or plan tag to the subscriber.

Watch out for: Custom field slugs must match exactly what is configured in the ConvertKit account; mismatched slugs are silently dropped.

Deprovision a user (unsubscribe and remove tags)

  1. GET /v4/subscribers?email_address=user@example.com to retrieve the subscriber ID.
  2. DELETE /v4/tags/{tag_id}/subscribers/{subscriber_id} for each tag to remove.
  3. POST /v4/subscribers/{id}/unsubscribe to set state to 'inactive'.

Watch out for: Unsubscribing via API triggers the same suppression as a manual unsubscribe; the subscriber cannot be reactivated via API.

Sync subscriber list with an external system

  1. GET /v4/subscribers with status=active and per_page=1000 to fetch the first page.
  2. Use pagination.end_cursor as the 'after' parameter in subsequent requests until has_next_page is false.
  3. Compare returned subscriber records against the external system and issue PUT /v4/subscribers/{id} for any field discrepancies.
  4. Register a webhook via POST /v4/webhooks for subscriber.subscriber_activate and subscriber.subscriber_unsubscribe to receive real-time delta updates.

Watch out for: Full list pagination can be slow for large accounts (100k+ subscribers); use the updated_after filter parameter to scope incremental syncs.

Why building this yourself is a trap

The most common integration traps in ConvertKit's API are version mixing, silent field drops, and irreversible state changes. API v3 and v4 coexist with different auth schemes - v3 uses query-param API keys, v4 uses Bearer tokens; mixing versions in a single integration will produce inconsistent auth failures.

Custom fields must be pre-created in the account before they can be set; unknown slugs are accepted without error but the data is discarded.

The 120 req/min rate limit is per account API key, not per IP - shared keys across multiple services aggregate toward this ceiling and will produce HTTP 429 responses under concurrent load.

Finally, the unsubscribe endpoint is a suppression action: once triggered, the subscriber record persists but cannot be reactivated programmatically, which has direct implications for any identity graph that models ConvertKit subscriber state as a reversible entitlement.

Automate ConvertKit 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.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 9, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste