The GoHighLevel API is how you connect HighLevel to anything its built-in integrations don't cover: a custom booking app, a database, an ERP, a client portal or your own AI agent. This guide covers what you need to get it working in production: authentication, headers, rate limits, the endpoints most builds use, webhooks and the mistakes that cause most failed integrations.
1. The basics and the API documentation
- Base URL:
https://services.leadconnectorhq.com. API traffic goes to the LeadConnector domain, not gohighlevel.com. - Current API: v2. The old v1 API with location API keys is end-of-life and no longer maintained, so new work should not use it.
- Every request needs a bearer token and a
Versionheader. - GoHighLevel API documentation: the GHL API documentation lives in the developer portal at marketplace.gohighlevel.com/docs. Each endpoint page shows the exact
Versionvalue and scopes it expects.
Authorization: Bearer <your token>
Version: 2021-07-28
Content-Type: application/json
Accept: application/jsonMany endpoints take 2021-07-28, but newer reference pages (contacts, for example) show v3. Don't guess: copy the value from each endpoint's page and keep it in one constant in your code, so a future version change is a one-line fix.
2. Getting a GoHighLevel API key
Older tutorials tell you to copy an "API key" from the sub-account settings. That was the v1 API, which is end-of-life. In v2 the "API key" is a Private Integration token: go to Settings → Private Integrations → Create new Integration, name it, tick only the scopes you need, and copy the token straight away, because it's only shown once. For apps used by many accounts, use OAuth instead (below).
3. Authentication: Private Integration token or OAuth?
HighLevel supports two ways to authenticate with the v2 API.
| Private Integration token | OAuth 2.0 (Marketplace app) | |
|---|---|---|
| Best for | Your own integration with one sub-account or your agency | Apps installed on many sub-accounts, or anything you sell |
| How you get it | Settings → Private Integrations → Create new Integration, pick scopes, copy the token | Create an app in the Marketplace developer portal, then run the OAuth install flow |
| Token life | Static. Rotate every 90 days (old and new both work for 7 days) | Access tokens expire daily and must be refreshed |
| Webhooks | Not available | Available (50+ event types) |
| Watch out for | The token is shown once. Store it as a secret straight away | Refresh logic and token storage per install |
Rule of thumb: one business connecting its own systems → Private Integration token. Anything installed across many sub-accounts, or anything that needs HighLevel to push events to you → OAuth. If you don't see Private Integrations in settings, the HighLevel docs say to check it's enabled in Labs.
Only give a token the scopes it needs. A token that can only read and write contacts does far less damage if it leaks than one with full access.
4. Rate limits
- Burst: 100 requests per 10 seconds.
- Daily: 200,000 requests per day.
- Both are counted per app per resource (one sub-account or one agency), so each install has its own budget.
- Responses include headers such as
X-RateLimit-RemainingandX-RateLimit-Daily-Remaining. Read them and slow down before you hit a429.
For imports and syncs, queue the work and retry 429 responses with exponential backoff. If you serve several sub-accounts, back off per sub-account, not globally:
async function ghl(path, options = {}, attempt = 0) {
const res = await fetch("https://services.leadconnectorhq.com" + path, {
...options,
headers: {
Authorization: "Bearer " + process.env.GHL_TOKEN,
Version: GHL_VERSION, // one constant, not scattered strings
"Content-Type": "application/json",
...options.headers,
},
});
if (res.status === 429 && attempt < 5) {
// back off, with a little jitter, then retry
const wait = 1000 * 2 ** attempt + Math.random() * 250;
await new Promise((r) => setTimeout(r, wait));
return ghl(path, options, attempt + 1);
}
if (!res.ok) throw new Error(res.status + " " + (await res.text()));
return res.json();
}5. The endpoints most builds use
| Job | Endpoint | Notes |
|---|---|---|
| Create or update a contact | POST /contacts/upsert | Matches existing contacts by email/phone using the location's duplicate settings. Use this instead of create, to avoid duplicates. |
| Find contacts | POST /contacts/search | Filtered, paginated search. |
| Tag a contact | POST /contacts/:id/tags | Tags are the simplest way to start a workflow from outside HighLevel. |
| Pipeline deals | /opportunities | Create, update stage and status, search. |
| Bookings | /calendars | Get free slots, create and update appointments. |
| Messages | /conversations | Read conversations and send SMS or email. US texting still needs A2P 10DLC. |
| Custom data | Custom fields, custom values, custom objects | Store data HighLevel has no standard field for. |
Example: create or update a lead from your own app.
curl -X POST "https://services.leadconnectorhq.com/contacts/upsert" \
-H "Authorization: Bearer $GHL_TOKEN" \
-H "Version: <version from the endpoint's docs page>" \
-H "Content-Type: application/json" \
-d '{
"locationId": "YOUR_LOCATION_ID",
"firstName": "Sam",
"email": "sam@example.com",
"phone": "+15555550123",
"tags": ["website-lead"],
"source": "Custom booking app"
}'Paths and fields change as HighLevel updates the API, so treat this table as a map and confirm the exact request on each endpoint's docs page.
6. Getting data out: webhooks
There are two ways HighLevel can tell your system something happened:
- Workflow webhook action (no app needed): any workflow can POST contact data to your URL when its trigger fires. Simple and works with a Private Integration token setup. See how to connect any app to GoHighLevel.
- App webhooks (OAuth Marketplace apps): HighLevel sends events such as contact, appointment and opportunity changes to your app's endpoint.
For app webhooks, follow HighLevel's guide:
- Verify the signature. Use the
X-GHL-Signatureheader (Ed25519) with HighLevel's published public key. The olderX-WH-Signature(RSA) header is deprecated from September 1, 2026. Reject anything that fails. - Return 200 straight away and do the work in a queue. Any non-2xx response is retried, up to 12 times with exponential backoff.
- Make processing idempotent. Store the webhook ID and skip events you've already handled, because retries mean duplicates.
7. The HighLevel MCP server (AI assistants)
HighLevel also runs an official MCP server, so AI assistants such as Claude and ChatGPT can work with a sub-account directly. Claude connects to https://services.leadconnectorhq.com/mcp/anthropic/v2 and ChatGPT or Codex to /mcp/openai/v2/. You sign in with OAuth, pick the sub-account and approve scopes. Instead of hundreds of separate tools it exposes four (list locations, search operations, describe an operation, execute it) covering 550+ API operations.
It's useful for reporting, quick lookups and admin tasks you'd otherwise do by hand. Setup steps are in the GoHighLevel MCP server guide. For production automations that must run the same way every time, a workflow or API code is still the better choice.
8. Mistakes that break GoHighLevel integrations
- Creating instead of upserting contacts, which fills the CRM with duplicates.
- Hard-coding the Version header in dozens of places, so a version change means hunting through the code.
- No 429 handling, so bulk imports fail halfway through.
- Choosing a Private Integration token and later needing webhooks, which means rebuilding the auth as an OAuth app.
- Losing the token because it's only shown once, then having to rotate it everywhere.
- Not verifying webhook signatures, so anyone who finds the URL can send fake events.
- Doing heavy work before responding to a webhook, which causes timeouts, retries and double processing.
- Using v1 code from old tutorials. v1 is end-of-life.
9. API, workflow custom code or n8n?
- Workflow custom code or webhook action: small logic inside one workflow, like formatting data or calling one outside service.
- n8n, Make or Zapier: connecting tools with branches and retries, without hosting your own code. See GoHighLevel vs n8n vs Make vs Zapier.
- Direct API code: two-way syncs, client portals, custom apps, large data volumes, or logic HighLevel can't express. More on that in when GoHighLevel isn't enough.
The API is also how custom AI agents read and update the CRM. See the GoHighLevel AI automation guide and the Agent Studio guide.
10. Real examples
- A pest inspection company: a custom quote engine plus a two-way Xero integration, where invoices are created from GoHighLevel and paid status syncs back.
- Integrations between GoHighLevel and real estate and hospitality platforms through webhooks and API calls.
Need a GoHighLevel API integration built?
I'm Arslan Mumtaz, a software engineer who builds GoHighLevel API integrations, webhooks and custom apps for businesses and agencies in the US, UK, Europe and Australia. See AI & automation and pricing, or start with a $97 audit of what you want to connect.