Bulk-imports contacts in a single API call. Existing contacts matched by email are silently skipped (ON CONFLICT DO NOTHING). Requires editor role or higher.
Parameters
Array of contact objects. Maximum 10,000 per call. Each object must include at least one of email or phone — rows missing both are silently dropped.| Field | Type | Description |
|---|
email | string | Email address |
phone | string | Phone number in international format |
first_name | string | First name |
last_name | string | Last name |
Request
curl -sS -X POST "https://api.arowana.app/v1/contacts/import" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contacts": [
{
"email": "jana@example.com",
"phone": "+4917612345678",
"first_name": "Jana",
"last_name": "Müller"
},
{
"email": "max@example.com",
"first_name": "Max",
"last_name": "Schmidt"
}
]
}'
Response
200 OK
400 Bad Request
422 Limit exceeded
imported reflects only newly written contacts. Duplicates matched by email are not counted.{ "error": "validation_error", "message": "contacts array must not be empty." }
{ "error": "limit_exceeded", "message": "Maximum 10,000 contacts per import call." }
Chunking large lists
For files larger than 10,000 rows, split into chunks and call the endpoint sequentially:
async function importAll(contacts) {
const CHUNK = 10_000;
for (let i = 0; i < contacts.length; i += CHUNK) {
const chunk = contacts.slice(i, i + CHUNK);
const res = await fetch("https://api.arowana.app/v1/contacts/import", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ contacts: chunk }),
});
const { imported } = await res.json();
console.log(`Chunk ${i / CHUNK + 1}: ${imported} imported`);
}
}
After a large import, use campaign segments or tags to target only newly added contacts in your first send.