Most GoHighLevel work never needs code. Workflows, snapshots and a Zapier connection cover the overwhelming majority of what agencies build. But there is a point past which the no-code route stops making sense — bulk migrations, custom client portals, per-task pricing that outgrows the value, or logic the workflow builder simply cannot express.
This guide covers what the GoHighLevel API can do, how authentication actually works in 2026, how to use inbound and outbound webhooks, and the limits worth designing around before you start.
API v1 or v2: Which You Should Be Building On
GoHighLevel has two API generations, and choosing the wrong one costs rework.
API v1 — legacy
Authenticated with a simple sub-account API key passed as a bearer token. Straightforward to start with, which is why so many older integrations use it. It is legacy: new endpoints are not being added, and long-term support is not guaranteed. Fine for a quick internal script; not a foundation for a product.
API v2 — current
OAuth 2.0, scoped permissions, far broader endpoint coverage, and the basis of the app marketplace. More setup, but it is where the platform is going. Anything you expect to maintain for more than a few months should be built here.
Rule of thumb: internal one-off script, v1 is acceptable. Anything client-facing, resold, or long-lived, use v2.
Authenticating with API v2
OAuth adds steps, but the flow is conventional.
- Create an app in the GoHighLevel Marketplace developer area.
- Choose the scopes you need — request the minimum, not everything. Over-scoped apps get rejected in review and are a liability if a token leaks.
- Set your redirect URI.
- Send the user through the authorisation URL; they pick which location (sub-account) to grant.
- Exchange the returned code for an access token and refresh token.
- Store the refresh token securely and rotate the access token before expiry.
Two things trip people up. First, tokens are scoped to a location, so a tool serving twenty clients holds twenty token pairs — plan your storage for that from day one. Second, refresh tokens can be invalidated when a user reinstalls the app, so handle re-authorisation gracefully rather than failing silently.
The Endpoints You Will Actually Use
The surface area is large; in practice most builds touch a handful.
- Contacts — create, update, search, upsert, tag. The workhorse of nearly every integration.
- Opportunities — create and move deals through pipeline stages. Essential for anything that syncs sales data.
- Calendars and appointments — read availability, book slots, cancel. The basis of custom booking interfaces.
- Conversations — send and read SMS and email. Used for custom inboxes and AI layers.
- Custom fields and custom values — read the schema so your integration writes to the right field IDs rather than guessing names. Our custom values and fields guide explains the distinction.
- Forms and submissions — pull submission data into external systems.
- Invoices and payments — read billing state, useful for reconciliation.
Always Upsert Contacts
The single most important habit: use the upsert endpoint rather than create. A create call with an email that already exists produces a duplicate, and duplicates split conversation history, break attribution and make reporting meaningless. Upsert matches on email or phone and updates in place.
Normalise before you send. Phone numbers must be E.164 — +447700900123, not 07700 900123. Emails should be lowercased and trimmed. Do this in your code, not in a GoHighLevel workflow afterwards.
Outbound Webhooks: GoHighLevel Telling You Things
Polling the API for changes is slow, expensive and hits rate limits. Webhooks push events to you instead.
There are two routes:
Workflow webhook action
Build a workflow, add a Webhook action, point it at your endpoint. You control exactly which contacts fire it and what the trigger conditions are. This is the most precise option and the one to reach for most of the time.
App-level webhook subscriptions
Marketplace apps can subscribe to events at the account level — contact created, opportunity status changed, appointment booked, inbound message received. Better for products that need every event rather than a filtered subset.
Receiving Them Properly
- Respond 200 immediately, then process asynchronously. A slow handler causes retries and duplicate processing.
- Make handlers idempotent. Retries happen. Key on the event or contact ID so replaying an event does not create a second record.
- Verify the source. Use a shared secret in a header or a signed payload — an unauthenticated public endpoint accepting CRM writes is a real risk.
- Log raw payloads for at least a few days. When something goes wrong, the payload is the only evidence of what actually happened.
Inbound Webhooks: Telling GoHighLevel Things
The reverse direction is often overlooked and is frequently the cheapest way to solve a problem. GoHighLevel workflows can be started by an Inbound Webhook trigger: your system posts JSON to a GoHighLevel URL, and the workflow runs with that data available.
This is ideal when an external event should start a GoHighLevel sequence — a payment clearing in your billing system, a support ticket closing, a delivery being completed. You avoid writing any API integration at all; you just post data and let the workflow builder do the rest.
Rate Limits and Designing Around Them
The API is rate limited per location, on both a burst and a daily basis. Exact figures change, so read the current documentation rather than trusting a number in a blog post — including this one. What matters is that you design as though limits are tight:
- Handle 429 responses with exponential backoff and jitter. Do not retry immediately in a loop.
- Use bulk endpoints where they exist instead of looping single calls.
- Cache read-heavy data such as custom field schemas and pipeline IDs. These change rarely; fetching them on every request wastes your budget.
- Queue writes rather than firing them all at once during a migration.
- Spread bulk jobs overnight so a migration does not starve the live integrations serving the same location.
A Realistic Migration Pattern
Bulk imports are the most common reason agencies reach for the API. A pattern that works:
- Export and clean first. Deduplicate, normalise phones and emails, and fix encoding problems before anything touches the API. Importing dirty data and cleaning it in GoHighLevel afterwards is far harder.
- Create the custom field schema up front, then map source columns to field IDs.
- Dry run 20 records into a sandbox sub-account and inspect them by hand.
- Import in batches with a queue, backoff and a written log of every ID.
- Reconcile. Count source rows against created contacts and investigate any gap before declaring it done.
If you are moving off another platform, our HubSpot to GoHighLevel migration guide covers the mapping decisions in more detail.
When Not to Use the API
Plenty of teams write code for problems the platform already solved. Skip the API when:
- A native workflow does it — internal notifications, reminders, tag logic, stage moves.
- A one-off Zapier or Make scenario would cost less than a day of development.
- You need the automation maintained by someone non-technical after you hand it over.
Custom code is a liability as well as an asset. Write it when it earns its keep.
Error Handling That Saves You Later
Integrations fail. The difference between a minor issue and a data disaster is almost entirely in how failure is handled.
- Log every request and response, including the payload, for at least a few days. When a client asks why a contact is wrong, this is the only evidence you will have.
- Distinguish retryable from fatal errors. A 429 or a 500 should back off and retry. A 400 caused by malformed data should stop and alert, because retrying will fail identically forever.
- Use a dead-letter queue. Records that fail repeatedly go somewhere a human can review them, rather than disappearing.
- Alert on failure rate, not on individual failures. One error is noise; a 5% failure rate over an hour is an incident.
- Never fail silently. The worst integration bug is the one that stops working and tells nobody, because by the time it is noticed you have weeks of missing data.
Security Basics
You are handling someone else’s customer database. Treat the credentials accordingly.
- Never commit keys or tokens to source control, including in a private repository.
- Store tokens encrypted at rest and scope each to the single location it serves.
- Verify inbound webhooks with a shared secret or signature. An open endpoint that writes to a CRM is an invitation.
- Request minimum scopes. An integration that only reads contacts should not hold write access to payments.
- Rotate credentials when anyone with access leaves, as covered in our permissions guide.
- Use HTTPS everywhere, including for internal webhook receivers.
Frequently Asked Questions
Which plan do I need for API access?
API access starts at the Unlimited plan. Starter is limited, so confirm the plan before scoping any development. Our pricing guide sets out the tiers.
Can one API key serve all my client sub-accounts?
No. v1 keys and v2 tokens are scoped per location. A multi-client tool holds credentials per client, which is a design requirement, not an inconvenience — it is also what lets you revoke one client cleanly.
Are webhooks guaranteed to arrive exactly once?
No. Assume at-least-once delivery and build idempotent handlers. Duplicate processing is the most common bug in GoHighLevel webhook integrations.
Should I migrate an existing v1 integration to v2?
If it is internal, small and working, there is no urgency. If it is client-facing or you plan to extend it, move it — v1 is legacy and new capability is only arriving in v2.
Can I build a white-label product on the API?
Yes, and that is what the Marketplace and v2 OAuth exist for. If you are heading in that direction, our guides to SaaS mode and white-label GoHighLevel cover the commercial side.
Getting Help
GHL Nexa builds custom GoHighLevel integrations — migrations, client portals, marketplace apps and API-backed automations — alongside the no-code work. If you are weighing up whether your problem needs code at all, send us the details and we will tell you honestly which route is cheaper.



