lemlist Contacts API
CRM-side contacts, contact lists, list membership and exports.
CRM-side contacts, contact lists, list membership and exports.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/lemlist-contacts-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Lemlist Contacts API
version: 1.0.0
description: 'Operations tagged Contacts across 3 of this provider''s published API definitions: lemlist-openapi-v1-legacy.json, lemlist-openapi-v2.json, lemlist-openapi.yml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://api.lemlist.com/api
tags:
- name: Contacts
paths:
/contacts/{idOrEmail}:
parameters:
- name: idOrEmail
in: path
required: true
example: '{{idOrEmail}}'
schema:
type: string
get:
summary: Get contact
tags:
- Contacts
parameters:
- name: Content
in: header
required: false
example: application/json
schema:
type: string
responses:
'200':
headers:
Content-Type:
schema:
type: string
example: application/json
description: Success
content:
application/json:
schema:
type: object
properties:
_id:
type: string
teamId:
type: string
email:
type: string
format: email
fullName:
type: string
fields:
type: object
properties:
firstName:
type: string
lastName:
type: string
company:
type: string
campaigns:
type: array
items:
type: object
properties:
campaignId:
type: string
campaignState:
type: string
leadState:
type: string
ownerId:
type: string
createdAt:
type: string
format: date-time
createdBy:
type: string
example:
_id: ctc_Ri5gMtaeDGufRQ3M2
teamId: tea_8GdMrBZ3gf4p2rj2h
email: chafik@lempire.co
fullName: Chafik Gharbi
fields:
firstName: Chafik
lastName: Gharbi
company: lempire
campaigns:
- campaignId: cam_C7BW5Z5ycSiRRWef6
campaignState: running
leadState: review
ownerId: usr_h47tiJr87Zn7XtQHi
createdAt: '2025-07-18T10:03:41.612Z'
createdBy: usr_h47tiJr87Zn7XtQHi
delete:
summary: Delete Contact
description: Deletes a lemlist contact resolved by its id (`ctc_xxx`) or email. Cascades to the contact's leads, opportunities, list associations, inbox conversations and activities. **No CRM-side propagation** — only the lemlist record is removed. Fails with `409 CONTACT_DELETE_BLOCKED` when the contact cannot be deleted right now — for example while an enrichment is still running (retry once it finishes); the response `error.message` states the specific reason.
tags:
- Contacts
responses:
'200':
description: Contact deleted.
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
deletedContactId:
type: string
description: ID of the deleted lemlist contact.
required:
- success
- deletedContactId
example:
success: true
deletedContactId: ctc_xW8Ou6C03Csv8vatp
'401':
description: The authentication you supplied is incorrect.
content:
text/plain:
example: The authentication you supplied is incorrect
'404':
description: Contact not found (`CONTACT_NOT_FOUND`).
content:
application/json:
example:
success: false
error:
code: CONTACT_NOT_FOUND
message: No contact found with the provided id or email
'409':
description: The contact cannot be deleted right now (`CONTACT_DELETE_BLOCKED`) — for example while an enrichment is in progress. The `error.message` states the specific reason; retry once it clears.
content:
application/json:
example:
success: false
error:
code: CONTACT_DELETE_BLOCKED
message: Contact cannot be removed if an enrichment is in progress
'405':
description: Method not allowed.
servers:
- url: https://api.lemlist.com/api
/contacts:
parameters: []
get:
summary: Get contacts
tags:
- Contacts
parameters:
- name: Content
in: header
required: false
example: application/json
schema:
type: string
responses:
'200':
headers:
Content-Type:
schema:
type: string
example: application/json
description: Success
content:
application/json:
schema:
type: array
items:
type: object
properties:
_id:
type: string
teamId:
type: string
email:
type: string
format: email
fullName:
type: string
fields:
type: object
properties:
firstName:
type: string
lastName:
type: string
company:
type: string
campaigns:
type: array
items:
type: object
properties:
campaignId:
type: string
campaignState:
type: string
leadState:
type: string
ownerId:
type: string
createdAt:
type: string
format: date-time
createdBy:
type: string
example:
- _id: ctc_Ri5gMtaeDGufRQ3M2
teamId: tea_8GdMrBZ3gf4p2rj2h
email: chafik@lempire.co
fullName: Chafik Gharbi
fields:
firstName: Chafik
lastName: Gharbi
company: lempire
campaigns:
- campaignId: cam_C7BW5Z5ycSiRRWef6
campaignState: running
leadState: review
ownerId: usr_h47tiJr87Zn7XtQHi
createdAt: '2025-07-18T10:03:41.612Z'
createdBy: usr_h47tiJr87Zn7XtQHi
- _id: ctc_Lznm3LZ8DMR4WS3r8
teamId: tea_8GdMrBZ3gf4p2rj2h
email: chafik2@lempire.co
fullName: Chafik Gharbi
fields:
firstName: Chafik
lastName: Gharbi
company: lempire
campaigns:
- campaignId: cam_C7BW5Z5ycSiRRWef6
campaignState: running
leadState: review
ownerId: usr_h47tiJr87Zn7XtQHi
createdAt: '2025-07-18T10:03:41.612Z'
createdBy: usr_h47tiJr87Zn7XtQHi
post:
summary: Add and update contact
description: Creates a new contact or updates an existing one (upsert). If a contact with the same email, LinkedIn URL, or Sales Navigator URL already exists, it will be updated with the provided non-empty fields. Null or empty values are ignored during updates to preserve existing data. You can target an existing contact directly by providing `contactId`, bypassing email/LinkedIn matching. You can optionally link the contact to a company by providing `companyId`, `companyDomain`, or `companyLinkedinUrl`.
tags:
- Contacts
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
contactId:
type: string
description: Existing contact ID. Updates a specific contact by ID, bypassing email/LinkedIn matching. Can only be used to update an existing contact, not to create a new one. When provided, `email` and `linkedinUrl` are not required. At least one of `contactId`, `email`, `linkedinUrl`, or `linkedinUrlSalesNav` is required.
email:
type: string
description: Contact email address. Used as a unique key for upsert matching. At least one of `contactId`, `email`, `linkedinUrl`, or `linkedinUrlSalesNav` is required.
linkedinUrl:
type: string
description: LinkedIn profile URL. Used as an alternative unique key for upsert matching. At least one of `contactId`, `email`, `linkedinUrl`, or `linkedinUrlSalesNav` is required.
linkedinUrlSalesNav:
type: string
description: LinkedIn Sales Navigator profile URL. Used as an alternative unique key for upsert matching.
additionalEmails:
type: array
items:
type: string
description: Additional email addresses for the contact. Each must be a valid email address.
firstName:
type: string
description: Contact first name.
lastName:
type: string
description: Contact last name.
phone:
type: string
description: Contact phone number.
jobTitle:
type: string
description: Contact job title. If a company is linked, this is saved as part of the job data.
jobDescription:
type: string
description: Contact job description. If a company is linked, this is saved as part of the job data.
picture:
type: string
description: URL of the contact's profile picture.
timezone:
type: string
description: Contact timezone.
industry:
type: string
description: Contact industry.
languages:
type: string
description: Contact languages.
location:
type: string
description: Contact location.
skills:
type: string
description: Contact skills.
summary:
type: string
description: Contact summary or bio.
tagline:
type: string
description: Contact tagline.
contactOwner:
type: string
description: Owner of the contact. Can be a user ID (e.g. `usr_...`) or a team member's email address. If the provided value does not match a team member, the owner defaults to the API key owner.
source:
type: string
description: Origin of the contact record. Set on creation only and cannot be updated afterwards. Defaults to `api`.
default: api
companyId:
type: string
description: ID of a company already existing in lemlist to link to this contact. Takes priority over `companyDomain` and `companyLinkedinUrl`.
companyDomain:
type: string
description: Domain of a company already existing in lemlist to link to this contact (e.g. `lemlist.com`). Used if `companyId` is not provided.
companyLinkedinUrl:
type: string
description: LinkedIn URL of a company already existing in lemlist to link to this contact. Used if `companyId` and `companyDomain` are not provided.
additionalProperties:
description: Any additional key is treated as a custom field. Custom fields must be registered in the team's CRM field registry beforehand.
example:
email: jane.smith@example.com
firstName: Jane
lastName: Smith
linkedinUrl: https://linkedin.com/in/janesmith
jobTitle: Product Manager
companyDomain: acme.com
responses:
'201':
description: Contact created successfully
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
data:
type: object
properties:
_id:
type: string
description: Unique contact identifier
teamId:
type: string
description: Team ID
emails:
type: array
description: Contact email addresses
items:
type: object
properties:
value:
type: string
linkedinUrl:
type: string
description: LinkedIn profile URL
linkedinUrlSalesNav:
type: string
description: LinkedIn Sales Navigator URL
fields:
type: object
description: Key/value fields (firstName, lastName, phone, jobTitle, custom fields, etc.)
additionalProperties: true
ownerId:
type: string
description: Owner user ID
companyId:
type: string
description: Linked company ID (present only when a company was linked)
source:
type: string
description: Record origin
createdAt:
type: string
format: date-time
description: Creation timestamp
createdBy:
type: string
description: User ID who created the record
unsubscribed:
type: boolean
description: Whether the contact is unsubscribed
created:
type: boolean
description: '`true` when a new contact was created, `false` when an existing contact was updated.'
updated:
type: boolean
description: Present and set to `true` only when an existing contact was updated (i.e. `created` is `false`).
companyLinked:
type: boolean
description: Whether a company was successfully linked to the contact.
warnings:
type: array
description: Optional warnings (e.g. company resolution issues).
items:
type: object
properties:
code:
type: string
message:
type: string
example:
success: true
data:
_id: ctc_xW8Ou6C03Csv8vatp
teamId: tea_8QvkOiBfPdb2ZRhHi
emails:
- value: jane.smith@example.com
linkedinUrl: https://www.linkedin.com/in/janesmith
fields:
firstName: Jane
lastName: Smith
jobTitle: Product Manager
ownerId: usr_ahfFktBBHUIxbVG5P
source: api
createdAt: '2025-10-28T00:40:37.917Z'
createdBy: usr_ahfFktBBHUIxbVG5P
unsubscribed: false
created: true
companyLinked: true
companyId: cpn_gG7PsmZFpEAnpMCHO
'200':
description: Existing contact updated (upsert matched by email or LinkedIn URL)
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
data:
type: object
description: 'Same shape as the 201 response, with `created: false` and `updated: true`.'
example:
success: true
data:
_id: ctc_xW8Ou6C03Csv8vatp
teamId: tea_8QvkOiBfPdb2ZRhHi
emails:
- value: jane.smith@example.com
fields:
firstName: Jane
lastName: Smith
jobTitle: Product Manager
ownerId: usr_ahfFktBBHUIxbVG5P
source: api
createdAt: '2025-10-28T00:40:37.917Z'
createdBy: usr_ahfFktBBHUIxbVG5P
unsubscribed: false
created: false
updated: true
companyLinked: false
'400':
description: Validation error
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
error:
type: object
properties:
code:
type: string
description: 'Error code: `MISSING_IDENTIFIER`, `INVALID_CONTACT_ID`, `CONTACT_NOT_FOUND_BY_ID`, `INVALID_EMAIL`, `INVALID_ADDITIONAL_EMAILS`, `INVALID_LINKEDIN_URL`, `INVALID_FIELD`, or `CONTACT_VALIDATION_FAILED`.'
message:
type: string
example:
success: false
error:
code: MISSING_IDENTIFIER
message: 'At least one identifier is required: email, linkedinUrl or contactId'
'401':
description: The authentication you supplied is incorrect
content:
text/plain:
example: The authentication you supplied is incorrect
parameters: []
servers:
- url: https://api.lemlist.com/api
/contacts/lists:
get:
summary: Get Contact Lists
description: Retrieves all static and dynamic contact lists for the team. Optionally filter by name.
tags:
- Contacts
parameters:
- name: search
in: query
required: false
description: Filter lists by name (case-insensitive partial match).
schema:
type: string
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
type: object
properties:
_id:
type: string
description: Unique list ID (`clt_xxx` format)
name:
type: string
description: Name of the contact list
dynamic:
type: boolean
description: Whether the list is dynamic (auto-populated by filters) or static (manually managed)
example:
- _id: clt_abc123def456ghi78
name: Nurturing
dynamic: false
- _id: clt_xyz789uvw012rst34
name: Enterprise Prospects
dynamic: true
'400':
description: Bad team
content:
text/plain:
example: Bad team
'401':
description: The authentication you supplied is incorrect
content:
text/plain:
example: The authentication you supplied is incorrect
'405':
description: Method not allowed
security:
- basicAuth: []
post:
summary: Create Contact List
description: Creates a new static contact list. After creating the list, use `POST /contacts/lists/{listId}/entities` to add contacts to it.
tags:
- Contacts
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
maxLength: 200
description: Name of the contact list to create (max 200 characters).
example:
name: Nurturing
responses:
'201':
description: Contact list created successfully
content:
application/json:
schema:
type: object
properties:
message:
type: string
list:
type: object
properties:
id:
type: string
description: Unique list ID (`clt_xxx` format)
name:
type: string
description: Name of the created list
example:
message: Contact list created successfully
list:
id: clt_abc123def456ghi78
name: Nurturing
'400':
description: 'Possible errors: Bad team / name is required / name too long'
content:
text/plain:
example: name is required and must be a non-empty string
'401':
description: The authentication you supplied is incorrect
content:
text/plain:
example: The authentication you supplied is incorrect
'405':
description: Method not allowed
security:
- basicAuth: []
servers:
- url: https://api.lemlist.com/api
/contacts/lists/{listId}/entities:
parameters:
- name: listId
in: path
required: true
description: The unique identifier of the contact list (`clt_xxx` format). Must be a **static** contact list.
example: clt_xxxxxxxxxxxxxxxxx
schema:
type: string
pattern: ^clt_[a-zA-Z0-9]+$
post:
summary: Add or Remove Contacts in a List
description: 'Adds existing CRM contacts to a static contact list, or removes them when `?action=remove` is set.
- **Add** (default): send a `POST` with a `contactIds` body. Contacts already in the list are silently skipped (no duplicates) and reported in `alreadyInList`.
- **Remove**: send a `POST` with `?action=remove` and a `contactIds` body. Contacts that are not in the list are silently skipped; `removedCount` reflects the associations actually removed. This only affects list membership, the contacts are not deleted from your CRM.
The list must be a **static** contact list (`clt_xxx`). Dynamic lists (auto-populated by filter rules) and company lists are rejected.
**Removal uses `POST ?action=remove` on purpose:** a `DELETE` request body is dropped by our stack, so `DELETE` on this path is **not supported** and returns `405 Method Not Allowed`.'
tags:
- Contacts
parameters:
- name: action
in: query
required: false
description: Omit to **add** the contacts to the list. Set to `remove` to **remove** them from the list.
example: remove
schema:
type: string
enum:
- remove
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- contactIds
properties:
contactIds:
type: array
items:
type: string
pattern: ^ctc_[a-zA-Z0-9]+$
minItems: 1
maxItems: 1000
description: Array of contact IDs (`ctc_xxx` format) to add to or remove from the list. Required, non-empty, maximum 1,000 per request.
example:
contactIds:
- ctc_xxxxxxxxxxxxxxxxx
- ctc_yyyyyyyyyyyyyyyyy
responses:
'200':
description: Contacts added to or removed from the list successfully. The response body depends on `action`.
content:
application/json:
schema:
type: object
properties:
message:
type: string
addedCount:
type: integer
description: Number of contacts added to the list (add only).
alreadyInList:
type: integer
description: Number of contacts that were already in the list and therefore skipped (add only).
removedCount:
type: integer
description: Number of list associations actually removed (remove only). Contacts that were not in the list are not counted.
examples:
add:
summary: Add contacts (no action)
value:
message: Contacts added to list successfully
addedCount: 2
alreadyInList: 0
remove:
summary: Remove contacts (action=remove)
value:
message: Contacts removed from list successfully
removedCount: 2
'400':
description: 'Possible errors: Bad team / Invalid listId format (expected clt_xxx) / contactIds is required and must be a non-empty array / Maximum 1,000 contacts per request / All contactIds must be in ctc_xxx format / List is not a contact list / Cannot manually add contacts to a dynamic list / Cannot manually remove contacts from a dynamic list.'
content:
text/plain:
example: contactIds is required and must be a non-empty array
'401':
description: The authentication you supplied is incorrect
content:
text/plain:
example: The authentication you supplied is incorrect
'404':
description: Contact list not found
content:
text/plain:
example: Contact list not found
'405':
description: Method not allowed. `DELETE` on this path is not supported because the request body is dropped by our stack. Use `POST ?action=remove` to remove contacts.
content:
text/plain:
example: Method not allowed
'500':
description: Internal error while updating the list
content:
text/plain:
example: Failed to add contacts to list
security:
- basicAuth: []
servers:
- url: https://api.lemlist.com/api
/contacts/export:
get:
summary: Export Contact List
description: 'Exports contacts or companies from a CRM list as a CSV file. The response is streamed as `text/csv` with a `Content-Disposition: attachment` header.
The exported columns depend on the `entity` type:
- **contact**: firstName, lastName, email, linkedinUrl, companyName, jobTitle, phone, status, owner, campaigns, contactLists, and more.
- **company**: name, domain, linkedinUrl, industry, website, status, size, owner, numberOfContacts, companyLists, and more.
Custom fields are included as additional columns.'
tags:
- Contacts
parameters:
- name: listId
in: query
required: true
description: The contact list ID to export (`clt_xxx` format), or `"all"` to export all contacts.
example: clt_abc123def456ghi78
schema:
type: string
- name: entity
in: query
required: false
description: The type of entities to export. Defaults to `"contact"`.
schema:
type: string
enum:
- contact
- company
default: contact
responses:
'200':
description: CSV file download
content:
text/csv:
example: 'firstName,lastName,email,companyName,jobTitle,status
John,Doe,john@example.com,Acme Inc,CEO,interested'
'400':
description: Bad team
content:
text/plain:
example: Bad team
'401':
description: Unauthorized
security:
- basicAuth: []
servers:
- url: https://api.lemlist.com/api
components:
schemas:
FieldRejection:
type: object
description: A value lemlist refused to write on a Contact or Company, with the reason why. Surfaced under `fieldRejections[]` on those objects; filter a list endpoint to only flagged records via the `fieldRejectionReason` query param.
properties:
field:
type: string
description: The record field the rejected value targeted (e.g. `emails`, `linkedinUrl`, `domain`).
reason:
type: string
description: Why the value was rejected, prefixed by its origin — `enrichment_*` (raised while enriching) or `crm_sync_*` (raised during CRM sync). Same values accepted by the `fieldRejectionReason` query param.
source:
type: string
description: Where the rejection came from — an enrichment source (`lemrich`) or a CRM provider (`hubspot`, `salesforce`, `pipedrive`).
conflictingRecordId:
type: string
description: For duplicate reasons, the id of the record that already holds the value — use it to merge or remap before resolving the duplicate. Omitted for non-duplicate reasons.
rejectedValue:
type: string
description: The value that was refused.
rejectedAt:
type: string
format: date-time
description: When the rejection was recorded.
Contact:
type: object
description: A contact record in your CRM. Not to be confused with a lead which is a contact specifically added to a campaign.
properties:
_id:
type: string
description: Unique contact identifier
teamId:
type: string
description: Team identifier the contact belongs to
fullName:
type: string
description: Contact's calculated full name
email:
type: string
format: email
description: Contact's primary email address
fields:
type: object
description: Custom fields associated with the contact
additionalProperties: true
campaigns:
type: array
description: List of campaigns the contact is associated with
items:
type: object
properties:
campaignId:
# --- truncated at 32 KB (33 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/lemlist/refs/heads/main/openapi/lemlist-contacts-api-openapi.yml