Skip to main content
POST
/
v1
/
contacts
/
import-csv
Bulk-import contacts from CSV
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

Authorization
string
header
default:Bearer brew_your_api_key
required

Send your Brew API key as Authorization: Bearer brew_xxx.

Body

application/json
csv
string
required
Required string length: 1 - 5000000
mapping
object
validate
boolean

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.

Response

Import summary (all rows valid).

summary
object
required
fieldsCreated
string[]
required
errors
object[]
required
warnings
object[]
required
validation
object
validationJobId
string