List Customers
The List Customers endpoint provides comprehensive customer retrieval capabilities with advanced filtering, search, and pagination features. This endpoint is designed for building customer management dashboards, implementing search functionality, and generating reports. It returns customers scoped to your application with built-in security and audit logging.
Endpoint DetailsCopied!
-
Method:
GET
-
URL:
/api/v0/customers
-
Content Type: Not applicable (GET request)
-
Authentication: Required (
x-client-key
andx-client-secret
) -
Rate Limiting: Subject to standard API rate limits
Request HeadersCopied!
x-client-key: your_client_key_here
x-client-secret: your_client_secret_here
Query ParametersCopied!
Pagination Parameters
Parameter |
Type |
Required |
Default |
Description |
Constraints |
---|---|---|---|---|---|
|
number |
❌ |
|
Number of customers to skip for pagination |
Min: 0, Max: 10,000 |
|
number |
❌ |
|
Number of customers to return per page |
Min: 1, Max: 100 |
Filter Parameters
Parameter |
Type |
Required |
Default |
Description |
---|---|---|---|---|
|
CustomerStatus |
❌ |
- |
Filter customers by their account status |
|
string |
❌ |
- |
Search customers by first name or last name |
|
string |
❌ |
- |
Filter customers by email address |
Filter Details
Status Filter Options
-
ACTIVE
- Customers with active accounts who can use services -
BLACKLISTED
- Customers who have been blacklisted from services -
DEACTIVATED
- Customers with deactivated accounts
Name Search
-
Case-insensitive partial matching across both first and last names
-
OR logic: Matches if the search term appears in either first_name OR last_name
-
Examples:
-
name=john
matches "John Doe", "Jane Johnson", "Michael Johnston" -
name=smith
matches "John Smith", "Sarah Smith", "Smithfield Corp"
-
Email Filter
-
Case-insensitive partial matching within email addresses
-
Substring search: Finds emails containing the specified text
-
Examples:
-
email=@gmail.com
finds all Gmail users -
email=support
finds emails containing "support" -
email=john
finds "john@example.com", "johnny@test.com"
-
Request ExamplesCopied!
Basic List Request
GET /api/v0/customers HTTP/1.1
Host: api.devdraft.com
x-client-key: your_client_key
x-client-secret: your_client_secre
Paginated Request
GET /api/v0/customers?skip=20&take=50 HTTP/1.1
Host: api.devdraft.com
x-client-key: your_client_key
x-client-secret: your_client_secret
Filtered Request
GET /api/v0/customers?status=ACTIVE&name=john&take=25 HTTP/1.1
Host: api.devdraft.com
x-client-key: your_client_key
x-client-secret: your_client_secret
Search Request
GET /api/v0/customers?email=@business.com&skip=0&take=100 HTTP/1.1
Host: api.devdraft.com
x-client-key: your_client_key
x-client-secret: your_client_secret
Success ResponseCopied!
Status Code: 200 OK
Response Format
Returns an array of customer objects ordered by creation date (newest first).
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone_number": "+1-555-123-4567",
"customer_type": "Individual",
"status": "ACTIVE",
"last_spent": 250.75,
"last_purchase_date": "2024-01-10T15:30:00Z",
"appId": "app_123456789",
"app_id": "app_123456789",
"createdAt": "2024-01-01T10:00:00Z",
"updatedAt": "2024-01-15T14:30:00Z"
},
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"first_name": "Jane",
"last_name": "Smith",
"email": "jane.smith@business.com",
"phone_number": "+1-555-987-6543",
"customer_type": "Business",
"status": "ACTIVE",
"last_spent": 1500.00,
"last_purchase_date": "2024-01-20T09:15:00Z",
"appId": "app_123456789",
"app_id": "app_123456789",
"createdAt": "2024-01-05T14:20:00Z",
"updatedAt": "2024-01-20T09:15:00Z"
}
]
Empty Result
When no customers match the criteria:
[]
Response Field Descriptions
Field |
Type |
Description |
Nullable |
---|---|---|---|
|
string |
Unique customer identifier (UUID) |
❌ |
|
string |
Customer's first name |
❌ |
|
string |
Customer's last name |
❌ |
|
string |
Customer's email address |
✅ |
|
string |
Customer's phone number with country code |
❌ |
|
string |
Account type (Individual, Business, Enterprise, Non-Profit) |
✅ |
|
string |
Account status (ACTIVE, BLACKLISTED, DEACTIVATED) |
❌ |
|
number |
Amount of most recent transaction |
❌ |
|
string |
ISO 8601 timestamp of last purchase |
✅ |
|
string |
Associated application identifier |
❌ |
|
string |
Duplicate field for backward compatibility |
❌ |
|
string |
ISO 8601 timestamp when customer was created |
❌ |
|
string |
ISO 8601 timestamp when customer was last updated |
❌ |
Error ResponsesCopied!
Authentication Error (401)
{
"statusCode": 401,
"message": "Invalid API credentials",
"error": "Unauthorized"
}
Invalid Parameters (400)
{
"statusCode": 400,
"message": [
"take must not be greater than 100",
"skip must not be less than 0"
],
"error": "Bad Request"
}
Rate Limit Exceeded (429)
{
"statusCode": 429,
"message": "Too many requests",
"error": "Too Many Requests",
"retry_after": 60
}
Server Error (500)
{
"statusCode": 500,
"message": "Internal server error",
"error": "Internal Server Error"
}
Integration ExamplesCopied!
cURL Examples
Basic List
curl -X GET https://api.devdraft.com/api/v0/customers \
-H "x-client-key: your_client_key" \
-H "x-client-secret: your_client_secret"
With Pagination
curl -X GET "https://api.devdraft.com/api/v0/customers?skip=50&take=25" \
-H "x-client-key: your_client_key" \
-H "x-client-secret: your_client_secret"
With Filters
curl -X GET "https://api.devdraft.com/api/v0/customers?status=ACTIVE&name=smith&email=@gmail.com" \
-H "x-client-key: your_client_key" \
-H "x-client-secret: your_client_secret"
JavaScript/Node.js Examples
Basic Implementation
async function listCustomers() {
const response = await fetch('https://api.devdraft.com/api/v0/customers', {
headers: {
'x-client-key': process.env.DEVDRAFT_CLIENT_KEY,
'x-client-secret': process.env.DEVDRAFT_CLIENT_SECRET
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const customers = await response.json();
return customers;
}
Advanced Filtering
async function searchCustomers(filters = {}) {
const params = new URLSearchParams();
// Add pagination
if (filters.page) {
const skip = (filters.page - 1) * (filters.limit || 10);
params.append('skip', skip.toString());
}
if (filters.limit) {
params.append('take', filters.limit.toString());
}
// Add filters
if (filters.status) params.append('status', filters.status);
if (filters.name) params.append('name', filters.name);
if (filters.email) params.append('email', filters.email);
const response = await fetch(`https://api.devdraft.com/api/v0/customers?${params}`, {
headers: {
'x-client-key': process.env.DEVDRAFT_CLIENT_KEY,
'x-client-secret': process.env.DEVDRAFT_CLIENT_SECRET
}
});
return await response.json();
}
// Usage examples
const activeCustomers = await searchCustomers({ status: 'ACTIVE', limit: 50 });
const gmailUsers = await searchCustomers({ email: '@gmail.com' });
const johnDoes = await searchCustomers({ name: 'john' });
Pagination Helper
class CustomerPaginator {
constructor(apiKey, apiSecret) {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.baseUrl = 'https://api.devdraft.com/api/v0/customers';
}
async getPage(page = 1, limit = 25, filters = {}) {
const skip = (page - 1) * limit;
const params = new URLSearchParams({
skip: skip.toString(),
take: limit.toString(),
...filters
});
const response = await fetch(`${this.baseUrl}?${params}`, {
headers: {
'x-client-key': this.apiKey,
'x-client-secret': this.apiSecret
}
});
const customers = await response.json();
return {
customers,
page,
limit,
hasMore: customers.length === limit
};
}
async *getAllPages(filters = {}, pageSize = 100) {
let page = 1;
let hasMore = true;
while (hasMore) {
const result = await this.getPage(page, pageSize, filters);
yield result.customers;
hasMore = result.hasMore;
page++;
}
}
}
// Usage
const paginator = new CustomerPaginator(
process.env.DEVDRAFT_CLIENT_KEY,
process.env.DEVDRAFT_CLIENT_SECRET
);
// Get all customers across all pages
for await (const customerBatch of paginator.getAllPages({ status: 'ACTIVE' })) {
console.log(`Processing ${customerBatch.length} customers`);
// Process each batch
}
Python Examples
Basic Implementation
import requests
import os
from typing import Optional, Dict, List
class DevdraftCustomers:
def __init__(self):
self.base_url = "https://api.devdraft.com/api/v0/customers"
self.headers = {
'x-client-key': os.getenv('DEVDRAFT_CLIENT_KEY'),
'x-client-secret': os.getenv('DEVDRAFT_CLIENT_SECRET')
}
def list_customers(
self,
skip: int = 0,
take: int = 10,
status: Optional[str] = None,
name: Optional[str] = None,
email: Optional[str] = None
) -> List[Dict]:
"""List customers with optional filtering."""
params = {'skip': skip, 'take': take}
if status:
params['status'] = status
if name:
params['name'] = name
if email:
params['email'] = email
response = requests.get(
self.base_url,
headers=self.headers,
params=params
)
response.raise_for_status()
return response.json()
def search_customers(self, query: str) -> List[Dict]:
"""Search customers by name or email."""
# Try name search first
name_results = self.list_customers(name=query, take=100)
# Then email search
email_results = self.list_customers(email=query, take=100)
# Combine and deduplicate
all_results = name_results + email_results
seen_ids = set()
unique_results = []
for customer in all_results:
if customer['id'] not in seen_ids:
seen_ids.add(customer['id'])
unique_results.append(customer)
return unique_results
def get_all_customers(self, **filters) -> List[Dict]:
"""Get all customers with pagination."""
all_customers = []
skip = 0
take = 100 # Max per request
while True:
batch = self.list_customers(skip=skip, take=take, **filters)
if not batch:
break
all_customers.extend(batch)
skip += take
return all_customers
# Usage examples
client = DevdraftCustomers()
# Get first 50 active customers
active_customers = client.list_customers(status='ACTIVE', take=50)
# Search for customers named "john"
john_customers = client.search_customers('john')
# Get all business customers
business_customers = client.get_all_customers(status='ACTIVE')
Advanced Filtering with Pandas
import pandas as pd
def customers_to_dataframe(customers: List[Dict]) -> pd.DataFrame:
"""Convert customer list to pandas DataFrame for analysis."""
df = pd.DataFrame(customers)
# Convert timestamps
df['createdAt'] = pd.to_datetime(df['createdAt'])
df['updatedAt'] = pd.to_datetime(df['updatedAt'])
df['last_purchase_date'] = pd.to_datetime(df['last_purchase_date'])
return df
# Get all customers and analyze
client = DevdraftCustomers()
all_customers = client.get_all_customers()
df = customers_to_dataframe(all_customers)
# Analysis examples
print(f"Total customers: {len(df)}")
print(f"Active customers: {len(df[df['status'] == 'ACTIVE'])}")
print(f"Average last spent: ${df['last_spent'].mean():.2f}")
# Find high-value customers
high_value = df[df['last_spent'] > 1000]
print(f"High-value customers: {len(high_value)}")
PHP Examples
Basic Implementation
<?php
class DevdraftCustomers {
private $baseUrl = 'https://api.devdraft.com/api/v0/customers';
private $headers;
public function __construct($clientKey, $clientSecret) {
$this->headers = [
'x-client-key: ' . $clientKey,
'x-client-secret: ' . $clientSecret
];
}
public function listCustomers($params = []) {
$url = $this->baseUrl;
if (!empty($params)) {
$url .= '?' . http_build_query($params);
}
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => implode("\r\n", $this->headers)
]
]);
$result = file_get_contents($url, false, $context);
if ($result === false) {
throw new Exception('Failed to fetch customers');
}
return json_decode($result, true);
}
public function searchByStatus($status, $limit = 25) {
return $this->listCustomers([
'status' => $status,
'take' => $limit
]);
}
public function searchByName($name, $limit = 25) {
return $this->listCustomers([
'name' => $name,
'take' => $limit
]);
}
public function getAllCustomers($filters = []) {
$allCustomers = [];
$skip = 0;
$take = 100;
do {
$params = array_merge($filters, [
'skip' => $skip,
'take' => $take
]);
$batch = $this->listCustomers($params);
$allCustomers = array_merge($allCustomers, $batch);
$skip += $take;
} while (count($batch) === $take);
return $allCustomers;
}
}
// Usage
$client = new DevdraftCustomers(
$_ENV['DEVDRAFT_CLIENT_KEY'],
$_ENV['DEVDRAFT_CLIENT_SECRET']
);
// Get active customers
$activeCustomers = $client->searchByStatus('ACTIVE', 50);
// Search for customers
$johnCustomers = $client->searchByName('john');
// Get all customers (be careful with large datasets)
$allCustomers = $client->getAllCustomers(['status' => 'ACTIVE']);
echo "Found " . count($allCustomers) . " active customers\n";
?>
Advanced Use CasesCopied!
1. Customer Dashboard Implementation
// Real-time dashboard with live updates
class CustomerDashboard {
constructor(apiKey, apiSecret) {
this.api = new DevdraftCustomers(apiKey, apiSecret);
this.refreshInterval = 30000; // 30 seconds
}
async loadDashboard() {
try {
const [active, blacklisted, deactivated] = await Promise.all([
this.api.list_customers({ status: 'ACTIVE', take: 100 }),
this.api.list_customers({ status: 'BLACKLISTED', take: 100 }),
this.api.list_customers({ status: 'DEACTIVATED', take: 100 })
]);
return {
active: active.length,
blacklisted: blacklisted.length,
deactivated: deactivated.length,
total: active.length + blacklisted.length + deactivated.length,
lastUpdated: new Date().toISOString()
};
} catch (error) {
console.error('Dashboard load failed:', error);
throw error;
}
}
startAutoRefresh(callback) {
setInterval(async () => {
try {
const data = await this.loadDashboard();
callback(data);
} catch (error) {
console.error('Auto refresh failed:', error);
}
}, this.refreshInterval);
}
}
2. Customer Export System
import csv
from datetime import datetime
def export_customers_to_csv(client, filename=None, filters=None):
"""Export customers to CSV file."""
if filename is None:
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f'customers_export_{timestamp}.csv'
customers = client.get_all_customers(**(filters or {}))
fieldnames = [
'id', 'first_name', 'last_name', 'email', 'phone_number',
'customer_type', 'status', 'last_spent', 'last_purchase_date',
'createdAt', 'updatedAt'
]
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for customer in customers:
# Only write the fields we want
row = {field: customer.get(field, '') for field in fieldnames}
writer.writerow(row)
return filename, len(customers)
# Usage
client = DevdraftCustomers()
filename, count = export_customers_to_csv(
client,
filters={'status': 'ACTIVE'},
filename='active_customers.csv'
)
print(f"Exported {count} customers to {filename}")
3. Customer Search Engine
class CustomerSearchEngine {
constructor(apiClient) {
this.api = apiClient;
this.cache = new Map();
this.cacheTimeout = 5 * 60 * 1000; // 5 minutes
}
async search(query, options = {}) {
const cacheKey = `${query}-${JSON.stringify(options)}`;
// Check cache first
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey);
if (Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.results;
}
}
const results = await this.performSearch(query, options);
// Cache results
this.cache.set(cacheKey, {
results,
timestamp: Date.now()
});
return results;
}
async performSearch(query, options) {
const { status, limit = 50 } = options;
// Search by name and email in parallel
const [nameResults, emailResults] = await Promise.all([
this.api.searchCustomers({ name: query, status, take: limit }),
this.api.searchCustomers({ email: query, status, take: limit })
]);
// Combine and deduplicate
const allResults = [...nameResults, ...emailResults];
const uniqueResults = Array.from(
new Map(allResults.map(customer => [customer.id, customer])).values()
);
// Sort by relevance (exact matches first)
return uniqueResults.sort((a, b) => {
const aExact = this.isExactMatch(a, query);
const bExact = this.isExactMatch(b, query);
if (aExact && !bExact) return -1;
if (!aExact && bExact) return 1;
// Then by last purchase date
const aDate = new Date(a.last_purchase_date || 0);
const bDate = new Date(b.last_purchase_date || 0);
return bDate - aDate;
});
}
isExactMatch(customer, query) {
const q = query.toLowerCase();
return customer.first_name.toLowerCase() === q ||
customer.last_name.toLowerCase() === q ||
customer.email?.toLowerCase() === q;
}
}
Performance OptimizationCopied!
Caching Strategy
class CachedCustomerAPI {
constructor(apiClient, cacheOptions = {}) {
this.api = apiClient;
this.cache = new Map();
this.defaultTTL = cacheOptions.ttl || 300000; // 5 minutes
this.maxCacheSize = cacheOptions.maxSize || 1000;
}
async listCustomers(params = {}) {
const cacheKey = this.getCacheKey(params);
if (this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey);
if (Date.now() < cached.expiry) {
return cached.data;
}
this.cache.delete(cacheKey);
}
const data = await this.api.listCustomers(params);
// Manage cache size
if (this.cache.size >= this.maxCacheSize) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(cacheKey, {
data,
expiry: Date.now() + this.defaultTTL
});
return data;
}
getCacheKey(params) {
return JSON.stringify(params, Object.keys(params).sort());
}
clearCache() {
this.cache.clear();
}
}
Batch Processing
def process_customers_in_batches(client, processor_func, batch_size=100, filters=None):
"""Process all customers in batches to manage memory usage."""
skip = 0
processed_count = 0
while True:
# Fetch batch
batch = client.list_customers(
skip=skip,
take=batch_size,
**(filters or {})
)
if not batch:
break
# Process batch
try:
processor_func(batch)
processed_count += len(batch)
print(f"Processed {processed_count} customers so far...")
except Exception as e:
print(f"Error processing batch starting at {skip}: {e}")
# Optionally continue or break
skip += batch_size
# Rate limiting - avoid overwhelming the API
time.sleep(0.1)
return processed_count
# Example processor
def send_newsletter(customers):
for customer in customers:
if customer.get('email'):
# Send newsletter logic here
print(f"Sending newsletter to {customer['email']}")
# Usage
client = DevdraftCustomers()
total_processed = process_customers_in_batches(
client,
send_newsletter,
batch_size=50,
filters={'status': 'ACTIVE'}
)
print(f"Newsletter sent to {total_processed} customers")
Best PracticesCopied!
1. Efficient Pagination
-
Use
take=100
for maximum efficiency when fetching large datasets -
Implement cursor-based pagination for real-time data
-
Cache results when appropriate to reduce API calls
2. Smart Filtering
-
Combine filters to reduce result sets:
status=ACTIVE&email=@business.com
-
Use specific searches rather than broad queries
-
Implement client-side filtering for frequently accessed data
3. Error Handling
-
Always implement retry logic with exponential backoff
-
Handle rate limiting gracefully with proper delays
-
Cache successful responses to reduce API load
4. Security Considerations
-
Never expose API credentials in client-side code
-
Implement proper access controls in your application
-
Log customer access for audit purposes
5. Performance Monitoring
-
Track API response times and implement timeouts
-
Monitor cache hit rates and adjust TTL accordingly
-
Use batch processing for large operations
Related EndpointsCopied!
-
GET /api/v0/customers/{id}
- Get specific customer details -
POST /api/v0/customers
- Create new customers -
PATCH /api/v0/customers/{id}
- Update customer information -
POST /api/v0/customers/{id}/liquidation_addresses
- Manage customer liquidation addresses
SupportCopied!
For technical support or questions about listing customers:
-
Verify your pagination parameters are within limits
-
Check filter syntax and supported values
-
Monitor your rate limiting usage
-
Contact support with specific query parameters that cause issues