curl --request POST \
--url https://api.reply.io/v3/contacts/import \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"items": [
{
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
"company": "Acme"
},
{
"email": "jane@example.com",
"firstName": "Jane",
"lastName": "Smith",
"company": "Globex"
}
],
"options": {
"overwriteExisting": false,
"skipExisting": true,
"detectTimeZone": true,
"listIds": [
10,
20
]
}
}
'import requests
url = "https://api.reply.io/v3/contacts/import"
payload = {
"items": [
{
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
"company": "Acme"
},
{
"email": "jane@example.com",
"firstName": "Jane",
"lastName": "Smith",
"company": "Globex"
}
],
"options": {
"overwriteExisting": False,
"skipExisting": True,
"detectTimeZone": True,
"listIds": [10, 20]
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
items: [
{email: 'john@example.com', firstName: 'John', lastName: 'Doe', company: 'Acme'},
{
email: 'jane@example.com',
firstName: 'Jane',
lastName: 'Smith',
company: 'Globex'
}
],
options: {
overwriteExisting: false,
skipExisting: true,
detectTimeZone: true,
listIds: [10, 20]
}
})
};
fetch('https://api.reply.io/v3/contacts/import', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.reply.io/v3/contacts/import",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'items' => [
[
'email' => 'john@example.com',
'firstName' => 'John',
'lastName' => 'Doe',
'company' => 'Acme'
],
[
'email' => 'jane@example.com',
'firstName' => 'Jane',
'lastName' => 'Smith',
'company' => 'Globex'
]
],
'options' => [
'overwriteExisting' => false,
'skipExisting' => true,
'detectTimeZone' => true,
'listIds' => [
10,
20
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.reply.io/v3/contacts/import"
payload := strings.NewReader("{\n \"items\": [\n {\n \"email\": \"john@example.com\",\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"company\": \"Acme\"\n },\n {\n \"email\": \"jane@example.com\",\n \"firstName\": \"Jane\",\n \"lastName\": \"Smith\",\n \"company\": \"Globex\"\n }\n ],\n \"options\": {\n \"overwriteExisting\": false,\n \"skipExisting\": true,\n \"detectTimeZone\": true,\n \"listIds\": [\n 10,\n 20\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.reply.io/v3/contacts/import")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"items\": [\n {\n \"email\": \"john@example.com\",\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"company\": \"Acme\"\n },\n {\n \"email\": \"jane@example.com\",\n \"firstName\": \"Jane\",\n \"lastName\": \"Smith\",\n \"company\": \"Globex\"\n }\n ],\n \"options\": {\n \"overwriteExisting\": false,\n \"skipExisting\": true,\n \"detectTimeZone\": true,\n \"listIds\": [\n 10,\n 20\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/contacts/import")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"items\": [\n {\n \"email\": \"john@example.com\",\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"company\": \"Acme\"\n },\n {\n \"email\": \"jane@example.com\",\n \"firstName\": \"Jane\",\n \"lastName\": \"Smith\",\n \"company\": \"Globex\"\n }\n ],\n \"options\": {\n \"overwriteExisting\": false,\n \"skipExisting\": true,\n \"detectTimeZone\": true,\n \"listIds\": [\n 10,\n 20\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"items": [
{
"id": 12345,
"status": "created",
"error": null
},
{
"id": 12346,
"status": "updated",
"error": null
},
{
"id": null,
"status": "failed",
"error": "Invalid data"
}
],
"added": 1,
"updated": 1,
"skipped": 0,
"failed": 1
}Import contacts
contacts:write scope (or a broader one that includes it).
Use this endpoint when you need to add or update many contacts in one call — for example when syncing contacts from another system. Each item requires firstName; the remaining fields are optional and use the same fields as the contact update model. An item with no firstName is not imported — it comes back with status “failed” and error “Invalid data”, while the response itself still has HTTP status 200. Use options to control deduplication, list assignment, and other import behavior: which keys identify an existing contact (email plus any of LinkedIn URL, phone, name, company, or custom fields), whether matched contacts are overwritten or skipped, whether previously deleted contacts are restored, and which lists or sequence the imported contacts are added to. The response reports how many contacts were added, updated, skipped, or failed, with a per-item outcome.
curl --request POST \
--url https://api.reply.io/v3/contacts/import \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"items": [
{
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
"company": "Acme"
},
{
"email": "jane@example.com",
"firstName": "Jane",
"lastName": "Smith",
"company": "Globex"
}
],
"options": {
"overwriteExisting": false,
"skipExisting": true,
"detectTimeZone": true,
"listIds": [
10,
20
]
}
}
'import requests
url = "https://api.reply.io/v3/contacts/import"
payload = {
"items": [
{
"email": "john@example.com",
"firstName": "John",
"lastName": "Doe",
"company": "Acme"
},
{
"email": "jane@example.com",
"firstName": "Jane",
"lastName": "Smith",
"company": "Globex"
}
],
"options": {
"overwriteExisting": False,
"skipExisting": True,
"detectTimeZone": True,
"listIds": [10, 20]
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
items: [
{email: 'john@example.com', firstName: 'John', lastName: 'Doe', company: 'Acme'},
{
email: 'jane@example.com',
firstName: 'Jane',
lastName: 'Smith',
company: 'Globex'
}
],
options: {
overwriteExisting: false,
skipExisting: true,
detectTimeZone: true,
listIds: [10, 20]
}
})
};
fetch('https://api.reply.io/v3/contacts/import', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.reply.io/v3/contacts/import",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'items' => [
[
'email' => 'john@example.com',
'firstName' => 'John',
'lastName' => 'Doe',
'company' => 'Acme'
],
[
'email' => 'jane@example.com',
'firstName' => 'Jane',
'lastName' => 'Smith',
'company' => 'Globex'
]
],
'options' => [
'overwriteExisting' => false,
'skipExisting' => true,
'detectTimeZone' => true,
'listIds' => [
10,
20
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.reply.io/v3/contacts/import"
payload := strings.NewReader("{\n \"items\": [\n {\n \"email\": \"john@example.com\",\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"company\": \"Acme\"\n },\n {\n \"email\": \"jane@example.com\",\n \"firstName\": \"Jane\",\n \"lastName\": \"Smith\",\n \"company\": \"Globex\"\n }\n ],\n \"options\": {\n \"overwriteExisting\": false,\n \"skipExisting\": true,\n \"detectTimeZone\": true,\n \"listIds\": [\n 10,\n 20\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.reply.io/v3/contacts/import")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"items\": [\n {\n \"email\": \"john@example.com\",\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"company\": \"Acme\"\n },\n {\n \"email\": \"jane@example.com\",\n \"firstName\": \"Jane\",\n \"lastName\": \"Smith\",\n \"company\": \"Globex\"\n }\n ],\n \"options\": {\n \"overwriteExisting\": false,\n \"skipExisting\": true,\n \"detectTimeZone\": true,\n \"listIds\": [\n 10,\n 20\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reply.io/v3/contacts/import")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"items\": [\n {\n \"email\": \"john@example.com\",\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"company\": \"Acme\"\n },\n {\n \"email\": \"jane@example.com\",\n \"firstName\": \"Jane\",\n \"lastName\": \"Smith\",\n \"company\": \"Globex\"\n }\n ],\n \"options\": {\n \"overwriteExisting\": false,\n \"skipExisting\": true,\n \"detectTimeZone\": true,\n \"listIds\": [\n 10,\n 20\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"items": [
{
"id": 12345,
"status": "created",
"error": null
},
{
"id": 12346,
"status": "updated",
"error": null
},
{
"id": null,
"status": "failed",
"error": "Invalid data"
}
],
"added": 1,
"updated": 1,
"skipped": 0,
"failed": 1
}Authorizations
Authenticate every request with a Bearer token. Pass your Reply API key in the
Authorization header:
Authorization: Bearer <your-api-key>
Get your API key from the Reply dashboard: Settings → API Key.
Body
Import contacts by providing an array of contact objects with optional import settings
Array of contact objects to import. Uses the same fields as the contact patch model,
except that firstName is required for every item. An item with no firstName is not imported — it is returned
with status: failed and error: "Invalid data". Supplying only lastName does not satisfy the
requirement.
1Show child attributes
Show child attributes
Import settings
Show child attributes
Show child attributes
Response
Contacts import processed successfully
Summary of a processed contacts import request with per-item results