curl --request POST \
--url https://api.lemlist.com/api/campaigns/{campaignId}/leads/ \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "support@lemlist.com",
"firstName": "John",
"lastName": "Doe",
"companyName": "lemlist",
"jobTitle": "Developer",
"linkedinUrl": "https://www.linkedin.com/in/johndoe",
"phone": "+33 123456789",
"timezone": "Europe/Paris",
"contactOwner": "login_email@lemlist.com",
"companySize": "50-100",
"customVariable1": "any value you want"
}
'import requests
url = "https://api.lemlist.com/api/campaigns/{campaignId}/leads/"
payload = {
"email": "support@lemlist.com",
"firstName": "John",
"lastName": "Doe",
"companyName": "lemlist",
"jobTitle": "Developer",
"linkedinUrl": "https://www.linkedin.com/in/johndoe",
"phone": "+33 123456789",
"timezone": "Europe/Paris",
"contactOwner": "login_email@lemlist.com",
"companySize": "50-100",
"customVariable1": "any value you want"
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'support@lemlist.com',
firstName: 'John',
lastName: 'Doe',
companyName: 'lemlist',
jobTitle: 'Developer',
linkedinUrl: 'https://www.linkedin.com/in/johndoe',
phone: '+33 123456789',
timezone: 'Europe/Paris',
contactOwner: 'login_email@lemlist.com',
companySize: '50-100',
customVariable1: 'any value you want'
})
};
fetch('https://api.lemlist.com/api/campaigns/{campaignId}/leads/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));require 'uri'
require 'net/http'
url = URI("https://api.lemlist.com/api/campaigns/{campaignId}/leads/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"support@lemlist.com\",\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"companyName\": \"lemlist\",\n \"jobTitle\": \"Developer\",\n \"linkedinUrl\": \"https://www.linkedin.com/in/johndoe\",\n \"phone\": \"+33 123456789\",\n \"timezone\": \"Europe/Paris\",\n \"contactOwner\": \"login_email@lemlist.com\",\n \"companySize\": \"50-100\",\n \"customVariable1\": \"any value you want\"\n}"
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.lemlist.com/api/campaigns/{campaignId}/leads/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => 'support@lemlist.com',
'firstName' => 'John',
'lastName' => 'Doe',
'companyName' => 'lemlist',
'jobTitle' => 'Developer',
'linkedinUrl' => 'https://www.linkedin.com/in/johndoe',
'phone' => '+33 123456789',
'timezone' => 'Europe/Paris',
'contactOwner' => 'login_email@lemlist.com',
'companySize' => '50-100',
'customVariable1' => 'any value you want'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"campaignId": "cam_bSn8EORHQxbWPjHvu",
"campaignName": "NEW TO DELETE",
"firstName": "John",
"lastName": "Doe",
"companyName": "lemlist",
"jobTitle": "GTM Engineer",
"companyDomain": "example.com",
"email": "support@lemlist.com",
"_id": "lea_fiDpiGV585wy3Oii2",
"isPaused": false,
"contactId": "ctc_xW8Ou6C03Csv8vatp"
}"No API key provided""The authentication you supplied is incorrect""User linked to this API key is blocked""No user found for this API key"{
"error": "Invalid email address"
}"Lead already in other campaign"Create Lead in Campaign
Creates a new lead and adds it to a specific campaign. Never updates an existing lead.
curl --request POST \
--url https://api.lemlist.com/api/campaigns/{campaignId}/leads/ \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"email": "support@lemlist.com",
"firstName": "John",
"lastName": "Doe",
"companyName": "lemlist",
"jobTitle": "Developer",
"linkedinUrl": "https://www.linkedin.com/in/johndoe",
"phone": "+33 123456789",
"timezone": "Europe/Paris",
"contactOwner": "login_email@lemlist.com",
"companySize": "50-100",
"customVariable1": "any value you want"
}
'import requests
url = "https://api.lemlist.com/api/campaigns/{campaignId}/leads/"
payload = {
"email": "support@lemlist.com",
"firstName": "John",
"lastName": "Doe",
"companyName": "lemlist",
"jobTitle": "Developer",
"linkedinUrl": "https://www.linkedin.com/in/johndoe",
"phone": "+33 123456789",
"timezone": "Europe/Paris",
"contactOwner": "login_email@lemlist.com",
"companySize": "50-100",
"customVariable1": "any value you want"
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({
email: 'support@lemlist.com',
firstName: 'John',
lastName: 'Doe',
companyName: 'lemlist',
jobTitle: 'Developer',
linkedinUrl: 'https://www.linkedin.com/in/johndoe',
phone: '+33 123456789',
timezone: 'Europe/Paris',
contactOwner: 'login_email@lemlist.com',
companySize: '50-100',
customVariable1: 'any value you want'
})
};
fetch('https://api.lemlist.com/api/campaigns/{campaignId}/leads/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));require 'uri'
require 'net/http'
url = URI("https://api.lemlist.com/api/campaigns/{campaignId}/leads/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"email\": \"support@lemlist.com\",\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"companyName\": \"lemlist\",\n \"jobTitle\": \"Developer\",\n \"linkedinUrl\": \"https://www.linkedin.com/in/johndoe\",\n \"phone\": \"+33 123456789\",\n \"timezone\": \"Europe/Paris\",\n \"contactOwner\": \"login_email@lemlist.com\",\n \"companySize\": \"50-100\",\n \"customVariable1\": \"any value you want\"\n}"
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.lemlist.com/api/campaigns/{campaignId}/leads/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'email' => 'support@lemlist.com',
'firstName' => 'John',
'lastName' => 'Doe',
'companyName' => 'lemlist',
'jobTitle' => 'Developer',
'linkedinUrl' => 'https://www.linkedin.com/in/johndoe',
'phone' => '+33 123456789',
'timezone' => 'Europe/Paris',
'contactOwner' => 'login_email@lemlist.com',
'companySize' => '50-100',
'customVariable1' => 'any value you want'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"campaignId": "cam_bSn8EORHQxbWPjHvu",
"campaignName": "NEW TO DELETE",
"firstName": "John",
"lastName": "Doe",
"companyName": "lemlist",
"jobTitle": "GTM Engineer",
"companyDomain": "example.com",
"email": "support@lemlist.com",
"_id": "lea_fiDpiGV585wy3Oii2",
"isPaused": false,
"contactId": "ctc_xW8Ou6C03Csv8vatp"
}"No API key provided""The authentication you supplied is incorrect""User linked to this API key is blocked""No user found for this API key"{
"error": "Invalid email address"
}"Lead already in other campaign"400 LEAD_ALREADY_IN_CAMPAIGN, nothing written; use PATCH /campaigns/{campaignId}/leads/{leadId} instead. With deduplicate=true, a contact with a lead in another campaign is refused with 409 LEAD_ALREADY_IN_OTHER_CAMPAIGN; only this parameter drives that check, not the campaign’s “remove duplicates” setting.updateStrategy decides what they keep. overwrite (default): sent values replace stored ones, an empty value clears. overwriteIgnoreEmpty: same, empty values ignored. fillEmptyOnly: only empty fields are filled, identifiers, company link, owner and status kept, and the lead takes the values the contact kept. Kept values are listed in warnings (FIELDS_KEPT, params.fields, company fields prefixed with company).{{yourVariableName}}.In the example, companySize and customVariable1 are custom variables.Naming rules: only letters, digits, _, -, space and # are kept — any other character (e.g. . or $) is replaced by _. Values are stored as text.Authorizations
Basic authentication header of the form Basic <encoded-value>, where <encoded-value> is the base64-encoded string username:password.
Path Parameters
The unique identifier of the campaign
Query Parameters
Refuse the lead with 409 LEAD_ALREADY_IN_OTHER_CAMPAIGN when the contact already has one in another campaign. Default: false. The campaign's own "remove duplicates" setting does not apply here.
Run the LinkedIn enrichment. Default: false
Find verified email. Default: false
Verify existing email (debounce). Default: false
Find phone number. Default: false
What the matched contact and company keep (the lead is always created). overwrite (default): sent values replace stored ones, an empty string clears. overwriteIgnoreEmpty: empty values ignored. fillEmptyOnly: only empty fields filled, identifiers, links, owner and status kept. Other values: 400.
overwrite, overwriteIgnoreEmpty, fillEmptyOnly Body
Email of the lead
First name of the lead
Last name of the lead
Company name
Job title
LinkedIn profile URL
Profile picture URL
Phone number
Company domain
Personalized icebreaker message
Lead's timezone in IANA format (e.g., Europe/Paris, America/New_York)
Contact owner (user ID or user login email)
Response
Success
Whether the lead is paused in the campaign
Associated contact ID
Non-blocking notices: the company part was skipped, or FIELDS_KEPT when the update strategy kept values you sent (params.fields names them, company fields prefixed with company).
Show child attributes
Show child attributes
Was this page helpful?