---
title: "Add multiple contacts in bulk"
method: POST
path: "/v2/contacts/bulk"
tags: ["contacts-v2"]
---

# Add multiple contacts in bulk

`POST /v2/contacts/bulk`

## Request

- Base URL: `https://api.tracklysms.com/api`
- URL: `https://api.tracklysms.com/api/v2/contacts/bulk`
- Auth: API key in header `X-Api-Key`

Maximum 1000 contacts per request.

## Request body

- BulkContactRequest
  - `contacts` ContactRequest[], required: Array of contacts (max 1000)
    - `ad_network_id` string: Ad network campaign/account ID
    - `ad_network_source` string: Ad network source (e.g. adwords, facebook)
    - `consent_language` string: Exact disclosure text shown to consumer at consent
    - `consent_method` string: How consent was obtained (web_form, sms_keyword, mobile_app, verbal, written, api_with_proof, double_opt_in). Optional; stored on the contact for TCPA compliance when a valid method is provided, null otherwise. Invalid or verbal values are ignored, not rejected.
    - `consent_source` string: URL or description of consent capture point. Defaults to url when omitted.
    - `custom_fields` object: Custom fields key-value pairs
    - `double_optin` boolean: When true, the contact is held pending confirmation (not sendable, not enrolled in journeys) and sent the confirmation message from optin_configuration_id; they activate only after replying Y/YES/CONFIRM within 24h. Requires optin_configuration_id (a double-opt-in config on the same list). For DOI contacts, journey enrollment happens at confirmation and follows the opt-in configuration's journey settings, not this request's use_journeys. Defaults false — omit for standard single opt-in.
    - `ip_address` string: IP address for consent tracking
    - `list_number` string, required: Sending list phone number in E.164 format
    - `optin_configuration_id` string: Double-opt-in opt-in configuration to drive confirmation. Required when double_optin is true.
    - `override_custom_fields` boolean: Replace custom fields entirely instead of merging
    - `phone_number` string, required: Contact phone number in E.164 format
    - `screen_litigator` boolean: When true, the contact phone number is screened for litigator / DNC status (Blacklist Alliance) and the result is badged onto the contact profile asynchronously. Independent of use_validation. The contact is always created and never rejected on a hit — suppress litigators downstream via the is_litigator audience filter. Per non-cached screen billed at \$0.015. Requires an active payment method (HTTP 402 if not enabled). Single-contact endpoint only for now.
    - `signup_date` string, date-time: Initial signup date for a new membership (ISO datetime UTC, defaults to now; existing membership dates are preserved)
    - `skip_journey_if_exists` boolean: Skip journey enrollment if ListContact already exists
    - `url` string: Signup URL for consent tracking
    - `use_journeys` boolean: Enroll contact in matching journeys
    - `use_validation` boolean: When true, the contact phone number is validated (LRN + DNC) and the validation result is attached to the response and persisted to the contact profile. The contact is created either way — invalid or unreachable numbers are accepted and stored, then can be segmented out of campaigns using the validation_status audience filter. Per non-cached lookup is billed at \$0.003 (rolled up monthly). Requires an active payment method (HTTP 402 if not enabled). There is no `reject_on_fail` flag by design: the audience builder is the right segmentation surface for excluding invalid numbers from sends, and a per-API rejection flag would force callers to choose between two divergent code paths instead of treating accept-and-segment as the universal contract.
    - `user_agent` string: Browser/device user agent string at consent

## Response `201`

Contacts added

- BulkContactResponse
  - `contacts_created` integer: Number of new Contact records created
  - `error_count` integer: Number of contacts that failed
  - `errors` object[]: Array of error details
  - `journeys_enrolled` integer: Total journey enrollments
  - `list_contacts_created` integer: Number of new ListContact records created
  - `success_count` integer: Number of contacts successfully added
  - `warnings` string[]: Warnings about missed configuration (e.g. active journeys without use_journeys flag)

## Other responses

- `400`: Validation error
- `401`: Authentication error
- `413`: Too many contacts

---

[API](https://skmtc.dev/tracklysms/apis/trackly-sms-api.md) · [All operations](https://skmtc.dev/tracklysms/apis/trackly-sms-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tracklysms/apis/trackly-sms-api/revisions/ac1ad22cb2a9?raw)
