Skip to main content
POST
https://YOUR_DEPLOYMENT.lupitor.com/api/v1
/
api
/
v1
/
leads
/
bulk
curl -X POST https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaignId": "YOUR_CAMPAIGN_ID",
    "leads": [
      {
        "phoneNumber": "+15555551234",
        "externalId": "CRM_001",
        "metadata": { "name": "John Doe" },
        "priority": 10
      },
      {
        "phoneNumber": "+15555555678",
        "externalId": "CRM_002",
        "metadata": { "name": "Jane Smith" },
        "priority": 5
      }
    ]
  }'
{
  "success": true,
  "data": {
    "created": 3,
    "skipped": 0,
    "createdIds": [
      "jh7abc123xyz",
      "jh7abc456def",
      "jh7abc789ghi"
    ],
    "skippedIdentifiers": []
  },
  "error": null
}
Endpoint Alias: You can also use /api/v1/records/bulk instead of /api/v1/leads/bulk. Both endpoints are functionally identical - use whichever naming convention fits your integration.

Lead Import Modes

Each lead in the array can use either import mode:

Standard Mode

Phone number known at import timeProvide phoneNumber directly for each lead.

Prefetch Mode

Phone number fetched just-in-timeProvide externalId only. Phone fetched via predial function before calling.
Each lead must have at least one of phoneNumber or externalId. You can mix modes within the same bulk request.

Authentication

x-api-key
string
required
Your API key with write scope

Request Body

campaignId
string
required
The ID of the campaign these leads belong to
leads
array
required
Array of lead objects (max 1000 per request)

Response

success
boolean
Whether the request was successful
data
object
error
string | null
Error message if request failed

Examples

All leads with phone numbers:
curl -X POST https://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaignId": "YOUR_CAMPAIGN_ID",
    "leads": [
      {
        "phoneNumber": "+15555551234",
        "externalId": "CRM_001",
        "metadata": { "name": "John Doe" },
        "priority": 10
      },
      {
        "phoneNumber": "+15555555678",
        "externalId": "CRM_002",
        "metadata": { "name": "Jane Smith" },
        "priority": 5
      }
    ]
  }'
{
  "success": true,
  "data": {
    "created": 3,
    "skipped": 0,
    "createdIds": [
      "jh7abc123xyz",
      "jh7abc456def",
      "jh7abc789ghi"
    ],
    "skippedIdentifiers": []
  },
  "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:
  1. Checks externalId first - if provided, checks for existing lead with same externalId in campaign
  2. Then checks phoneNumber - if provided, checks for existing lead with same phone in campaign
  3. Skips duplicates without failing the entire request
  4. Returns details about what was created vs skipped
This makes bulk import idempotent - you can safely retry without creating duplicates!

Common Errors

ErrorCauseSolution
campaignId is requiredMissing campaignIdInclude campaignId in request body
leads array is requiredMissing or invalid leadsInclude leads array in request body
leads array cannot be emptyEmpty leads arrayAdd at least one lead to the array
Maximum 1000 leads per bulk requestToo many leadsSplit into multiple requests
leads[N]: At least one of phoneNumber or externalId is requiredLead missing both identifiersEach lead needs phoneNumber or externalId

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 (E.164 format)
  • Ensure each lead has phoneNumber OR externalId
  • Remove duplicates in your data first
  • Check the created vs skipped counts
  • Log skippedIdentifiers for investigation
  • Retry failed requests with exponential backoff
  • Ensure your campaign has a predial function configured
  • Include any metadata needed by the predial function
  • Test with a small batch first

Example: CSV Import Script

Python
import csv
import requests
import time

def import_leads_from_csv(file_path, campaign_id, api_key, mode='standard'):
    """
    Import leads from CSV file in batches

    Args:
        file_path: Path to CSV file
        campaign_id: Campaign ID to import to
        api_key: Your API key
        mode: 'standard' (phone required) or 'prefetch' (externalId only)
    """

    batch_size = 500
    leads = []

    with open(file_path, 'r') as file:
        reader = csv.DictReader(file)

        for row in reader:
            lead = {}

            # Always include externalId if available
            if row.get('id') or row.get('external_id'):
                lead['externalId'] = row.get('id') or row.get('external_id')

            # Include phone number if available (required for standard mode)
            if row.get('phone'):
                lead['phoneNumber'] = row['phone']

            # Add metadata
            lead['metadata'] = {
                'name': row.get('name', ''),
                'company': row.get('company', '')
            }

            if row.get('priority'):
                lead['priority'] = int(row['priority'])

            leads.append(lead)

            # 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://YOUR_DEPLOYMENT.lupitor.com/api/v1/leads/bulk',
        headers={
            'x-api-key': api_key,
            'Content-Type': 'application/json'
        },
        json={
            'campaignId': campaign_id,
            'leads': leads
        }
    )

    result = response.json()
    if result['success']:
        print(f"Created: {result['data']['created']}, Skipped: {result['data']['skipped']}")
    else:
        print(f"Error: {result['error']}")

# Usage - Standard mode (CSV has phone numbers)
import_leads_from_csv(
    'leads_with_phones.csv',
    'YOUR_CAMPAIGN_ID',
    'YOUR_API_KEY',
    mode='standard'
)

# Usage - Prefetch mode (CSV has only external IDs)
import_leads_from_csv(
    'leads_external_ids.csv',
    'YOUR_CAMPAIGN_ID',
    'YOUR_API_KEY',
    mode='prefetch'
)