Lemlist-Brevo Contact Sync Workflow
1. Purpose
This Python script automates bidirectional contact synchronization between Lemlist and Brevo (formerly Sendinblue) email marketing platforms. It enables seamless data transfer, contact management, and synchronization workflows between the two platforms, supporting marketing automation and contact database management operations.
Key Capabilities:
- Import contacts from Lemlist campaigns to Brevo
- Import contacts from Brevo to Lemlist campaigns
- Export Lemlist contacts to CSV files
- Search and find contacts across platforms
- Date-based filtering for selective synchronization
- Automatic attribute mapping and creation
- Duplicate detection and handling
2. Trigger
Type: Python Script (Command Line)
Execution Methods:
Standalone Execution
The script can be run directly from the command line with various options:
Basic Usage:
python lemlist-brevo.py
Default Behavior: When run without arguments, the script executes both:
- Lemlist to Brevo import (for today's date)
- Brevo to Lemlist import (for yesterday and today)
3. Step-by-Step Process
Step 1: Environment Configuration
Loads environment variables from .env file, validates required API credentials, and initializes logging configuration.
What input it uses:
.envfile containing:LEMLIST_AUTH- Lemlist API authentication tokenBREVO_API_KEY- Brevo API keyLEMLIST_CAMPAIGN_ID- Default Lemlist campaign ID for importsLEMLIST_CAMPAIGN_ID_BREVO_TO_LEMLIST- Campaign ID for Brevo-to-Lemlist importsLOG_DIR- Log directory path (default: "logs")
What output it produces:
- Initialized API headers for Lemlist and Brevo
- Log file path:
logs/lemlist_brevo_YYYYMMDD.log - Validated credentials ready for API calls
Why this step is needed: Ensures secure API access and proper logging for debugging and audit trails.
Required Environment Variables:
LEMLIST_AUTH=your_lemlist_auth_token
BREVO_API_KEY=your_brevo_api_key
LEMLIST_CAMPAIGN_ID=cam_izAb64Dp8ifZphk5F
LEMLIST_CAMPAIGN_ID_BREVO_TO_LEMLIST=cam_ieN79BAdD9w7ixzhF
LOG_DIR=logs
Step 2: Fetch Campaigns (Lemlist)
Retrieves all campaigns from Lemlist account using pagination to handle large datasets.
What input it uses:
- Lemlist API base URL:
https://api.lemlist.com/api - Authentication headers
- Pagination parameters (limit: 100, offset: 0)
What output it produces:
- Array of all campaigns with:
- Campaign ID (
_id) - Campaign name (
name) - Other campaign metadata
- Campaign ID (
Why this step is needed: Provides campaign list for fetching contacts from multiple campaigns, enabling comprehensive contact synchronization.
API Endpoint: GET /api/campaigns?limit=100&offset={offset}
Step 3: Export Leads from Campaigns (Lemlist)
Exports all leads from each Lemlist campaign, including contacts in all states (active, paused, completed, etc.).
What input it uses:
- Campaign IDs from Step 2
- Export format: JSON
- State filter:
all(includes all contact states)
What output it produces:
- Array of contact objects containing:
- Email address
- Contact attributes (firstName, lastName, company, etc.)
- Campaign metadata
- Date fields (createdAt, addedToCampaign, etc.)
Why this step is needed: Retrieves complete contact data from Lemlist campaigns for synchronization with Brevo.
API Endpoint: GET /api/campaigns/{campaign_id}/export/leads?format=json&state=all
Step 4: Date Filtering (Optional)
Filters contacts based on creation or enrollment date if a date parameter is provided.
What input it uses:
- Contact array from Step 3
- Date string (format:
YYYY-MM-DDorDD-MM-YYYY) - Date normalization function handles multiple formats
What output it produces:
- Filtered contact array containing only contacts added on the specified date
- Debug logging for date field identification
Why this step is needed: Enables selective synchronization of contacts added on specific dates, reducing unnecessary API calls and maintaining data freshness.
Date Field Detection: The script intelligently searches for date fields in contacts:
- Standard fields:
createdAt,created_at,addedAt,added_at - Campaign-specific:
addedToCampaign,enrolledAt,enrollmentDate - Normalized matching handles variations in field naming
Step 5: Fetch Brevo Contacts
Retrieves contacts from Brevo account using pagination, with optional date filtering via API parameters.
What input it uses:
- Brevo API base URL:
https://api.brevo.com/v3 - API authentication headers
- Optional date range parameters:
createdSince- ISO 8601 format start datecreatedBefore- ISO 8601 format end date
- Pagination (limit: 500, offset: 0)
What output it produces:
- Array of Brevo contact objects containing:
- Email address
- Attributes (nested in
attributesfield) - Creation date (
createdAt) - Contact ID
Why this step is needed: Retrieves contact data from Brevo for synchronization to Lemlist or comparison purposes.
API Endpoint: GET /v3/contacts?limit=500&offset={offset}&createdSince={date}&createdBefore={date}
Date Filtering:
- If date range provided: Uses API-level filtering for efficiency
- If no date: Fetches all contacts with pagination
Step 6: Attribute Mapping and Creation (Brevo)
Maps Lemlist contact fields to Brevo attribute names and creates missing attributes in Brevo if they don't exist.
What input it uses:
- Contact fields from Lemlist contacts
- Existing Brevo attributes (fetched via API)
- Field mapping dictionary (
CSV_TO_BREVO_MAPPING)
What output it produces:
- Created Brevo attributes (type: TEXT)
- Mapping of Lemlist fields to Brevo attribute names
- List of failed attribute creations (logged)
Why this step is needed: Ensures all contact data can be stored in Brevo by creating necessary custom attributes and mapping field names correctly.
Field Mapping Examples:
firstName/first_name→FIRSTNAMElastName/last_name→LASTNAMEcompany/companyName→COMPANYlinkedinUrl/linkedin_url→LINKEDINphone/mobile→SMSjobTitle/job_title→JOB_TITLE
System-Restricted Fields: These fields cannot be created or overwritten:
EMAIL,WHATSAPP,SMS_BLACKLISTED,EMAIL_BLACKLISTEDUPDATE_DATE,MODIFIED,STATUS,EMAILSTATUS
API Endpoint: POST /v3/contacts/attributes/normal/{attribute_name}
Step 7: Duplicate Detection
Checks if an email already exists in the target platform before importing to prevent duplicates.
What input it uses:
- Email address from source contact
- Target platform API (Brevo or Lemlist)
What output it produces:
- Boolean result:
Trueif email exists,Falseif not - For Brevo: HTTP 200 (exists) or 404 (not found)
- For Lemlist: Searches across all campaigns
Why this step is needed: Prevents duplicate contacts and maintains data integrity across platforms.
Brevo Check: GET /v3/contacts/{email}
Lemlist Check: Searches all campaigns for matching email
Step 8: Phone Number Formatting
Formats phone numbers to international format for Brevo SMS field, with validation and country code handling.
What input it uses:
- Raw phone number string
- Default country code:
+91(India)
What output it produces:
- Formatted phone number in international format (e.g.,
+919876543210) - Empty string if phone number is invalid
Why this step is needed: Ensures phone numbers are properly formatted for SMS functionality in Brevo.
Formatting Rules:
- Removes spaces, dashes, parentheses, dots
- Validates international format (starts with
+) - Handles Indian numbers (10 digits → adds
+91prefix) - Validates length (10-15 digits after country code)
Step 9: Import Contacts to Brevo
Creates new contacts in Brevo with mapped attributes, skipping duplicates and handling errors gracefully.
What input it uses:
- Filtered and mapped contact data from Lemlist
- Brevo attribute mappings
- Formatted phone numbers
What output it produces:
- Import results:
imported: Count of successfully imported contactsfailed: Count of failed imports with error messagesskipped: Count of contacts skipped (already exist)- Sample lists of each category (first 10)
Why this step is needed: Transfers contact data from Lemlist to Brevo, enabling unified contact management and email marketing campaigns.
API Endpoint: POST /v3/contacts
Payload Structure:
{
"email": "contact@example.com",
"attributes": {
"FIRSTNAME": "John",
"LASTNAME": "Doe",
"COMPANY": "Example Corp",
"SMS": "+919876543210"
},
"updateEnabled": true
}
Import Limit: Maximum 500 contacts per execution (configurable via MAX_IMPORT_LIMIT)
Step 10: Import Contacts to Lemlist
Creates or updates contacts in Lemlist campaign from Brevo contact data.
What input it uses:
- Brevo contacts (from yesterday and today by default)
- Lemlist campaign ID:
LEMLIST_CAMPAIGN_ID_BREVO_TO_LEMLIST - Contact lookup dictionary for duplicate detection
What output it produces:
- Import results:
imported: Count of newly created contactsupdated: Count of updated existing contactsfailed: Count of failed operations- Sample lists of each category
Why this step is needed: Synchronizes contacts from Brevo back to Lemlist, maintaining bidirectional data flow for sales outreach campaigns.
API Endpoint: POST /api/campaigns/{campaign_id}/leads
Payload Structure:
{
"email": "contact@example.com",
"firstName": "John",
"lastName": "Doe",
"companyName": "Example Corp",
"linkedinUrl": "https://linkedin.com/in/johndoe",
"phone": "+919876543210"
}
Field Mapping (Brevo → Lemlist):
FIRSTNAME→firstNameLASTNAME→lastNameCOMPANY→companyNameLINKEDIN→linkedinUrlSMS→phoneWEBSITE→companyDomain
Step 11: CSV Export (Optional)
Exports Lemlist contacts to CSV file for backup, analysis, or external processing.
What input it uses:
- Contact data from Lemlist (optionally filtered by date)
- Output directory:
/Users/fleetstudio-47/Documents/GitHub/AI-Hubspoot/
What output it produces:
- CSV file named:
lemlist_contacts_on_YYYYMMDD.csv(if date specified)lemlist_leads_all.csv(if all contacts)
- File contains all contact fields as columns
Why this step is needed: Provides data backup and enables external analysis using spreadsheet tools or other systems.
File Location: /Users/fleetstudio-47/Documents/GitHub/AI-Hubspoot/lemlist_contacts_on_YYYYMMDD.csv
Step 12: Logging and Reporting
Logs all operations, errors, and results to daily log files with timestamps.
What input it uses:
- Operation results from all steps
- Error messages and exceptions
- Progress indicators
What output it produces:
- Daily log file:
logs/lemlist_brevo_YYYYMMDD.log - Console output with summary statistics
- Detailed operation logs for debugging
Why this step is needed: Provides audit trail, debugging information, and operation history for troubleshooting and compliance.
Log Format:
[YYYY-MM-DD HH:MM:SS] Log message
Summary Output:
Import Summary:
- Dates: YYYY-MM-DD (yesterday) and YYYY-MM-DD (today)
- Total Contacts Fetched: XXX
- Successfully Imported: XXX
- Updated: XXX
- Failed: XXX
- Skipped (already exists): XXX
4. Workflow Logic Summary
The script executes the following sequence based on command-line arguments:
Default Execution (No Arguments):
-
Lemlist to Brevo Import:
- Fetches contacts from all Lemlist campaigns for today's date
- Filters contacts by date if specified
- Maps fields to Brevo attributes
- Creates missing Brevo attributes
- Checks for duplicates
- Imports new contacts (max 500)
- Logs results
-
Brevo to Lemlist Import:
- Fetches contacts from Brevo (yesterday and today)
- Maps Brevo fields to Lemlist format
- Checks for existing contacts in Lemlist
- Creates or updates contacts in Lemlist campaign
- Logs results
Export Mode (--export):
- Fetches contacts from Lemlist campaigns
- Applies date filtering if specified
- Exports to CSV file
- Returns file path
Search Mode (--search):
- Searches all Lemlist campaigns for email
- Returns contact details and campaign information
- Logs search results
Brevo to Lemlist Only (--brevo-to-lemlist):
- Fetches Brevo contacts (yesterday and today)
- Imports to Lemlist campaign
- Logs results
5. Configuration
Environment Variables (.env file)
Create a .env file in the script directory with the following variables:
# Required: API Credentials
LEMLIST_AUTH=your_lemlist_basic_auth_token
BREVO_API_KEY=your_brevo_api_key
# Optional: Campaign IDs (defaults provided)
LEMLIST_CAMPAIGN_ID=cam_izAb64Dp8ifZphk5F
LEMLIST_CAMPAIGN_ID_BREVO_TO_LEMLIST=cam_ieN79BAdD9w7ixzhF
# Optional: Logging
LOG_DIR=logs
API Credentials
Lemlist API:
- Base URL:
https://api.lemlist.com/api - Authentication: Basic Auth (token in
LEMLIST_AUTH) - Format: Base64 encoded
email:api_key
Brevo API:
- Base URL:
https://api.brevo.com/v3 - Authentication: API Key in header (
api-key) - Documentation: https://developers.brevo.com/
Campaign Configuration
Default Campaigns:
LEMLIST_CAMPAIGN_ID: Used for Lemlist-to-Brevo imports (default:cam_izAb64Dp8ifZphk5F)LEMLIST_CAMPAIGN_ID_BREVO_TO_LEMLIST: Used for Brevo-to-Lemlist imports (default:cam_ieN79BAdD9w7ixzhF)
6. Dependencies
External APIs
Lemlist API
- Endpoint:
https://api.lemlist.com/api - Purpose: Contact management and campaign data retrieval
- Requires Auth: Yes (Basic Auth)
- Rate Limits: Handled with retry logic (3 attempts, 5-second delay on 429)
- Endpoints Used:
GET /campaigns- List all campaignsGET /campaigns/{id}/export/leads- Export campaign leadsPOST /campaigns/{id}/leads- Create/update contact
Brevo API
- Endpoint:
https://api.brevo.com/v3 - Purpose: Email marketing and contact management
- Requires Auth: Yes (API Key)
- Rate Limits: Handled with pagination and retry logic
- Endpoints Used:
GET /contacts- List contacts (with date filtering)GET /contacts/{email}- Check if contact existsGET /contacts/attributes- List existing attributesPOST /contacts/attributes/normal/{name}- Create custom attributePOST /contacts- Create contact
Python Dependencies
Required Packages:
requests>=2.25.0 # HTTP API calls
csv # CSV file operations (built-in)
pathlib # File path handling (built-in)
datetime # Date/time operations (built-in)
urllib.parse # URL encoding (built-in)
Installation:
pip install requests
File System
Required Directories:
logs/- Log file storage (created automatically)/Users/fleetstudio-47/Documents/GitHub/AI-Hubspoot/- CSV export directory (must exist or be writable)
7. Error Handling
API Error Handling
Retry Logic:
- Maximum 3 attempts for failed requests
- 5-second delay on rate limit (HTTP 429)
- 2-second delay between retries for other errors
- Logs errors to file for debugging
Common Error Scenarios:
- 429 Too Many Requests: Automatic retry with delay
- 404 Not Found: Handled gracefully (contact doesn't exist)
- 400 Bad Request: Logged with error message
- Network Errors: Retried with exponential backoff
Data Validation
Email Validation:
- Checks for missing email addresses
- Normalizes email to lowercase
- Skips contacts without valid email
Phone Number Validation:
- Validates length (10-15 digits)
- Formats to international standard
- Skips invalid phone numbers with logging
Date Parsing:
- Handles multiple date formats:
- ISO format:
YYYY-MM-DD - European format:
DD-MM-YYYY - ISO datetime:
YYYY-MM-DDTHH:MM:SS
- ISO format:
- Gracefully handles parsing errors
Duplicate Handling
Brevo Import:
- Checks if email exists before import
- Skips existing contacts (logged as "skipped")
- Prevents duplicate entries
Lemlist Import:
- Searches all campaigns for existing email
- Updates existing contacts instead of creating duplicates
- Tracks created vs updated counts
8. Usage Examples
Example 1: Daily Synchronization
Scenario: Sync contacts added today between Lemlist and Brevo
# Run default sync (today's contacts)
python lemlist-brevo.py
Output:
- Imports today's Lemlist contacts to Brevo
- Imports yesterday and today's Brevo contacts to Lemlist
- Logs results to daily log file
Example 2: Historical Import
Scenario: Import contacts from a specific date
# Import contacts from December 1, 2024
python lemlist-brevo.py 2024-12-01
# Or European date format
python lemlist-brevo.py 01-12-2024
Example 3: Export All Contacts
Scenario: Backup all Lemlist contacts to CSV
python lemlist-brevo.py --export --all
Output: lemlist_leads_all.csv in export directory
Example 4: Find Contact
Scenario: Search for a contact across all Lemlist campaigns
python lemlist-brevo.py --search john.doe@example.com
Output: Contact details and campaign information logged
Example 5: One-Way Import
Scenario: Only import from Brevo to Lemlist
python lemlist-brevo.py --brevo-to-lemlist
Output: Imports Brevo contacts (yesterday and today) to Lemlist
Example 6: Scheduled Execution
Scenario: Run as cron job for daily synchronization
# Add to crontab (runs daily at 2 AM)
0 2 * * * cd /path/to/script && python lemlist-brevo.py >> /var/log/lemlist-sync.log 2>&1
9. Logging
Log File Location
Default: logs/lemlist_brevo_YYYYMMDD.log
Example: logs/lemlist_brevo_20241205.log
Log Format
[YYYY-MM-DD HH:MM:SS] Log message
Log Levels
- Info: Normal operations, progress updates
- Error: API errors, validation failures
- Debug: Detailed field information (when debug mode enabled)
Sample Log Output
[2024-12-05 10:30:15] ============================================================
[2024-12-05 10:30:15] Starting Lemlist to Brevo import for 2024-12-05
[2024-12-05 10:30:15] ============================================================
[2024-12-05 10:30:16] Step 1: Fetching contacts from Lemlist...
[2024-12-05 10:30:18] Found 25 contacts from Lemlist
[2024-12-05 10:30:18] Importing contacts to Brevo...
[2024-12-05 10:30:20] SUCCESS: Email john@example.com imported to Brevo
[2024-12-05 10:30:25]
Import Summary:
- Date: 2024-12-05
- Total Contacts Fetched: 25
- Successfully Imported: 20
- Failed: 2
- Skipped (already exists): 3
[2024-12-05 10:30:25] ============================================================
10. Limitations and Considerations
Rate Limits
- Lemlist API: No documented rate limits, but script implements retry logic
- Brevo API: Free tier: 300 requests/day, Paid tiers: Higher limits
- Recommendation: Use date filtering to limit API calls
Import Limits
- Maximum Import: 500 contacts per execution (configurable)
- Reason: Prevents timeout and rate limit issues
- Workaround: Run script multiple times with date ranges
Date Filtering
- Lemlist: Client-side filtering (fetches all, then filters)
- Brevo: API-level filtering (more efficient)
- Impact: Large Lemlist campaigns may take longer to process
Attribute Creation
- Brevo: Creates attributes as TEXT type only
- Limitation: Cannot create date, number, or category attributes automatically
- Workaround: Create complex attributes manually in Brevo dashboard
Phone Number Formatting
- Default Country Code: +91 (India)
- Limitation: Assumes Indian numbers for 10-digit inputs
- Customization: Modify
format_phone_number()function for other countries
CSV Export Path
- Hardcoded Path:
/Users/fleetstudio-47/Documents/GitHub/AI-Hubspoot/ - Limitation: Not configurable via environment variable
- Customization: Modify
export_lemlist_contacts_to_csv()function
11. Troubleshooting
Common Issues
Issue: Missing API Credentials
ValueError: LEMLIST_AUTH is required in .env file
Solution: Ensure .env file exists with valid credentials
Issue: Import Fails Silently Check:
- Log file for error messages
- API key validity
- Network connectivity
- Rate limit status
Issue: Contacts Not Found Check:
- Date format (use YYYY-MM-DD)
- Campaign IDs are correct
- Contacts exist in source platform
Issue: Attribute Creation Fails Check:
- Attribute name follows Brevo rules (uppercase, alphanumeric)
- Not a system-restricted field
- Brevo API key has attribute creation permissions
Issue: Phone Numbers Not Imported Check:
- Phone number format (must be valid)
- Country code handling
- Log file for "Invalid phone length" messages
12. Source Links
Script Location
File Path: lemlist-brevo.py
Repository: https://github.com/TeamFleetStudio/Lemlist-Brevo
API Documentation
Lemlist API:
- Documentation: https://developer.lemlist.com/
- Base URL:
https://api.lemlist.com/api
Brevo API:
- Documentation: https://developers.brevo.com/
- Base URL:
https://api.brevo.com/v3 - Contact Management: https://developers.brevo.com/reference/createcontact
Related Tools
- Lemlist: https://lemlist.com/ - Sales outreach platform
- Brevo: https://www.brevo.com/ - Email marketing and CRM platform
Related Workflows
- Workflows Index - View all workflows
- Wally Signup Users Info Workflow - User onboarding automation
- Workflows Directory - Workflow documentation guidelines