curl --request GET \
--url https://api.coldsend.pro/api/public/v1/contacts \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.coldsend.pro/api/public/v1/contacts"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.coldsend.pro/api/public/v1/contacts', 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.coldsend.pro/api/public/v1/contacts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"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"
"net/http"
"io"
)
func main() {
url := "https://api.coldsend.pro/api/public/v1/contacts"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.coldsend.pro/api/public/v1/contacts")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.coldsend.pro/api/public/v1/contacts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"message": "<string>",
"contacts": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"team_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"email": "<string>",
"signal": "NEVER_CONTACTED",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"first_name": "<string>",
"last_name": "<string>",
"company": "<string>",
"custom_fields": {},
"signal_override": "NEVER_CONTACTED",
"signal_computed_at": "2023-11-07T05:31:56Z",
"source_type": "<string>",
"source_name": "<string>",
"first_batch_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"last_touch_at": "2023-11-07T05:31:56Z",
"last_reply_at": "2023-11-07T05:31:56Z",
"last_open_at": "2023-11-07T05:31:56Z",
"campaign_count": 0,
"is_unsubscribed": false,
"is_bounced": false,
"tags": [
{
"tag_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"color": "<string>",
"category_group": "<string>",
"is_system": false,
"applied_by_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
],
"campaign_names": [
"<string>"
]
}
],
"pagination": {
"page": 123,
"limit": 123,
"total": 123,
"pages": 123,
"has_next": true,
"has_prev": true
},
"success": true
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Search the contact directory
Search the team’s contact directory
Contacts are the team-scoped people of record; a lead is one campaign membership of a contact. This endpoint returns the directory view — signal, engagement rollups, source provenance and tags — rather than a single campaign’s membership rows.
Filtering Options
All filters AND together; list-valued filters OR within themselves.
- signal: Engagement signal (UNSUBSCRIBED, BOUNCED, HOT_LEAD, ENGAGED, CONTACTED, NEVER_CONTACTED)
- tag_ids: Contacts carrying any of these tags
- is_unsubscribed / is_bounced: Suppression and deliverability state
- source_type / source_name: Where the contact came from
- campaign_id / not_in_campaign_id: Membership in / exclusion from a campaign
- last_touch_after / last_touch_before: Outbound touch window
- has_replied / has_opened: Engagement flags
- search: Substring match across email, name and company
Example Usage
import requests
response = requests.get(
"https://api.coldsend.pro/api/public/v1/contacts",
headers={"X-API-Key": "your-api-key"},
params={"signal": "HOT_LEAD", "limit": 20}
)
print(response.json())
curl --request GET \
--url https://api.coldsend.pro/api/public/v1/contacts \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.coldsend.pro/api/public/v1/contacts"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.coldsend.pro/api/public/v1/contacts', 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.coldsend.pro/api/public/v1/contacts",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"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"
"net/http"
"io"
)
func main() {
url := "https://api.coldsend.pro/api/public/v1/contacts"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.coldsend.pro/api/public/v1/contacts")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.coldsend.pro/api/public/v1/contacts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"message": "<string>",
"contacts": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"team_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"email": "<string>",
"signal": "NEVER_CONTACTED",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"first_name": "<string>",
"last_name": "<string>",
"company": "<string>",
"custom_fields": {},
"signal_override": "NEVER_CONTACTED",
"signal_computed_at": "2023-11-07T05:31:56Z",
"source_type": "<string>",
"source_name": "<string>",
"first_batch_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"last_touch_at": "2023-11-07T05:31:56Z",
"last_reply_at": "2023-11-07T05:31:56Z",
"last_open_at": "2023-11-07T05:31:56Z",
"campaign_count": 0,
"is_unsubscribed": false,
"is_bounced": false,
"tags": [
{
"tag_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"color": "<string>",
"category_group": "<string>",
"is_system": false,
"applied_by_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
],
"campaign_names": [
"<string>"
]
}
],
"pagination": {
"page": 123,
"limit": 123,
"total": 123,
"pages": 123,
"has_next": true,
"has_prev": true
},
"success": true
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Authorizations
API key with format cs_live_xxx
Query Parameters
Narrow to the members of a saved list
Page number
x >= 1Items per page
1 <= x <= 100Sort column
Sort descending
Match any of these engagement signals
Engagement signal for a directory contact.
Materialised onto contacts.signal by the contact rollup job so the
directory can filter and sort on it without touching leads. Derived
from the contact's campaign memberships in strict precedence order --
the first match wins:
UNSUBSCRIBED : suppressed for this team, i.e. a row exists in
`unsubscribes` for (team_id, email) with
unsubscribed_at IS NOT NULL. Suppression is
email-keyed, so this is independent of any
particular lead row.
BOUNCED : a membership bounced and the contact has never
otherwise engaged.
HOT_LEAD : a thread carries a positive INTENT tag
(Demo Booked / Interested).
ENGAGED : has replied or opened, or a thread carries the
Question INTENT tag.
CONTACTED : has been sent to, but no engagement recorded.
NEVER_CONTACTED : no membership has been sent yet.
A non-NULL contacts.signal_override always wins over the derived
value, so a manually pinned signal is never clobbered by the job.
Mirrors the contactsignal Postgres enum created in migration
20260826_0001_add_contact_directory.py. Keep in sync.
NEVER_CONTACTED, CONTACTED, ENGAGED, HOT_LEAD, BOUNCED, UNSUBSCRIBED Match contacts carrying any of these tags
Filter on team-level suppression
Filter on whether any membership bounced
Match any of these acquisition sources
How a contact first entered the directory.
Stored as a plain string column (not a Postgres enum) to match the
existing lead_batches.source_type convention; this class is the
canonical set of allowed values and is enforced by a CHECK constraint.
CSV, MANUAL, COPY, EXTERNAL_EMAIL, API Exact origin name, e.g. the uploaded file name
Only contacts with a membership in this campaign
Exclude contacts already in this campaign
Last outbound contact at or after this time
Last outbound contact strictly before this time
Filter on whether the contact ever replied
Filter on whether the contact ever opened
Substring match across email, name and company
Response
Contacts retrieved successfully
Response for a directory search.
Not to be confused with ContactListResponse from the shared schema
module, which describes one saved segment.
Was this page helpful?

