curl --request POST \
--url https://api.politicalcomms.com/v1/email/lists/import \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"source_url": "https://files.example.com/exports/august-donors.csv",
"name": "August donors",
"consent": {
"source": "donation_form",
"note": "Donate page opt-in checkbox"
}
}
'import requests
url = "https://api.politicalcomms.com/v1/email/lists/import"
payload = {
"source_url": "https://files.example.com/exports/august-donors.csv",
"name": "August donors",
"consent": {
"source": "donation_form",
"note": "Donate page opt-in checkbox"
}
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
source_url: 'https://files.example.com/exports/august-donors.csv',
name: 'August donors',
consent: {source: 'donation_form', note: 'Donate page opt-in checkbox'}
})
};
fetch('https://api.politicalcomms.com/v1/email/lists/import', 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.politicalcomms.com/v1/email/lists/import",
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([
'source_url' => 'https://files.example.com/exports/august-donors.csv',
'name' => 'August donors',
'consent' => [
'source' => 'donation_form',
'note' => 'Donate page opt-in checkbox'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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.politicalcomms.com/v1/email/lists/import"
payload := strings.NewReader("{\n \"source_url\": \"https://files.example.com/exports/august-donors.csv\",\n \"name\": \"August donors\",\n \"consent\": {\n \"source\": \"donation_form\",\n \"note\": \"Donate page opt-in checkbox\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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.politicalcomms.com/v1/email/lists/import")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"source_url\": \"https://files.example.com/exports/august-donors.csv\",\n \"name\": \"August donors\",\n \"consent\": {\n \"source\": \"donation_form\",\n \"note\": \"Donate page opt-in checkbox\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.politicalcomms.com/v1/email/lists/import")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source_url\": \"https://files.example.com/exports/august-donors.csv\",\n \"name\": \"August donors\",\n \"consent\": {\n \"source\": \"donation_form\",\n \"note\": \"Donate page opt-in checkbox\"\n }\n}"
response = http.request(request)
puts response.read_bodyImport Contacts From A URL
Import contacts into a list from a CSV you host.
The file is fetched over HTTPS through the same SSRF-guarded fetcher POST /contact-lists/import uses: private and link-local addresses are refused and re-checked on every redirect, and the 50 MB cap is enforced while streaming rather than trusted from Content-Length. Fetching and staging happen before this call returns; the rows are written afterwards, which is why the status is 202. Poll GET /email/lists/imports/{id}.
mapping is optional. Omit it and the platform uses the mapping it recognizes from the export’s own headers, which is what a caller exporting from a common ESP wants. When neither your mapping nor the recognizer finds an email column the request returns 400 VALIDATION_ERROR with details.headers listing the headers that were read, so the retry can name the right column instead of guessing.
Early access. This endpoint returns 403 EMAIL_EARLY_ACCESS until the email product reaches general availability. The contract below is stable and safe to build against.
curl --request POST \
--url https://api.politicalcomms.com/v1/email/lists/import \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"source_url": "https://files.example.com/exports/august-donors.csv",
"name": "August donors",
"consent": {
"source": "donation_form",
"note": "Donate page opt-in checkbox"
}
}
'import requests
url = "https://api.politicalcomms.com/v1/email/lists/import"
payload = {
"source_url": "https://files.example.com/exports/august-donors.csv",
"name": "August donors",
"consent": {
"source": "donation_form",
"note": "Donate page opt-in checkbox"
}
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
source_url: 'https://files.example.com/exports/august-donors.csv',
name: 'August donors',
consent: {source: 'donation_form', note: 'Donate page opt-in checkbox'}
})
};
fetch('https://api.politicalcomms.com/v1/email/lists/import', 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.politicalcomms.com/v1/email/lists/import",
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([
'source_url' => 'https://files.example.com/exports/august-donors.csv',
'name' => 'August donors',
'consent' => [
'source' => 'donation_form',
'note' => 'Donate page opt-in checkbox'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$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.politicalcomms.com/v1/email/lists/import"
payload := strings.NewReader("{\n \"source_url\": \"https://files.example.com/exports/august-donors.csv\",\n \"name\": \"August donors\",\n \"consent\": {\n \"source\": \"donation_form\",\n \"note\": \"Donate page opt-in checkbox\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
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.politicalcomms.com/v1/email/lists/import")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"source_url\": \"https://files.example.com/exports/august-donors.csv\",\n \"name\": \"August donors\",\n \"consent\": {\n \"source\": \"donation_form\",\n \"note\": \"Donate page opt-in checkbox\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.politicalcomms.com/v1/email/lists/import")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source_url\": \"https://files.example.com/exports/august-donors.csv\",\n \"name\": \"August donors\",\n \"consent\": {\n \"source\": \"donation_form\",\n \"note\": \"Donate page opt-in checkbox\"\n }\n}"
response = http.request(request)
puts response.read_bodyAuthorizations
Authenticate every request by passing your API key in the X-API-Key header. Keys are scoped to your organization hierarchy.
Headers
Optional idempotency key: a unique string of 16-200 printable ASCII characters (a UUID is recommended). Retrying the write with the same key within 24 hours returns the stored response of the first call with an X-Idempotent-Replayed: true response header instead of executing it again. Reusing a key with a different request body returns 422 (IDEMPOTENCY_MISMATCH); a duplicate sent while the first call is still running returns 409 with a Retry-After header. Keys are scoped per endpoint and organization.
16 - 200Body
HTTPS URL of the CSV. Maximum 50 MB.
Show child attributes
Show child attributes
Name for the list this file becomes. Defaults to the file name. The uploaded file IS the list: an import creates one rather than adding to an existing list.
255Scopes the list to one sending domain. Omit for an organization-wide list any campaign can use.
The addresses were purchased or rented. An acquired list must pass paid validation before it can be sent to.
CSV header to contact field, for example {"Email Address": "email", "First": "first_name"}. Exactly one header must map to email.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
