curl --request GET \
--url https://api.privy.com/v1/contacts \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.privy.com/v1/contacts"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.privy.com/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.privy.com/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 => [
"Authorization: Bearer <token>"
],
]);
$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.privy.com/v1/contacts"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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.privy.com/v1/contacts")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.privy.com/v1/contacts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "cus_a1b2c3d4e5f6g7h8",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"email_consent": "subscribed",
"phone_number": "+15551234567",
"sms_consent": "subscribed",
"tags": [
"vip"
],
"custom_fields": {
"loyalty_tier": "gold"
},
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-03-20T14:22:00Z"
}
],
"pagination": {
"page": 1,
"per_page": 25,
"total_count": 142,
"total_pages": 6
}
}{
"error": {
"code": "unauthorized",
"message": "Bearer token is missing or invalid"
}
}{
"error": {
"code": "insufficient_scope",
"message": "Token does not have the required scope"
}
}{
"error": {
"code": "contacts_not_ready",
"message": "This account's contacts have not been assigned public API ids, so contact reads are unavailable. Enable API access for the account, or have Privy staff run the public_id backfill task for it, then retry."
}
}{
"error": {
"code": "validation_failed",
"message": "One or more fields are invalid",
"details": [
{
"field": "email",
"message": "is required"
}
]
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded"
}
}List contacts
Retrieve a paginated list of contacts, most recently created first. Optionally filter by email, phone number, or consent status.
Unfiltered, the list covers every contact in the account whatever their consent state, so
pagination.total_count is the size of your contact list — not the size of your mailable or
textable audience. Filter on email_consent=subscribed or sms_consent=subscribed for those.
Contacts you have deleted are excluded — but deletion is soft: creating a contact with
the same email or phone number restores the existing record rather than creating a new
one, so a previously deleted contact can reappear here later with its original id and
history.
Required scope: contacts_read
curl --request GET \
--url https://api.privy.com/v1/contacts \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.privy.com/v1/contacts"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.privy.com/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.privy.com/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 => [
"Authorization: Bearer <token>"
],
]);
$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.privy.com/v1/contacts"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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.privy.com/v1/contacts")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.privy.com/v1/contacts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "cus_a1b2c3d4e5f6g7h8",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"email_consent": "subscribed",
"phone_number": "+15551234567",
"sms_consent": "subscribed",
"tags": [
"vip"
],
"custom_fields": {
"loyalty_tier": "gold"
},
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-03-20T14:22:00Z"
}
],
"pagination": {
"page": 1,
"per_page": 25,
"total_count": 142,
"total_pages": 6
}
}{
"error": {
"code": "unauthorized",
"message": "Bearer token is missing or invalid"
}
}{
"error": {
"code": "insufficient_scope",
"message": "Token does not have the required scope"
}
}{
"error": {
"code": "contacts_not_ready",
"message": "This account's contacts have not been assigned public API ids, so contact reads are unavailable. Enable API access for the account, or have Privy staff run the public_id backfill task for it, then retry."
}
}{
"error": {
"code": "validation_failed",
"message": "One or more fields are invalid",
"details": [
{
"field": "email",
"message": "is required"
}
]
}
}{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded"
}
}Authorizations
Send either an API token or an OAuth access token as
Authorization: Bearer <token>. See the Authentication page for details.
Headers
Target a subaccount instead of the main connected account.
Pass a subaccount id from get_account's subaccounts list. Only direct subaccounts
of the connected account are reachable — omit this for the main account itself.
Query Parameters
Page number (starts at 1).
x >= 1Number of results per page. Values outside 10–100 are clamped to the nearest bound.
10 <= x <= 100Filter by exact email address.
Filter by exact phone number (E.164 format).
Filter by email consent status. subscribed matches both explicit
and implicit opt-in. suppressed matches merchant-suppressed contacts
only; compliance_suppressed matches system/compliance-suppressed contacts.
subscribed, unsubscribed, never_subscribed, suppressed, compliance_suppressed Filter by SMS consent status.
subscribed, unsubscribed, never_subscribed, single_opt_in, pending Was this page helpful?