Authentication
Your API key with write scope
Request Body
The ID of the campaign these leads belong to
Array of lead objects (max 1000 per request)Show Lead Object Properties
Lead’s phone number in E.164 format
Custom data about the lead
ISO 8601 timestamp for when to call
Response
Whether the request was successful
Number of leads successfully created
Number of leads skipped (duplicates)
Array of IDs for created leads
Array of phone numbers that were skipped
Error message if request failed
curl -X POST https://lupitor.acrely.ai/api/v1/leads/bulk \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"campaignId": "k978z8ndfnqjnbxmphvf0wd59h6yrd0a",
"leads": [
{
"phoneNumber": "+15555551234",
"externalId": "CRM_001",
"metadata": {
"name": "John Doe",
"company": "Acme Corp"
},
"priority": 10
},
{
"phoneNumber": "+15555555678",
"externalId": "CRM_002",
"metadata": {
"name": "Jane Smith",
"company": "Tech Inc"
},
"scheduledFor": "2024-12-25T09:00:00Z",
"priority": 5
},
{
"phoneNumber": "+15555559999",
"metadata": {
"name": "Bob Johnson"
}
}
]
}'
{
"success": true,
"data": {
"created": 3,
"skipped": 0,
"createdIds": [
"jh7abc123xyz",
"jh7abc456def",
"jh7abc789ghi"
],
"skippedPhones": []
},
"error": null
}
Limits
Maximum Size: You can create up to 1000 leads per bulk request. For larger imports, make multiple requests.
Rate Limit: Bulk endpoint is limited to 10 requests per minute to prevent abuse.
Duplicate Handling
The bulk endpoint automatically handles duplicates:
- Checks each phone number against existing leads in the campaign
- Skips duplicates without failing the entire request
- Returns details about what was created vs skipped
This makes bulk import idempotent - you can safely retry without creating duplicates!
Common Errors
| Error | Cause | Solution |
campaignId is required | Missing campaignId | Include campaignId in request body |
leads array is required | Missing or invalid leads | Include leads array in request body |
leads array cannot be empty | Empty leads array | Add at least one lead to the array |
Maximum 1000 leads per bulk request | Too many leads | Split into multiple requests |
leads[0].phoneNumber is required | Missing phone number | Each lead must have a phoneNumber |
Best Practices
- Use 100-500 leads per request for optimal performance
- Don’t max out at 1000 unless necessary
- Monitor response times and adjust
- Spread out bulk imports over time
- Don’t import 10,000 leads at once
- Respect rate limits (10/min)
- Validate phone numbers before sending
- Ensure E.164 format (+15555551234)
- Remove duplicates in your data first
- Check the
created vs skipped counts
- Log
skippedPhones for investigation
- Retry failed requests with exponential backoff
Example: CSV Import Script
import csv
import requests
import time
def import_leads_from_csv(file_path, campaign_id, api_key):
"""Import leads from CSV file in batches"""
batch_size = 500
leads = []
with open(file_path, 'r') as file:
reader = csv.DictReader(file)
for row in reader:
leads.append({
'phoneNumber': row['phone'],
'externalId': row['id'],
'metadata': {
'name': row['name'],
'company': row.get('company', '')
},
'priority': int(row.get('priority', 0))
})
# Send batch when we hit the limit
if len(leads) >= batch_size:
send_batch(leads, campaign_id, api_key)
leads = []
time.sleep(6) # Rate limit: 10/min = 1 every 6 sec
# Send remaining leads
if leads:
send_batch(leads, campaign_id, api_key)
def send_batch(leads, campaign_id, api_key):
"""Send a batch of leads to the API"""
response = requests.post(
'https://lupitor.acrely.ai/api/v1/leads/bulk',
headers={
'x-api-key': api_key,
'Content-Type': 'application/json'
},
json={
'campaignId': campaign_id,
'leads': leads
}
)
result = response.json()
print(f"Created: {result['data']['created']}, Skipped: {result['data']['skipped']}")
# Usage
import_leads_from_csv(
'leads.csv',
'k978z8ndfnqjnbxmphvf0wd59h6yrd0a',
'YOUR_API_KEY'
)