GHL API v2 & Integrations
Overview
GoHighLevel's API v2 is a RESTful API providing programmatic access to the platform's features. It enables custom integrations, workflow automation, data syncing, and building marketplace applications.
Important: API v1 is deprecated. All new development should use API v2.
- Base URL:
https://services.leadconnectorhq.com - Docs: https://marketplace.gohighlevel.com/docs/
- GitHub: https://github.com/GoHighLevel/highlevel-api-docs
Authentication
OAuth 2.0 (Authorization Code Grant)
Used for Marketplace Apps and multi-location integrations.
Flow:
1. User clicks "Install" on your marketplace app
2. Redirect to GHL authorization URL
3. User grants permission
4. Receive authorization code
5. Exchange code for access_token + refresh_token
6. Use access_token in API calls
7. Refresh token before expiryAuthorization URL:
https://marketplace.gohighlevel.com/oauth/chooselocation
?response_type=code
&redirect_uri={YOUR_REDIRECT_URI}
&client_id={YOUR_CLIENT_ID}
&scope={SCOPES}Token Exchange:
POST https://services.leadconnectorhq.com/oauth/token
Content-Type: application/x-www-form-urlencoded
client_id={CLIENT_ID}
&client_secret={CLIENT_SECRET}
&grant_type=authorization_code
&code={AUTH_CODE}
&redirect_uri={REDIRECT_URI}Token Refresh:
POST https://services.leadconnectorhq.com/oauth/token
Content-Type: application/x-www-form-urlencoded
client_id={CLIENT_ID}
&client_secret={CLIENT_SECRET}
&grant_type=refresh_token
&refresh_token={REFRESH_TOKEN}Private Integration Token
Used for single-location or internal integrations (no OAuth flow needed).
- Go to Settings → Integrations → Private Integrations in your sub-account
- Create a new integration
- Select required scopes
- Copy the generated API key
Usage:
GET https://services.leadconnectorhq.com/contacts/
Authorization: Bearer {PRIVATE_TOKEN}
Version: 2021-07-28Rate Limits
| Limit Type | Value |
|---|---|
| Burst | 100 requests per 10 seconds |
| Daily | 200,000 requests per day |
| Scope | Per marketplace app, per resource (Location or Company) |
Rate limit headers are returned in API responses:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1639159200Best Practice: Implement exponential backoff when receiving 429 Too Many Requests.API Endpoint Reference
Contacts
| Method | Endpoint | Description |
|---|---|---|
GET | /contacts/{contactId} | Get a contact by ID |
GET | /contacts/ | Search/list contacts |
POST | /contacts/ | Create a new contact |
PUT | /contacts/{contactId} | Update a contact |
DELETE | /contacts/{contactId} | Delete a contact |
POST | /contacts/{contactId}/tags | Add tags |
DELETE | /contacts/{contactId}/tags | Remove tags |
GET | /contacts/{contactId}/tasks | Get contact tasks |
GET | /contacts/{contactId}/notes | Get contact notes |
Conversations
| Method | Endpoint | Description |
|---|---|---|
GET | /conversations/{conversationId} | Get conversation |
GET | /conversations/search | Search conversations |
POST | /conversations/messages | Send a message |
GET | /conversations/{id}/messages | Get messages in conversation |
PUT | /conversations/{id}/messages/{messageId}/status | Update message status |
Calendars & Appointments
| Method | Endpoint | Description |
|---|---|---|
GET | /calendars/ | List calendars |
GET | /calendars/{calendarId} | Get calendar |
POST | /calendars/ | Create calendar |
PUT | /calendars/{calendarId} | Update calendar |
GET | /calendars/{calendarId}/free-slots | Get available slots |
GET | /calendars/events | List appointments |
POST | /calendars/events | Book appointment |
PUT | /calendars/events/{eventId} | Update appointment |
Opportunities
| Method | Endpoint | Description |
|---|---|---|
GET | /opportunities/search | Search opportunities |
GET | /opportunities/{id} | Get opportunity |
POST | /opportunities/ | Create opportunity |
PUT | /opportunities/{id} | Update opportunity |
DELETE | /opportunities/{id} | Delete opportunity |
PUT | /opportunities/{id}/status | Update status (won/lost/open) |
GET | /opportunities/pipelines | List pipelines |
Payments
| Method | Endpoint | Description |
|---|---|---|
GET | /payments/orders | List orders |
GET | /payments/orders/{orderId} | Get order details |
GET | /payments/subscriptions | List subscriptions |
GET | /payments/transactions | List transactions |
POST | /payments/custom-provider/ | Create custom payment provider |
Workflows
| Method | Endpoint | Description |
|---|---|---|
GET | /workflows/ | List workflows |
POST | /contacts/{contactId}/workflow/{workflowId} | Add contact to workflow |
DELETE | /contacts/{contactId}/workflow/{workflowId} | Remove contact from workflow |
Other Endpoints
- Users: CRUD for agency/location users
- Locations: Manage sub-accounts
- Forms: List and retrieve form submissions
- Surveys: List and retrieve survey submissions
- Businesses: Manage business profiles
- Social Planner: Schedule and manage social posts
- Media: Upload and manage files
- Custom Fields / Custom Values: CRUD operations
Webhooks
Webhooks push real-time event data to your application.
Setup
- Go to Settings → Integrations → Webhooks in your sub-account
- Add a webhook URL
- Select events to subscribe to
Key Webhook Events
| Event | Description |
|---|---|
ContactCreate | New contact created |
ContactUpdate | Contact record updated |
ContactDelete | Contact deleted |
ContactTagUpdate | Tag added or removed |
ConversationUnreadUpdate | New unread message |
InboundMessage | Incoming SMS, email, or chat |
OutboundMessage | Outgoing message sent |
AppointmentCreate | New appointment booked |
AppointmentUpdate | Appointment rescheduled/cancelled |
OpportunityCreate | New opportunity created |
OpportunityStageUpdate | Opportunity moved to new stage |
OpportunityStatusUpdate | Opportunity won/lost/abandoned |
TaskCreate | New task assigned |
NoteCreate | New note added |
FormSubmission | Form submitted |
SurveySubmission | Survey completed |
Webhook Payload Example
{
"type": "ContactCreate",
"locationId": "loc_abc123",
"id": "contact_xyz789",
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phone": "+15551234567",
"tags": ["new-lead", "source:facebook"],
"dateAdded": "2025-01-15T10:30:00Z"
}Best Practices
- Respond with 200 quickly — Process webhook data asynchronously
- Validate payloads — Check for required fields before processing
- Handle duplicates — Implement idempotency for webhook handlers
- Log everything — Store raw payloads for debugging
- Set up retry logic — GHL retries failed webhook deliveries
Building Marketplace Apps
Steps
- Create a developer account at marketplace.gohighlevel.com
- Register your app — Set redirect URIs, scopes, and app details
- Implement OAuth 2.0 — Handle auth flow, token storage, and refresh
- Build your integration — Use API endpoints to interact with GHL data
- Submit for review — GHL reviews before publishing to the marketplace
- Maintain and update — Monitor for API changes and update accordingly
Required Scopes
Select only the scopes your app needs:
contacts.readonly/contacts.writeconversations.readonly/conversations.write/conversations/message.readonly/conversations/message.writecalendars.readonly/calendars.write/calendars/events.readonly/calendars/events.writeopportunities.readonly/opportunities.writelocations.readonly/locations.writeworkflows.readonlyusers.readonly/users.writepayments.readonly/payments.writeforms.readonly/surveys.readonly
Integration Patterns
Common Integrations
| Tool | Pattern |
|---|---|
| Zapier | Use GHL's Zapier app or custom webhooks |
| Make (Integromat) | HTTP module with API endpoints |
| Google Sheets | Native workflow action or API-based sync |
| Slack | Webhook notifications from workflows |
| Stripe | Native integration for payments |
| WordPress | Embed forms, calendars; use tracking code |
| Shopify | Webhook-based order sync |
| Custom Apps | Direct API v2 integration |
Data Sync Pattern
External System ←→ Middleware (Node.js/Python) ←→ GHL API v2
↕
GHL WebhooksError Handling
// Example: Node.js API call with error handling
async function getContact(contactId, token) {
try {
const response = await fetch(
`https://services.leadconnectorhq.com/contacts/${contactId}`,
{
headers: {
Authorization: `Bearer ${token}`,
Version: "2021-07-28",
},
},
);
if (response.status === 429) {
// Rate limited — wait and retry
const retryAfter = response.headers.get("Retry-After") || 10;
await sleep(retryAfter * 1000);
return getContact(contactId, token); // Retry
}
if (!response.ok) {
throw new Error(`API Error: ${response.status} ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error("GHL API Error:", error);
throw error;
}
}Key Resources
- API Docs: marketplace.gohighlevel.com/docs
- GitHub: GoHighLevel/highlevel-api-docs
- Postman Collection: Available in the official GitHub repo
- Developer Community: GHL Developer Facebook Group