curl --request POST \
--url https://brew.new/api/v1/contacts/import-csv \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"csv": "email,firstName,lastName\nada@example.com,Ada,Lovelace\ngrace@example.com,Grace,Hopper",
"mapping": {
"email": "email",
"firstName": "firstName",
"lastName": "lastName"
}
}
'import requests
url = "https://brew.new/api/v1/contacts/import-csv"
payload = {
"csv": "email,firstName,lastName
ada@example.com,Ada,Lovelace
grace@example.com,Grace,Hopper",
"mapping": {
"email": "email",
"firstName": "firstName",
"lastName": "lastName"
}
}
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({
csv: 'email,firstName,lastName\nada@example.com,Ada,Lovelace\ngrace@example.com,Grace,Hopper',
mapping: {email: 'email', firstName: 'firstName', lastName: 'lastName'}
})
};
fetch('https://brew.new/api/v1/contacts/import-csv', 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://brew.new/api/v1/contacts/import-csv",
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([
'csv' => 'email,firstName,lastName
ada@example.com,Ada,Lovelace
grace@example.com,Grace,Hopper',
'mapping' => [
'email' => 'email',
'firstName' => 'firstName',
'lastName' => 'lastName'
]
]),
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://brew.new/api/v1/contacts/import-csv"
payload := strings.NewReader("{\n \"csv\": \"email,firstName,lastName\\nada@example.com,Ada,Lovelace\\ngrace@example.com,Grace,Hopper\",\n \"mapping\": {\n \"email\": \"email\",\n \"firstName\": \"firstName\",\n \"lastName\": \"lastName\"\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://brew.new/api/v1/contacts/import-csv")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"csv\": \"email,firstName,lastName\\nada@example.com,Ada,Lovelace\\ngrace@example.com,Grace,Hopper\",\n \"mapping\": {\n \"email\": \"email\",\n \"firstName\": \"firstName\",\n \"lastName\": \"lastName\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/contacts/import-csv")
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 \"csv\": \"email,firstName,lastName\\nada@example.com,Ada,Lovelace\\ngrace@example.com,Grace,Hopper\",\n \"mapping\": {\n \"email\": \"email\",\n \"firstName\": \"firstName\",\n \"lastName\": \"lastName\"\n }\n}"
response = http.request(request)
puts response.read_body{
"summary": {
"inserted": 2,
"updated": 0,
"failed": 0,
"skipped": 0
},
"fieldsCreated": [],
"errors": [],
"warnings": []
}{
"summary": {
"inserted": 1,
"updated": 0,
"failed": 1,
"skipped": 2
},
"fieldsCreated": [],
"errors": [
{
"email": "dupe@example.com",
"code": "DUPLICATE_EMAILS_IN_BATCH",
"message": "Email 'dupe@example.com' appears 2 times in batch."
}
],
"warnings": []
}{
"error": {
"code": "INVALID_REQUEST",
"type": "invalid_request",
"message": "Request validation failed.",
"suggestion": "Fix the field reported in `param` and retry.",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "csv"
}
}{
"error": {
"code": "INVALID_API_KEY",
"type": "authentication_error",
"message": "The provided API key is invalid.",
"suggestion": "Check the API key format and retry with a valid active key.",
"docs": "https://docs.brew.new/api-reference/api/authentication"
}
}{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"type": "authorization_error",
"message": "The caller does not have the required permission.",
"suggestion": "Use an API key or session with the required permission.",
"docs": "https://docs.brew.new/api-reference/api/authentication",
"param": "contacts"
}
}{
"error": {
"code": "RATE_LIMITED",
"type": "rate_limit",
"message": "Too many requests.",
"suggestion": "Wait for the retry window before sending another request.",
"docs": "https://docs.brew.new/api-reference/api/rate-limits",
"retryAfter": 42
}
}{
"error": {
"code": "INTERNAL_ERROR",
"type": "internal_error",
"message": "An unexpected error occurred.",
"suggestion": "Retry the request. If it keeps failing, contact support.",
"docs": "https://docs.brew.new/api-reference/api/errors"
}
}Bulk-import contacts from CSV
Parse a raw CSV string and upsert the rows as contacts (≤ 1000). By default each column header maps to a field of the same name (email is required); pass mapping to remap columns explicitly. Rows with a missing/invalid email are SKIPPED (counted in summary.skipped), not errored; disposable-domain rows are imported with validationStatus: "risky" (deliverable but flagged). Returns the same { summary, fieldsCreated, errors, warnings } as batch-create, 207 when some rows fail validation.
curl --request POST \
--url https://brew.new/api/v1/contacts/import-csv \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"csv": "email,firstName,lastName\nada@example.com,Ada,Lovelace\ngrace@example.com,Grace,Hopper",
"mapping": {
"email": "email",
"firstName": "firstName",
"lastName": "lastName"
}
}
'import requests
url = "https://brew.new/api/v1/contacts/import-csv"
payload = {
"csv": "email,firstName,lastName
ada@example.com,Ada,Lovelace
grace@example.com,Grace,Hopper",
"mapping": {
"email": "email",
"firstName": "firstName",
"lastName": "lastName"
}
}
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({
csv: 'email,firstName,lastName\nada@example.com,Ada,Lovelace\ngrace@example.com,Grace,Hopper',
mapping: {email: 'email', firstName: 'firstName', lastName: 'lastName'}
})
};
fetch('https://brew.new/api/v1/contacts/import-csv', 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://brew.new/api/v1/contacts/import-csv",
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([
'csv' => 'email,firstName,lastName
ada@example.com,Ada,Lovelace
grace@example.com,Grace,Hopper',
'mapping' => [
'email' => 'email',
'firstName' => 'firstName',
'lastName' => 'lastName'
]
]),
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://brew.new/api/v1/contacts/import-csv"
payload := strings.NewReader("{\n \"csv\": \"email,firstName,lastName\\nada@example.com,Ada,Lovelace\\ngrace@example.com,Grace,Hopper\",\n \"mapping\": {\n \"email\": \"email\",\n \"firstName\": \"firstName\",\n \"lastName\": \"lastName\"\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://brew.new/api/v1/contacts/import-csv")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"csv\": \"email,firstName,lastName\\nada@example.com,Ada,Lovelace\\ngrace@example.com,Grace,Hopper\",\n \"mapping\": {\n \"email\": \"email\",\n \"firstName\": \"firstName\",\n \"lastName\": \"lastName\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://brew.new/api/v1/contacts/import-csv")
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 \"csv\": \"email,firstName,lastName\\nada@example.com,Ada,Lovelace\\ngrace@example.com,Grace,Hopper\",\n \"mapping\": {\n \"email\": \"email\",\n \"firstName\": \"firstName\",\n \"lastName\": \"lastName\"\n }\n}"
response = http.request(request)
puts response.read_body{
"summary": {
"inserted": 2,
"updated": 0,
"failed": 0,
"skipped": 0
},
"fieldsCreated": [],
"errors": [],
"warnings": []
}{
"summary": {
"inserted": 1,
"updated": 0,
"failed": 1,
"skipped": 2
},
"fieldsCreated": [],
"errors": [
{
"email": "dupe@example.com",
"code": "DUPLICATE_EMAILS_IN_BATCH",
"message": "Email 'dupe@example.com' appears 2 times in batch."
}
],
"warnings": []
}{
"error": {
"code": "INVALID_REQUEST",
"type": "invalid_request",
"message": "Request validation failed.",
"suggestion": "Fix the field reported in `param` and retry.",
"docs": "https://docs.brew.new/api-reference/api/errors",
"param": "csv"
}
}{
"error": {
"code": "INVALID_API_KEY",
"type": "authentication_error",
"message": "The provided API key is invalid.",
"suggestion": "Check the API key format and retry with a valid active key.",
"docs": "https://docs.brew.new/api-reference/api/authentication"
}
}{
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"type": "authorization_error",
"message": "The caller does not have the required permission.",
"suggestion": "Use an API key or session with the required permission.",
"docs": "https://docs.brew.new/api-reference/api/authentication",
"param": "contacts"
}
}{
"error": {
"code": "RATE_LIMITED",
"type": "rate_limit",
"message": "Too many requests.",
"suggestion": "Wait for the retry window before sending another request.",
"docs": "https://docs.brew.new/api-reference/api/rate-limits",
"retryAfter": 42
}
}{
"error": {
"code": "INTERNAL_ERROR",
"type": "internal_error",
"message": "An unexpected error occurred.",
"suggestion": "Retry the request. If it keeps failing, contact support.",
"docs": "https://docs.brew.new/api-reference/api/errors"
}
}Authorizations
Send your Brew API key as Authorization: Bearer brew_xxx.
Body
1 - 5000000Show child attributes
Show child attributes
Optional deliverability check on ingestion. When true, each address is validated with the provider (2 credits per address, charged on success) and the verdict is saved to the contact’s validationStatus. Submissions above the inline cap (100 addresses) upsert first and validate as a background job, returning a validationJobId.
Was this page helpful?