Skip to main content

Overview

The lead sync API lets you push leads from your CRM (HubSpot, Salesforce, Pipedrive, or any custom system) into Naturalead. It supports idempotent upserts using an external lead_id, so you can run periodic syncs without creating duplicates.

Idempotent Upserts

Use your CRM’s lead ID as the external identifier. Re-syncing the same lead updates it instead of creating a duplicate.

Bulk Operations

Sync up to 100 leads per request. The API handles partial failures gracefully.

Authentication

Authenticate sync requests with an API key.
curl -H "X-API-Key: nl_live_your_api_key_here" ...
Sync requests require an API key with the leads:sync_create scope. Create one in Settings > API Keys in the dashboard.

Using External lead_id for Idempotent Upserts

Every lead in the sync payload can include a lead_id field that maps to your CRM’s unique identifier. When Naturalead receives a lead with a lead_id that already exists in your account, it updates the existing lead instead of creating a new one. This makes your sync operations idempotent — running the same sync twice produces the same result.

Bulk Sync Example

Send an array of leads to the sync endpoint. Each lead should include a lead_id from your CRM.
curl -X POST https://api.naturalead.ai/api/leads/sync \
  -H "X-API-Key: nl_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "hubspot",
    "leads": [
      {
        "lead_id": "hs_12345",
        "name": "Alice Johnson",
        "phone": "+14155551234",
        "email": "[email protected]",
        "customFields": {
          "company": "Acme Corp",
          "deal_stage": "discovery",
          "deal_value": 50000
        },
        "tags": ["enterprise", "inbound"]
      },
      {
        "lead_id": "hs_12346",
        "name": "Bob Smith",
        "phone": "+14155555678",
        "email": "[email protected]",
        "customFields": {
          "company": "Globex Inc",
          "deal_stage": "proposal",
          "deal_value": 25000
        },
        "tags": ["smb", "outbound"]
      }
    ]
  }'

Handling Partial Failures

When syncing multiple leads, some may succeed while others fail validation. The API does not roll back successful operations when others fail. Instead, it returns a detailed breakdown.
{
  "created": 1,
  "updated": 1,
  "failed": 1,
  "errors": [
    {
      "index": 2,
      "lead_id": "hs_12347",
      "error": "Invalid phone number format"
    }
  ]
}
Always check the errors array in the response. A 200 status code does not mean every lead was synced successfully — it means the request was processed, and the response contains the outcome for each lead.
Recommended error handling strategy:
  1. Log all entries from the errors array with their lead_id for investigation.
  2. Fix the invalid data in your CRM.
  3. Re-sync only the failed leads in the next run.

Bulk Delete for Synced Leads

To remove leads that were deleted in your CRM, use the bulk delete endpoint with the external lead_id values.
curl -X DELETE https://api.naturalead.ai/api/leads/sync \
  -H "X-API-Key: nl_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "hubspot",
    "lead_ids": ["hs_12345", "hs_12346"]
  }'

Rate Limit Considerations

The sync API has a dedicated rate limit to protect system stability.
LimitValue
Sync requests per minute50
Leads per sync request100
Maximum leads per minute5,000
If you exceed the rate limit, the API returns a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait before retrying.
For large initial imports (more than 5,000 leads), break the sync into batches:
import time
import requests

BATCH_SIZE = 100
DELAY_BETWEEN_BATCHES = 1.5  # seconds

def sync_leads_in_batches(all_leads):
    for i in range(0, len(all_leads), BATCH_SIZE):
        batch = all_leads[i:i + BATCH_SIZE]
        response = requests.post(
            "https://api.naturalead.ai/api/leads/sync",
            headers={
                "X-API-Key": "nl_live_your_api_key_here",
                "Content-Type": "application/json",
            },
            json={"source": "hubspot", "leads": batch},
        )

        result = response.json()
        print(f"Batch {i // BATCH_SIZE + 1}: "
              f"created={result['created']}, "
              f"updated={result['updated']}, "
              f"failed={result['failed']}")

        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", 60))
            print(f"Rate limited. Waiting {retry_after}s...")
            time.sleep(retry_after)
        else:
            time.sleep(DELAY_BETWEEN_BATCHES)

Best Practices for Periodic Sync

1

Use External lead_id Consistently

Always include the CRM’s unique lead identifier as lead_id. This is the key that makes upserts idempotent. Never omit it, or you risk creating duplicates.
2

Sync Incrementally

Rather than syncing your entire CRM on every run, track the last sync timestamp and only send leads that were created or modified since then. This reduces API calls and processing time.
3

Handle Deletes Separately

After syncing new and updated leads, query your CRM for recently deleted records and send those to the bulk delete endpoint.
4

Schedule During Off-Peak Hours

If you are syncing large volumes, schedule your sync jobs during off-peak hours to avoid competing with real-time conversation traffic for rate limit capacity.
5

Monitor and Alert on Failures

Log every sync response and set up alerts when the failed count exceeds a threshold. Persistent failures often indicate data quality issues in the CRM that should be resolved at the source.
For real-time sync (pushing leads as they are created in your CRM), consider using your CRM’s webhook or event system to trigger individual lead creation via POST /api/leads instead of the bulk sync endpoint.