Developers
Connect your systems to Getlead CRM
Create, find and update leads and notes from your own website, app or backend — a REST API with one Bearer key, JSON in and out, and every result returned in the same response.
REST + JSON·Bearer key auth·100 requests a minute·All times UTC
V3 API
Quick start
Every path below is relative to one base URL. Requests and responses are JSON, and every request is processed synchronously — you get the result, or the error, in the same response. There is no tracking id to poll and no callback to wait for.
https://v3.getleadcrm.com/api/v1ik_, sent in the Authorization header1. Create an API key
API keys start with ik_ and are created inside Getlead CRM:
- Sign in at v3.getleadcrm.com and open Settings.
- Click All Integrations, then View Documentation on the API Integration card.
- Open the API Keys tab and click Generate New API Key. Give it a name that says where it will be used.
- Copy the key straight away and store it somewhere safe, such as your server's environment variables.
Treat the key like a password: it can read and change your business's leads. Keep it on your server — never in a web page or mobile app a customer can open. Use a separate, clearly named key for each integration, and if one leaks, generate a replacement and stop using the old key.
2. Send it on every request
Pass the key as a Bearer token in the Authorization header, together with a JSON content type:
Authorization: Bearer ik_YOUR_API_KEY_HERE
Content-Type: application/json
3. Make your first call
Start with GET /meta. It needs no parameters and returns the ids of your business's sources, statuses, purposes, custom fields, branches and agents — the ids every other endpoint uses. Replace ik_YOUR_API_KEY_HERE with your key and run:
curl -X GET \
"https://v3.getleadcrm.com/api/v1/meta" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json"
A 200 response with a data object means your key works. A 401 means the key is missing, mistyped or deleted. Then create your first lead with POST /leads, using a source_id from the /meta response.
Rate limit
Each API key can make 100 requests a minute. Above that, the API answers 429 Too Many Requests with a retry_after value in seconds — wait that long, then retry, backing off exponentially if it happens again:
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Call /meta once and cache the ids rather than calling it before every request.
Dates and times are UTC
Everything is UTC, in both directions — no business timezone is applied anywhere in the API.
- Responses return ISO 8601 UTC with a trailing
Z, e.g.2026-06-18T10:00:00.000000Z. - Date filters take a full ISO 8601 datetime, e.g.
2026-01-31T18:30:00Z. A bare date such as2026-01-31is rejected — convert your local day boundary to UTC first (midnight 1 February in India is2026-01-31T18:30:00Z).
Status codes and errors
| Code | Meaning |
|---|---|
200 OK | GET requests, and PATCH /leads/by-identifier (returns the lead id). |
201 Created | POST /leads and the note endpoints — returns the new id. |
204 No Content | PATCH /leads/{lead_id} and DELETE a note — done, no body. |
401 Unauthorized | Missing or invalid API key. |
404 Not Found | No lead (or note) with that id or identifier in your business. |
422 Unprocessable | Validation error or business-rule failure: an unknown id, a duplicate phone or email, or a status change the workflow does not allow. |
429 Too Many Requests | Rate limit exceeded — wait retry_after seconds. |
5xx | Server error — retry with exponential backoff. |
Errors carry a machine-readable code, a human-readable message and, where useful, the ids involved:
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
{
"error": {
"code": "lead_status_must_belong_to_business",
"message": "The selected lead status does not belong to this business.",
"context": { "status_id": "STATUS_ID" }
}
}
Plain validation errors (for example a malformed filter) return a message plus an errors object keyed by field, and 404 returns a single message.
Endpoints
- GET
/meta - GET
/leads - GET
/leads/{lead_id} - POST
/leads - PATCH
/leads/{lead_id} - PATCH
/leads/by-identifier - POST
/leads/{lead_id}/notes - POST
/leads/by-identifier/notes - DELETE
/leads/{lead_id}/notes/{note_id}
In the examples, ik_YOUR_API_KEY_HERE, {lead_id}, {note_id} and the …_ID_FROM_META values are placeholders — replace them with your own. Ids are 26-character ULIDs. The custom_fields keys (districts, category) are examples too: use the field_key values your own /meta returns.
Fetch your reference data
/api/v1/metaReturns your business's active sources, statuses, purposes, custom fields, branches and agents. Call it first and cache the ids: creating, updating and filtering leads all reference these ids, never names.
Parameters
None.
Responses
200 OK — your active reference data
{
"data": {
"sources": [
{
"id": "01HG3JSOURCE...",
"name": "Website"
}
],
"statuses": [
{
"id": "01HG3JSTATUS...",
"name": "New Lead"
}
],
"purposes": [
{
"id": "01HG3JPURPOSE...",
"name": "General Inquiry"
}
],
"custom_fields": [
{
"field_key": "industry",
"field_type": "select",
"allowed_values": [
{
"value": "tech",
"label": "Technology"
}
]
}
],
"branches": [
{
"id": "01HG3JBRANCH...",
"name": "Downtown"
}
],
"agents": [
{
"id": "01HG3JAGENT...",
"name": "Priya Nair",
"email": "[email protected]"
}
]
}
}
401 Unauthorized — missing or invalid API key
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
429 Too Many Requests — rate limit exceeded; wait retry_after seconds
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Code examples
curl -X GET \
"https://v3.getleadcrm.com/api/v1/meta" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json"
const response = await fetch('https://v3.getleadcrm.com/api/v1/meta', {
method: 'GET',
headers: {
'Authorization': 'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
},
});
// 201/200 return { data: { lead_id } }; PATCH /leads/{id} returns 204 (no body)
const data = response.status === 204 ? null : await response.json();
console.log(data);
<?php
$ch = curl_init('https://v3.getleadcrm.com/api/v1/meta');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ik_YOUR_API_KEY_HERE',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
curl_close($ch);
print_r($data);
import requests
url = 'https://v3.getleadcrm.com/api/v1/meta'
headers = {
'Authorization': f'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
}
response = requests.get(url, headers=headers)
print(response.status_code, response.text)
List leads
/api/v1/leadsList the business's leads, newest first. Cursor-paginated: read meta.next_cursor and pass it back as cursor until it comes back null. Each item is the SAME object shape as GET /leads/{lead_id}, so one parser covers both. Every filter id comes from /meta. A newly created lead is listed immediately, but an edit to an existing lead can take a few minutes to show up here — GET /leads/{lead_id} always reads live.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| Filters | ||
search | string | Free-text match across name, email and phone numbers. Max 255 characters. |
status | string[] | Status ULIDs from /meta. Repeatable (status[]=), or pass a single bare value. Matches any of the given ids. |
source | string[] | Source ULIDs from /meta. Repeatable (source[]=), or pass a single bare value. Matches any of the given ids. |
purposes | string[] | Purpose ULIDs from /meta. Repeatable (purposes[]=), or pass a single bare value. Matches any of the given ids. |
branches | string[] | Branch ULIDs from /meta. Repeatable (branches[]=), or pass a single bare value. Matches any of the given ids. |
assigned_to | string[] | Agent ULIDs from /meta — the lead's current assignee. Repeatable (assigned_to[]=), or pass a single bare value. Matches any of the given ids. |
created_by | string[] | Agent ULIDs from /meta — who created the lead. Repeatable (created_by[]=), or pass a single bare value. Matches any of the given ids. |
is_assigned | boolean | True returns only assigned leads, false only unassigned. Accepts true, false, 1 or 0. Omit to not filter on it. |
is_starred | boolean | Whether the lead is starred. Accepts true, false, 1 or 0. Omit to not filter on it. |
has_deals | boolean | Whether the lead has at least one deal. Accepts true, false, 1 or 0. Omit to not filter on it. |
has_name | boolean | Whether the lead has a name set. Accepts true, false, 1 or 0. Omit to not filter on it. |
has_follow_up | boolean | Whether the lead has an open task of any type, overdue included. Accepts true, false, 1 or 0. Omit to not filter on it. |
has_purpose | boolean | Whether the lead has at least one purpose. Accepts true, false, 1 or 0. Omit to not filter on it. |
has_branch | boolean | Whether the lead belongs to at least one branch. Accepts true, false, 1 or 0. Omit to not filter on it. |
custom_fields[{field_key}] | string | Filter on a custom field, e.g. custom_fields[industry]=tech. The key must be a field_key from /meta — a key this business does not have is ignored, not rejected. Repeat the same key (custom_fields[industry][]=) to match any of several values. |
| Date ranges | ||
created_at[from] | string | Earliest point (inclusive) at which the lead was created. A full ISO 8601 UTC datetime only (2026-01-31T18:30:00, optionally with Z or an offset) — a bare date is rejected. Either bound may be sent on its own; the other end then runs to now. |
created_at[to] | string | Latest point (inclusive) at which the lead was created. A full ISO 8601 UTC datetime only (2026-01-31T18:30:00, optionally with Z or an offset) — a bare date is rejected. Either bound may be sent on its own; the other end then runs to the earliest record. |
last_contacted_at[from] | string | Earliest point (inclusive) at which the lead was last contacted. A full ISO 8601 UTC datetime only (2026-01-31T18:30:00, optionally with Z or an offset) — a bare date is rejected. Either bound may be sent on its own; the other end then runs to now. |
last_contacted_at[to] | string | Latest point (inclusive) at which the lead was last contacted. A full ISO 8601 UTC datetime only (2026-01-31T18:30:00, optionally with Z or an offset) — a bare date is rejected. Either bound may be sent on its own; the other end then runs to the earliest record. |
last_activity_at[from] | string | Earliest point (inclusive) at which the lead last had activity. A full ISO 8601 UTC datetime only (2026-01-31T18:30:00, optionally with Z or an offset) — a bare date is rejected. Either bound may be sent on its own; the other end then runs to now. |
last_activity_at[to] | string | Latest point (inclusive) at which the lead last had activity. A full ISO 8601 UTC datetime only (2026-01-31T18:30:00, optionally with Z or an offset) — a bare date is rejected. Either bound may be sent on its own; the other end then runs to the earliest record. |
last_assigned_at[from] | string | Earliest point (inclusive) at which the lead was last assigned. A full ISO 8601 UTC datetime only (2026-01-31T18:30:00, optionally with Z or an offset) — a bare date is rejected. Either bound may be sent on its own; the other end then runs to now. |
last_assigned_at[to] | string | Latest point (inclusive) at which the lead was last assigned. A full ISO 8601 UTC datetime only (2026-01-31T18:30:00, optionally with Z or an offset) — a bare date is rejected. Either bound may be sent on its own; the other end then runs to the earliest record. |
| Pagination & sorting | ||
sort_by | string | One of created_at, last_contacted_at, last_activity_at. Anything else falls back to created_at. Default created_at. |
sort_direction | string | asc or desc. Default desc. |
per_page | integer | Leads per page, 1–50. Default 20. |
cursor | string | Opaque cursor from a previous response's meta.next_cursor. Omit for the first page. |
Responses
200 OK — a page of leads
{
"data": [
{
"id": "01HG3JQABC...",
"name": "Jane Doe",
"email": "[email protected]",
"notes": null,
"phone_numbers": [
{
"number": "+14155551234",
"type": "mobile",
"is_primary": true
}
],
"source": {
"id": "01HG3JSOURCE...",
"name": "Website"
},
"status": {
"id": "01HG3JSTATUS...",
"name": "Qualified"
},
"purposes": [
{
"id": "01HG3JPURPOSE...",
"name": "Demo"
}
],
"branches": [
{
"id": "01HG3JBRANCH...",
"name": "Downtown"
}
],
"custom_fields": [
{
"field_key": "industry",
"value": "tech"
}
],
"assigned_to_id": "01HG3JAGENT...",
"is_starred": false,
"last_contacted_at": "2026-06-18T10:00:00.000000Z",
"last_activity_at": "2026-06-18T10:00:00.000000Z",
"last_assigned_at": "2026-06-17T09:00:00.000000Z",
"created_at": "2026-06-18T10:00:00.000000Z"
}
],
"meta": {
"per_page": 20,
"next_cursor": "eyJpZCI6MTIzfQ",
"prev_cursor": null
}
}
401 Unauthorized — missing or invalid API key
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
422 Unprocessable — an invalid filter value, a bare date, or a date range whose [to] is before its [from]
{
"message": "The created_at.to field must be a date after or equal to created_at.from.",
"errors": {
"created_at.to": [
"The created_at.to field must be a date after or equal to created_at.from."
]
}
}
429 Too Many Requests — rate limit exceeded; wait retry_after seconds
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Code examples
curl -X GET \
"https://v3.getleadcrm.com/api/v1/leads?status%5B%5D=STATUS_ID_FROM_META&created_at%5Bfrom%5D=2026-01-01T00%3A00%3A00Z&created_at%5Bto%5D=2026-01-31T23%3A59%3A59Z&per_page=20" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json"
const response = await fetch('https://v3.getleadcrm.com/api/v1/leads?status%5B%5D=STATUS_ID_FROM_META&created_at%5Bfrom%5D=2026-01-01T00%3A00%3A00Z&created_at%5Bto%5D=2026-01-31T23%3A59%3A59Z&per_page=20', {
method: 'GET',
headers: {
'Authorization': 'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
},
});
// 201/200 return { data: { lead_id } }; PATCH /leads/{id} returns 204 (no body)
const data = response.status === 204 ? null : await response.json();
console.log(data);
<?php
$ch = curl_init('https://v3.getleadcrm.com/api/v1/leads?status%5B%5D=STATUS_ID_FROM_META&created_at%5Bfrom%5D=2026-01-01T00%3A00%3A00Z&created_at%5Bto%5D=2026-01-31T23%3A59%3A59Z&per_page=20');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ik_YOUR_API_KEY_HERE',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
curl_close($ch);
print_r($data);
import requests
url = 'https://v3.getleadcrm.com/api/v1/leads?status%5B%5D=STATUS_ID_FROM_META&created_at%5Bfrom%5D=2026-01-01T00%3A00%3A00Z&created_at%5Bto%5D=2026-01-31T23%3A59%3A59Z&per_page=20'
headers = {
'Authorization': f'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
}
response = requests.get(url, headers=headers)
print(response.status_code, response.text)
Fetch a single lead
/api/v1/leads/{lead_id}Returns one lead by its id, always read live.
Path parameters
| Parameter | Type | Description |
|---|---|---|
lead_id required | id | The lead's id, as returned by POST /leads or GET /leads. |
Responses
200 OK — the lead
{
"data": {
"id": "01HG3JQABC...",
"name": "Jane Doe",
"email": "[email protected]",
"notes": null,
"phone_numbers": [
{
"number": "+14155551234",
"type": "mobile",
"is_primary": true
}
],
"source": {
"id": "01HG3JSOURCE...",
"name": "Website"
},
"status": {
"id": "01HG3JSTATUS...",
"name": "Qualified"
},
"purposes": [
{
"id": "01HG3JPURPOSE...",
"name": "Demo"
}
],
"custom_fields": [
{
"field_key": "industry",
"value": "tech"
}
],
"assigned_to_id": null,
"created_at": "2026-06-18T10:00:00.000000Z"
}
}
401 Unauthorized — missing or invalid API key
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
404 Not Found — no lead with that id in your business
{
"message": "Lead not found"
}
429 Too Many Requests — rate limit exceeded; wait retry_after seconds
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Code examples
curl -X GET \
"https://v3.getleadcrm.com/api/v1/leads/{lead_id}" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json"
const response = await fetch('https://v3.getleadcrm.com/api/v1/leads/{lead_id}', {
method: 'GET',
headers: {
'Authorization': 'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
},
});
// 201/200 return { data: { lead_id } }; PATCH /leads/{id} returns 204 (no body)
const data = response.status === 204 ? null : await response.json();
console.log(data);
<?php
$ch = curl_init('https://v3.getleadcrm.com/api/v1/leads/{lead_id}');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ik_YOUR_API_KEY_HERE',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
curl_close($ch);
print_r($data);
import requests
url = 'https://v3.getleadcrm.com/api/v1/leads/{lead_id}'
headers = {
'Authorization': f'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
}
response = requests.get(url, headers=headers)
print(response.status_code, response.text)
Create a lead
/api/v1/leadsCreates a lead and returns its id. Only phone_numbers is required.
Body parameters
| Field | Type | Description |
|---|---|---|
phone_numbers required | string[] | One to 5 numbers in E.164 format (leading + and country code, e.g. +919876543210). A single string is also accepted. The first number is the primary one. |
name | string | Full name, up to 255 characters. |
email | string | A valid email address, up to 255 characters. |
source_id | id | A source id from /meta. When omitted, the lead is attributed to the "API" source. |
status_id | id | A status id from /meta. |
purpose_ids | id[] | Purpose ids from /meta. |
assigned_to_id | id | An active agent's id from /meta. |
notes | string | Plain-text notes, up to 1,000 characters. Line breaks are kept. |
custom_fields | object | Your business's custom fields, keyed by field_key from /meta. For select fields send the value, not the label. Custom fields marked required in your account must be included. |
Request body
{
"name": "John Doe",
"email": "[email protected]",
"phone_numbers": [
"+14155551234"
],
"source_id": "SOURCE_ID_FROM_META",
"status_id": "STATUS_ID_FROM_META",
"purpose_ids": [
"PURPOSE_ID_FROM_META"
],
"custom_fields": {
"districts": "kasaragod",
"category": "digital_marketing"
}
}
Responses
201 Created — the new lead id
{
"data": {
"lead_id": "01HG3JQABC..."
}
}
401 Unauthorized — missing or invalid API key
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
422 Unprocessable — validation error or business-rule failure (e.g. unknown id, duplicate phone or email)
{
"error": {
"code": "lead_status_must_belong_to_business",
"message": "The selected lead status does not belong to this business.",
"context": {
"status_id": "01HG3JSTATUS...",
"business_id": "01HG3JBUSINESS..."
}
}
}
429 Too Many Requests — rate limit exceeded; wait retry_after seconds
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Code examples
curl -X POST \
"https://v3.getleadcrm.com/api/v1/leads" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "[email protected]",
"phone_numbers": [
"+14155551234"
],
"source_id": "SOURCE_ID_FROM_META",
"status_id": "STATUS_ID_FROM_META",
"purpose_ids": [
"PURPOSE_ID_FROM_META"
],
"custom_fields": {
"districts": "kasaragod",
"category": "digital_marketing"
}
}'
const response = await fetch('https://v3.getleadcrm.com/api/v1/leads', {
method: 'POST',
headers: {
'Authorization': 'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"name": "John Doe",
"email": "[email protected]",
"phone_numbers": [
"+14155551234"
],
"source_id": "SOURCE_ID_FROM_META",
"status_id": "STATUS_ID_FROM_META",
"purpose_ids": [
"PURPOSE_ID_FROM_META"
],
"custom_fields": {
"districts": "kasaragod",
"category": "digital_marketing"
}
}),
});
// 201/200 return { data: { lead_id } }; PATCH /leads/{id} returns 204 (no body)
const data = response.status === 204 ? null : await response.json();
console.log(data);
<?php
$ch = curl_init('https://v3.getleadcrm.com/api/v1/leads');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ik_YOUR_API_KEY_HERE',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"name":"John Doe","email":"[email protected]","phone_numbers":["+14155551234"],"source_id":"SOURCE_ID_FROM_META","status_id":"STATUS_ID_FROM_META","purpose_ids":["PURPOSE_ID_FROM_META"],"custom_fields":{"districts":"kasaragod","category":"digital_marketing"}}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
curl_close($ch);
print_r($data);
import requests
url = 'https://v3.getleadcrm.com/api/v1/leads'
headers = {
'Authorization': f'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
}
data = {
'name': 'John Doe',
'email': '[email protected]',
'phone_numbers': [
'+14155551234'
],
'source_id': 'SOURCE_ID_FROM_META',
'status_id': 'STATUS_ID_FROM_META',
'purpose_ids': [
'PURPOSE_ID_FROM_META'
],
'custom_fields': {
'districts': 'kasaragod',
'category': 'digital_marketing'
}
}
response = requests.post(url, headers=headers, json=data)
print(response.status_code, response.text)
Update a lead
/api/v1/leads/{lead_id}Partially updates a lead by id. Send only the fields you want to change — anything you leave out stays as it is. To change only the status, send just {"status_id": "…"}; status changes still follow your configured workflow.
Parameters
lead_id in the path (required), plus any of the body fields from Create a lead — all optional here.
Request body
{
"name": "John Doe",
"email": "[email protected]",
"phone_numbers": [
"+14155551234"
],
"source_id": "SOURCE_ID_FROM_META",
"status_id": "STATUS_ID_FROM_META",
"purpose_ids": [
"PURPOSE_ID_FROM_META"
],
"custom_fields": {
"districts": "kasaragod",
"category": "digital_marketing"
}
}
Responses
204 No Content — the lead was updated
No response body.
401 Unauthorized — missing or invalid API key
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
404 Not Found — no lead with that id in your business
{
"message": "Lead not found"
}
422 Unprocessable — validation error, unknown id, or a status change the workflow does not allow
{
"error": {
"code": "status_transition_must_be_valid",
"message": "The status transition is not allowed according to the configured workflow."
}
}
429 Too Many Requests — rate limit exceeded; wait retry_after seconds
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Code examples
curl -X PATCH \
"https://v3.getleadcrm.com/api/v1/leads/{lead_id}" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "[email protected]",
"phone_numbers": [
"+14155551234"
],
"source_id": "SOURCE_ID_FROM_META",
"status_id": "STATUS_ID_FROM_META",
"purpose_ids": [
"PURPOSE_ID_FROM_META"
],
"custom_fields": {
"districts": "kasaragod",
"category": "digital_marketing"
}
}'
const response = await fetch('https://v3.getleadcrm.com/api/v1/leads/{lead_id}', {
method: 'PATCH',
headers: {
'Authorization': 'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"name": "John Doe",
"email": "[email protected]",
"phone_numbers": [
"+14155551234"
],
"source_id": "SOURCE_ID_FROM_META",
"status_id": "STATUS_ID_FROM_META",
"purpose_ids": [
"PURPOSE_ID_FROM_META"
],
"custom_fields": {
"districts": "kasaragod",
"category": "digital_marketing"
}
}),
});
// 201/200 return { data: { lead_id } }; PATCH /leads/{id} returns 204 (no body)
const data = response.status === 204 ? null : await response.json();
console.log(data);
<?php
$ch = curl_init('https://v3.getleadcrm.com/api/v1/leads/{lead_id}');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ik_YOUR_API_KEY_HERE',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"name":"John Doe","email":"[email protected]","phone_numbers":["+14155551234"],"source_id":"SOURCE_ID_FROM_META","status_id":"STATUS_ID_FROM_META","purpose_ids":["PURPOSE_ID_FROM_META"],"custom_fields":{"districts":"kasaragod","category":"digital_marketing"}}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
curl_close($ch);
print_r($data);
import requests
url = 'https://v3.getleadcrm.com/api/v1/leads/{lead_id}'
headers = {
'Authorization': f'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
}
data = {
'name': 'John Doe',
'email': '[email protected]',
'phone_numbers': [
'+14155551234'
],
'source_id': 'SOURCE_ID_FROM_META',
'status_id': 'STATUS_ID_FROM_META',
'purpose_ids': [
'PURPOSE_ID_FROM_META'
],
'custom_fields': {
'districts': 'kasaragod',
'category': 'digital_marketing'
}
}
response = requests.patch(url, headers=headers, json=data)
print(response.status_code, response.text)
Update a lead by phone or email
/api/v1/leads/by-identifierThe same partial update, but the lead is found by phone number or email instead of its id — useful when you do not store our lead id. It returns the matched lead's id so you can cache it.
Body parameters
| Field | Type | Description |
|---|---|---|
phone_number | string | E.164 phone number of the lead to update. Required when email is not sent. |
email | string | Email of the lead to update. Required when phone_number is not sent. |
| any update field | Any field from Create a lead you want to change. |
Request body
{
"phone_number": "+14155551234",
"name": "John Doe",
"status_id": "STATUS_ID_FROM_META"
}
Responses
200 OK — the matched lead id, so you can cache it
{
"data": {
"lead_id": "01HG3JQABC..."
}
}
401 Unauthorized — missing or invalid API key
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
404 Not Found — no lead matches the phone number or email
{
"message": "Lead not found with the provided identifier"
}
422 Unprocessable — validation error or business-rule failure (e.g. unknown id, duplicate phone or email)
{
"error": {
"code": "lead_status_must_belong_to_business",
"message": "The selected lead status does not belong to this business.",
"context": {
"status_id": "01HG3JSTATUS...",
"business_id": "01HG3JBUSINESS..."
}
}
}
429 Too Many Requests — rate limit exceeded; wait retry_after seconds
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Code examples
curl -X PATCH \
"https://v3.getleadcrm.com/api/v1/leads/by-identifier" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+14155551234",
"name": "John Doe",
"status_id": "STATUS_ID_FROM_META"
}'
const response = await fetch('https://v3.getleadcrm.com/api/v1/leads/by-identifier', {
method: 'PATCH',
headers: {
'Authorization': 'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"phone_number": "+14155551234",
"name": "John Doe",
"status_id": "STATUS_ID_FROM_META"
}),
});
// 201/200 return { data: { lead_id } }; PATCH /leads/{id} returns 204 (no body)
const data = response.status === 204 ? null : await response.json();
console.log(data);
<?php
$ch = curl_init('https://v3.getleadcrm.com/api/v1/leads/by-identifier');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ik_YOUR_API_KEY_HERE',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"phone_number":"+14155551234","name":"John Doe","status_id":"STATUS_ID_FROM_META"}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
curl_close($ch);
print_r($data);
import requests
url = 'https://v3.getleadcrm.com/api/v1/leads/by-identifier'
headers = {
'Authorization': f'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
}
data = {
'phone_number': '+14155551234',
'name': 'John Doe',
'status_id': 'STATUS_ID_FROM_META'
}
response = requests.patch(url, headers=headers, json=data)
print(response.status_code, response.text)
Add a note to a lead
/api/v1/leads/{lead_id}/notesAdds a note to a lead's timeline and returns the new note's id.
Parameters
| Field | Type | Description |
|---|---|---|
lead_id required | id (path) | The lead to add the note to. |
content | string | The note text. |
title | string | A short title for the note. |
Request body
{
"content": "Called the customer, will follow up next week.",
"title": "Follow up"
}
Responses
201 Created — the new note id
{
"data": {
"note_id": "01HG3JNOTE..."
}
}
401 Unauthorized — missing or invalid API key
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
404 Not Found — no lead with that id in your business
{
"message": "Lead not found"
}
429 Too Many Requests — rate limit exceeded; wait retry_after seconds
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Code examples
curl -X POST \
"https://v3.getleadcrm.com/api/v1/leads/{lead_id}/notes" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"content": "Called the customer, will follow up next week.",
"title": "Follow up"
}'
const response = await fetch('https://v3.getleadcrm.com/api/v1/leads/{lead_id}/notes', {
method: 'POST',
headers: {
'Authorization': 'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"content": "Called the customer, will follow up next week.",
"title": "Follow up"
}),
});
// 201/200 return { data: { lead_id } }; PATCH /leads/{id} returns 204 (no body)
const data = response.status === 204 ? null : await response.json();
console.log(data);
<?php
$ch = curl_init('https://v3.getleadcrm.com/api/v1/leads/{lead_id}/notes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ik_YOUR_API_KEY_HERE',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"content":"Called the customer, will follow up next week.","title":"Follow up"}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
curl_close($ch);
print_r($data);
import requests
url = 'https://v3.getleadcrm.com/api/v1/leads/{lead_id}/notes'
headers = {
'Authorization': f'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
}
data = {
'content': 'Called the customer, will follow up next week.',
'title': 'Follow up'
}
response = requests.post(url, headers=headers, json=data)
print(response.status_code, response.text)
Add a note by phone or email
/api/v1/leads/by-identifier/notesThe same as adding a note by id, but the lead is found by phone_number or email (one is required), sent in the body alongside content and title.
Request body
{
"phone_number": "+14155551234",
"content": "Called the customer, will follow up next week.",
"title": "Follow up"
}
Responses
201 Created — the new note id
{
"data": {
"note_id": "01HG3JNOTE..."
}
}
401 Unauthorized — missing or invalid API key
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
404 Not Found — no lead matches the phone number or email
{
"message": "Lead not found with the provided identifier"
}
422 Unprocessable — validation error, or neither phone_number nor email was sent
{
"message": "The phone number field is required when email is not present."
}
429 Too Many Requests — rate limit exceeded; wait retry_after seconds
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Code examples
curl -X POST \
"https://v3.getleadcrm.com/api/v1/leads/by-identifier/notes" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+14155551234",
"content": "Called the customer, will follow up next week.",
"title": "Follow up"
}'
const response = await fetch('https://v3.getleadcrm.com/api/v1/leads/by-identifier/notes', {
method: 'POST',
headers: {
'Authorization': 'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"phone_number": "+14155551234",
"content": "Called the customer, will follow up next week.",
"title": "Follow up"
}),
});
// 201/200 return { data: { lead_id } }; PATCH /leads/{id} returns 204 (no body)
const data = response.status === 204 ? null : await response.json();
console.log(data);
<?php
$ch = curl_init('https://v3.getleadcrm.com/api/v1/leads/by-identifier/notes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ik_YOUR_API_KEY_HERE',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"phone_number":"+14155551234","content":"Called the customer, will follow up next week.","title":"Follow up"}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
curl_close($ch);
print_r($data);
import requests
url = 'https://v3.getleadcrm.com/api/v1/leads/by-identifier/notes'
headers = {
'Authorization': f'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
}
data = {
'phone_number': '+14155551234',
'content': 'Called the customer, will follow up next week.',
'title': 'Follow up'
}
response = requests.post(url, headers=headers, json=data)
print(response.status_code, response.text)
Delete a note
/api/v1/leads/{lead_id}/notes/{note_id}Deletes one note from a lead.
Path parameters
| Parameter | Type | Description |
|---|---|---|
lead_id required | id | The lead the note belongs to. |
note_id required | id | The note's id, as returned when it was created. |
Responses
204 No Content — the note was deleted
No response body.
401 Unauthorized — missing or invalid API key
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid API key provided."
}
}
404 Not Found — the note does not exist, or does not belong to that lead
{
"message": "Note not found"
}
429 Too Many Requests — rate limit exceeded; wait retry_after seconds
{
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
Code examples
curl -X DELETE \
"https://v3.getleadcrm.com/api/v1/leads/{lead_id}/notes/{note_id}" \
-H "Authorization: Bearer ik_YOUR_API_KEY_HERE" \
-H "Content-Type: application/json"
const response = await fetch('https://v3.getleadcrm.com/api/v1/leads/{lead_id}/notes/{note_id}', {
method: 'DELETE',
headers: {
'Authorization': 'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
},
});
// 201/200 return { data: { lead_id } }; PATCH /leads/{id} returns 204 (no body)
const data = response.status === 204 ? null : await response.json();
console.log(data);
<?php
$ch = curl_init('https://v3.getleadcrm.com/api/v1/leads/{lead_id}/notes/{note_id}');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ik_YOUR_API_KEY_HERE',
'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$data = json_decode($response, true);
curl_close($ch);
print_r($data);
import requests
url = 'https://v3.getleadcrm.com/api/v1/leads/{lead_id}/notes/{note_id}'
headers = {
'Authorization': f'Bearer ik_YOUR_API_KEY_HERE',
'Content-Type': 'application/json',
}
response = requests.delete(url, headers=headers)
print(response.status_code, response.text)
Legacy · V2 API
V2 API reference
All V2 endpoints live under app.getleadcrm.com/api, accept POST requests, and return a JSON object with a status and a message.
| Create lead | /api/gl-website-contacts |
|---|---|
| Create task | /api/gl-task-creation-api |
| Create note | /api/gl-note-creation-api |
| Change status | /api/gl-status-change-api |
| Create deal | /api/gl-deal-creation-api |
| Assign to department | /api/gl-department-vise-agent-assign/{token} |
Example: create a lead. For this endpoint, token, countrycode, mobileno and source are required; name, email, company_name, staff_name, department, status and custom additional fields are optional.
# Create a lead (V2 — POST, parameters passed as documented)
curl -X POST "https://app.getleadcrm.com/api/gl-website-contacts?token=YOUR_API_TOKEN&name=Asha%20Menon&countrycode=91&mobileno=9895000000&source=Website"
# Success response
{
"status": "success",
"message": "Lead Added Successfully."
}
# Error response (validation example)
{
"status": "fail",
"message": "The mobileno must be between 8 and 14 digits."
}
V2 authentication
V2 requests authenticate with an API token (prefixed gl_) passed as the token parameter. Generate it inside the app:
- Open your profile dropdown and click Settings.
- In the Getlead settings menu, click GL Connect.
- Open the API menu.
- On the API page, click the Show / generate token button.
- Copy the token from the popup and pass it as the
tokenparameter.
Treat the token like a password — it grants write access to your account's leads, tasks and deals. If it leaks, regenerate it from the same screen.
FAQ
Developer questions, answered
How do I get a Getlead API key?
Sign in to Getlead CRM, open Settings, click All Integrations, then View Documentation on the API Integration card. On the API Keys tab, click Generate New API Key and copy the key — it starts with ik_. Send it as a Bearer token in the Authorization header of every request.
What can I do with the Getlead V3 API?
Fetch your reference data (sources, statuses, purposes, custom fields, branches and agents), list and search leads with filters and cursor pagination, fetch a single lead, create leads, update leads by id or by phone number or email, and add or delete notes on a lead.
What is the Getlead API rate limit?
Each API key can make 100 requests a minute. Above that the API returns HTTP 429 with a retry_after value in seconds — wait that long, then retry with exponential backoff.
Which timezone does the Getlead API use?
UTC, in both directions. Responses return ISO 8601 UTC times ending in Z, and date filters need a full ISO 8601 datetime — convert your local day boundary to UTC before sending it, because a bare date is rejected.
Is API access included in every Getlead plan?
Yes. API access is part of the single plan at $9 per user per month billed yearly, or $12 billed monthly. Getlead Standalone, the dedicated deployment for larger teams, adds the highest rate limits and a robust sandbox.
Do my V2 API integrations still work?
The V2 endpoints under app.getleadcrm.com/api are documented in the Legacy tab of this page for integrations already built on them. New integrations should use the V3 API at v3.getleadcrm.com/api/v1.
Where do I get help if an API request fails?
Start with the error the API returns — it carries a machine-readable code and a message naming the field or id at fault. If you are still stuck, the support team is reachable by phone, WhatsApp and email.
Stuck on an integration? Check the help centre or contact the team.
The fastest reply usually wins the deal
Reply in 30 seconds,
not 30 minutes.
Getlead catches the enquiry, alerts the right rep, and keeps every follow-up on track — so speed stops being luck.
No credit card required·Every feature unlocked·Cancel anytime