Add Recipients
curl --request POST \
--url https://api.example.com/email/lists/{list_id}/recipients \
--header 'Content-Type: application/json' \
--data '
{
"emails": [
{}
]
}
'import requests
url = "https://api.example.com/email/lists/{list_id}/recipients"
payload = { "emails": [{}] }
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({emails: [{}]})
};
fetch('https://api.example.com/email/lists/{list_id}/recipients', 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.example.com/email/lists/{list_id}/recipients",
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([
'emails' => [
[
]
]
]),
CURLOPT_HTTPHEADER => [
"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.example.com/email/lists/{list_id}/recipients"
payload := strings.NewReader("{\n \"emails\": [\n {}\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
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.example.com/email/lists/{list_id}/recipients")
.header("Content-Type", "application/json")
.body("{\n \"emails\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/email/lists/{list_id}/recipients")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"emails\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"added": 123,
"skipped_existing": 123,
"invalid": [
"<string>"
]
}Mailing Lists
Add Recipients
Bulk-add email addresses to a mailing list
POST
/
email
/
lists
/
{list_id}
/
recipients
Add Recipients
curl --request POST \
--url https://api.example.com/email/lists/{list_id}/recipients \
--header 'Content-Type: application/json' \
--data '
{
"emails": [
{}
]
}
'import requests
url = "https://api.example.com/email/lists/{list_id}/recipients"
payload = { "emails": [{}] }
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({emails: [{}]})
};
fetch('https://api.example.com/email/lists/{list_id}/recipients', 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.example.com/email/lists/{list_id}/recipients",
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([
'emails' => [
[
]
]
]),
CURLOPT_HTTPHEADER => [
"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.example.com/email/lists/{list_id}/recipients"
payload := strings.NewReader("{\n \"emails\": [\n {}\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
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.example.com/email/lists/{list_id}/recipients")
.header("Content-Type", "application/json")
.body("{\n \"emails\": [\n {}\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/email/lists/{list_id}/recipients")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"emails\": [\n {}\n ]\n}"
response = http.request(request)
puts response.read_body{
"added": 123,
"skipped_existing": 123,
"invalid": [
"<string>"
]
}Append-only bulk add: up to 1000 entries per request, each a plain email address or an object with
API tokens are created in the Dashboard under API Keys. The list must belong to the token’s project.
email plus optional name, external_id and attributes.
Each address is validated individually; invalid entries are reported in the invalid array rather than failing the batch. Addresses are deduplicated case-insensitively, and addresses already on the list — active, removed or unsubscribed — are counted in skipped_existing and left untouched, so re-syncing never resurrects an opt-out. A list holds at most 500 active recipients.
To keep a list in sync with another system (update names, reactivate removed contacts, change an address by
external_id), use Upsert Recipients instead. This endpoint never modifies an existing recipient.Authentication
This endpoint requires an API token passed as a Bearer token in theAuthorization header.
Authorization: Bearer YOUR_API_TOKEN
Path Parameters
string
required
The mailing list’s unique identifier (UUID). List ids come from List Mailing Lists.
Body
(string | object)[]
required
Between 1 and 1000 entries. A plain string is an email address; an object carries
email (required) plus optional name, external_id and attributes — the same shape as Upsert Recipients.Request
curl -X POST "https://api.shipstar.ai/api/v1/email/lists/a1b2c3d4-e5f6-7890-abcd-ef1234567890/recipients" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"emails": ["one@example.com", {"email": "two@example.com", "name": "Sam", "external_id": "usr_42"}]}'
const listId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
const response = await fetch(`https://api.shipstar.ai/api/v1/email/lists/${listId}/recipients`, {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
emails: ['one@example.com', 'two@example.com']
})
});
const result = await response.json();
import requests
list_id = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
response = requests.post(
f'https://api.shipstar.ai/api/v1/email/lists/{list_id}/recipients',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={'emails': ['one@example.com', 'two@example.com']}
)
result = response.json()
Response
integer
required
Number of addresses added to the list
integer
required
Number of entries already on the list in any status (including unsubscribed ones, which are never re-activated)
string[]
required
Addresses that failed validation and were not added
Example Response
200
{
"added": 2,
"skipped_existing": 1,
"invalid": ["not-an-email"]
}
Errors
| Status | Description |
|---|---|
| 400 | Adding the batch would exceed the 500 active recipients per list limit |
| 401 | Invalid or expired API token |
| 404 | Mailing list not found in the token’s project |
| 422 | Invalid request body (e.g. empty emails array or more than 1000 items) |
Rate Limits
This endpoint is limited to 30 requests per minute per IP.Was this page helpful?