Bulk import contacts

Import up to 250 contacts in one request. Asynchronous: the response returns a batchId and a successful response only means the import was accepted, not finished — poll GET /import/info with the batchId to confirm it completed before relying on the results. Contacts are matched by email (or by email_hash): existing contacts are updated and new ones are created, so an import is safe when some of the contacts already exist. Lists and tags are set per contact: use each contact's subscribe array to add it to lists, and its tags array to tag it (tags that do not exist are created). There is no request-level list id; one sent outside a contact is ignored. Contacts added to a list this way get list status active. A list that does not exist is skipped quietly. If the contact is already on the list as unconfirmed, previously bounced, previously unsubscribed, or is on the exclusion list, the subscription is skipped and the row is recorded as a partial success: the contact itself is still created or updated, GET /import/info lists it under success, and a callback with detailed_results reports it as a failure with code 104, 116 or 130. Check list membership if it matters. Sending the exact same request body again while the first import of it is still running (or for up to 24 hours if it never finishes) is rejected with 400: the message starts with "Failed to persist the JSON payload" and failureReasons[0] starts with "Duplicate request detected". Change the body or wait rather than retrying an accepted request unchanged. The status endpoints report failed rows by email only; to receive the reason a row failed, register a callback with detailed_results. Requires the contact import permission. It is the preferred way to create or update many contacts at once, with or without lists and tags.

Using the Bulk Contact Importer API

With ActiveCampaign, you can use the Bulk Contact Importer API to upload large numbers of contacts into your account with just one API call.

Take Note:

  • The Bulk Contact Importer API is available on all ActiveCampaign plans
  • This feature is for advanced users who are familiar with the API and are comfortable using it to import contacts
📘

Use exclude_automations to skip running automations on imports

Using this parameter will improve the import time.

If exclude_automations: true is included in the payload, automations will NOT run when bulk importing contacts.

By default, automations will run when doing a bulk import.

About the Bulk Contact Importer API

The bulk contact importer is designed to perform operations on large quantities of contacts at one time. These operations include:

  • Creating new contacts
  • Updating existing contacts
  • Setting fields on contacts
  • Tagging contacts
  • Adding contacts to lists
🚧

Note for less than 10 contacts:

The Bulk Contact Importer is not designed to support frequent operations involving a small number of contacts or to sync updates in real-time from an external database. Instead, contact updates should be collected into larger batches and imported as a group. Contacts can be imported in batches of up to 250 items.

If you need to upload 10 or fewer contacts at a time, please use the Contacts functionality of our ActiveCampaign API V3.

Rate limiting

The bulk importer process is optimized for the upload of larger lists of contacts. Running frequent requests that involve a small number of contacts requires significant overhead, and can affect the importer’s performance. As a result, we enforce the following rate limits on the Bulk Contact Importer API:

  • For requests containing a single contact, there is a limit of 20 requests per minute
  • For requests containing multiple contacts, there is a limit of 100 requests per minute

We recommend that all users attempt to batch updates into groups as large as possible before starting an import.

Payload size limiting

The maximum payload size of a single bulk_import request must be less than less than 400K bytes (399,999 bytes or less).

"Excluded" Emails will be Skipped

Any emails emails contained in a bulk import will be compared against your "Email Exclusions" list.
If an email is important that is in your exclusion list, the Bulk Import API response will be 200 "Success" but will contain:

{"success":5,"queued_contacts":2,"batchId":"59f14dbe-a54f-4d84-a3f6-a13849Aa3eb","message":"Contact import queued"}\`

A call to import status will show the skipped emails:

{"status":"completed","success":["5232"],"failure":["[email protected]", "[email protected]"]}
🚧

Why Bulk Imports Fail

Examples of invalid formatting that will fail an import:

  • Username before @ must have a length of at least 1 character.
  • Address does not contain special characters such as " ( ) , : ; < > @ [ \ ]
  • An @ symbol is required between the email username and domain url.
  • A . is required in the address's domain url, and there must be at least two letters after the . containing no special characters.

Other reasons for failure:

  • The imported contacts may not exceed your account limit
  • The contact must have an email address
  • The contact’s email address must not be on an exclusion list
  • The contact’s email must not be on a list of bounced email addresses
  • The contact must not have unsubscribed to a list that the import would add them to
{
  "contacts": [
    {
      "email": "[email protected]",
      "first_name": "Jane",
      "last_name": "Doe",
      "phone": "123-456-7890",
      "customer_acct_name": "ActiveCampaign",
      "tags": [
        "dictumst", "aliquam", "augue quam", "sollicitudin rutrum"
      ],
      "fields": [
        { "id": 1, "value": "foo" },
        { "id": 2, "value": "||foo||bar||baz||" }
      ],
      "subscribe": [
        { "listid": 1 },
        { "listid": 2 }
      ],
      "unsubscribe": [
        { "listid": 3 }
      ]
    }
  ],
  "callback": {
    "url": "www.your_web_server.com",
    "requestType": "POST",
    "detailed_results": "true",
    "params": [
      { "key": "", "value": "" }
    ],
    "headers": [
      { "key": "", "value": "" }
    ]
  }
}
{
   "Success":1,
   "queued_contacts":1,
   "batchId":"0641fbdd-f62f-4c2c-ba02-3a83d5d11ac9",
   "message":"Contact import queued"
}
❗️

400 Errors can take two forms

If the error occurs during contact creation, the error will return failureReasons as a list of objects, and a nested contact indicating which Contact the failure occurred with.

If the failure is more general (for example, rate limit exceeded), the error will return failureReasons as a list of strings.

Body Params
contacts
array of objects
required

The contacts to import: at least 1 and at most 250 per request. A request with more than 250 is rejected with 400 and nothing is queued. Each contact needs an email, or an email_hash of an existing contact, or a phone when enable_contacts_without_email is true. For fewer than about 10 contacts, POST /contact/sync per contact is simpler.

contacts*
boolean
Defaults to false

Set to true so the import does not start automations for the imported contacts. Must be a JSON boolean; any other type is rejected with 422.

boolean

Set to false to suppress webhooks for the imported contacts. When omitted, contact, custom field and tag webhooks are sent but list subscription webhooks are not; set to true to send list webhooks as well. Must be a JSON boolean; any other type is rejected with 422.

boolean
Defaults to false

Set to true to allow contacts that have a phone number but no email. Each such contact is identified by its phone. Must be a JSON boolean; any other type is rejected with 422.

boolean
Defaults to false

Set to true so custom field values that do not match an existing option of a dropdown, list box, radio or checkbox field are not added as new options.

string

A date and time after which the import's per-contact status records may be removed. Any format PHP's strtotime understands, for example 2026-12-31 or +30 days. An unparseable value is rejected with 422.

callback
object

Optional. A request the importer makes to your endpoint when the import finishes. This is the only way to receive per-row failure reasons for an API import.

Headers
string
Responses

Language
Credentials
Header
URL
LoadingLoading…
Response
Choose an example:
application/json