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": []
}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.
CSV cells are text: a column mapped onto an EXISTING field is coerced to that field’s type (dates → epoch ms, 1,234 → 1234, yes/no → booleans), and a cell that cannot be coerced ($49 in a number column) fails ITS row with a per-row errors[] entry (code: FIELD_TYPE_MISMATCH, field) while the other rows still land. An undeclared column is created as a string field unless every value is an ISO date (→ date); predeclare number / bool columns with POST /v1/fields.
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": []
}Authorizations
Send your Brew API key as Authorization: Bearer brew_xxx.
Headers
The brand this request acts on. REQUIRED for organization-scoped credentials (otherwise 400 BRAND_ID_REQUIRED — there is no default brand); list ids with GET /v1/brands. Brand-scoped credentials may omit it, and sending a different brand returns 403 BRAND_SCOPE_MISMATCH. A brand outside your organization returns 404 BRAND_NOT_FOUND.
1 - 64Body
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.
Consent record stamped on every imported row (typically { "source": "import" }).
Show child attributes
Show child attributes
Was this page helpful?