Find Your GoHighLevel Location ID for API v2 Requests

by Fahim

Every API v2 request to GoHighLevel that touches sub-account data requires a 22-character Location ID. Miss it or format it wrong, and you’ll hit immediate 401 Unauthorized or 422 Unprocessable Entity errors.

I ran head-first into this when migrating our legacy scripts from API v1 to v2. In v1, an API key was hardcoded to a single location. In v2, tokens and scopes are decoupled—your code has to explicitly pass the Location ID in query params, request bodies, or auth exchanges. Here is how to pull your Location ID manually right now, or dynamically in your codebase.

Terminal screen showing GoHighLevel API v2 JSON response with highlighted Location ID parameter
Terminal screen showing GoHighLevel API v2 JSON response with highlighted Location ID parameter

Why API v2 Requires a Location ID

HighLevel structured API v2 around OAuth 2.0 and Agency-level or Location-level app installations. While v1 relied on a static API key copied out of company settings, v2 isolates permissions. A single Agency access token can manage hundreds of sub-accounts, but the API gateway won’t guess which sub-account your script wants to touch.

Every sub-account in HighLevel has a unique alphanumeric string (usually 22 characters, like ve9bAl1GtfgPwtvxomXz). In the official HighLevel API docs, this field is labeled locationId. If you’re building custom integrations or syncing contacts, you have to feed this exact string to endpoints like /contacts/, /calendars/, and /opportunities/.

Method 1: Grab the Location ID from the App URL

The fastest manual method takes about three seconds and requires zero API calls. When you switch into any sub-account in the HighLevel web app, the platform dumps the Location ID directly into the browser URL path.

  1. Log into your GoHighLevel agency dashboard or client portal.
  2. Use the top-left switcher to open the specific sub-account you need.
  3. Check your browser address bar.

The URL looks like this:

Grab the segment right after /location/:

https://app.gohighlevel.com/v2/location/ve9bAl1GtfgPwtvxomXz/dashboard

In this example, ve9bAl1GtfgPwtvxomXz is the Location ID. Copy that string. It stays permanent for the life of that sub-account, even if you rename the company or hook up custom domains later.

Method 2: Copy from Sub-Account Business Profile Settings

If you don’t want to parse URLs by hand, HighLevel also exposes the Location ID inside the account settings UI.

  1. Open the sub-account dashboard.
  2. Click Settings at the bottom of the left sidebar.
  3. Under the default Business Profile tab, check the General section.
  4. Look for the field labeled Location ID or Business ID.
  5. Click the copy icon next to the value.

If you manage dozens of sub-accounts across an agency, check out our guide on how to find sub-account Location ID in GoHighLevel across your entire agency sub-account table in bulk.

Method 3: Fetch Location IDs Programmatically via the Agency API

When building multi-tenant SaaS integrations, you can’t manually copy-paste URLs. You need your script to query the agency API directly and pull the full list of sub-accounts along with their Location IDs.

To run this call, your OAuth app needs the locations.readonly scope, and you must pass an Agency-level access token in the Authorization header. You also have to supply the required Version header for API v2.

Here is the cURL request to fetch the first 20 sub-accounts in your agency:

curl -X GET "https://services.leadconnectorhq.com/locations/search?limit=20"  -H "Authorization: Bearer YOUR_AGENCY_ACCESS_TOKEN"  -H "Version: 2021-07-28"  -H "Accept: application/json"

The API responds with a JSON array of location objects. The id field is your Location ID:

{ "locations": [ { "id": "ve9bAl1GtfgPwtvxomXz", "name": "Acme Dental Clinic", "email": "contact@acmedental.test", "address": "123 Main St", "city": "Austin", "state": "TX", "country": "US" } ], "total": 1
}

Store locations[n].id in your database or cache so your app can route downstream API requests correctly.

Method 4: Extract Location ID from the OAuth 2.0 Token Exchange

If your integration installs via the HighLevel App Marketplace, users authenticate via OAuth 2.0. Once a user approves permissions, HighLevel redirects back to your callback URL with a temporary authorization code.

When you exchange that code for an access token at https://services.leadconnectorhq.com/oauth/token, the returned JSON payload includes the locationId and companyId tied to the install.

Here is how to handle that token exchange in Node.js:

async function exchangeCodeForTokens(authCode) { const response = await fetch('https://services.leadconnectorhq.com/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' }, body: new URLSearchParams({ client_id: process.env.GHL_CLIENT_ID, client_secret: process.env.GHL_CLIENT_SECRET, grant_type: 'authorization_code', code: authCode, user_type: 'Location' }) }); if (!response.ok) { const errorBody = await response.text(); throw new Error(`Token exchange failed: ${response.status} ${errorBody}`); } const data = await response.json(); // Extract Location ID directly from the response body const locationId = data.locationId; const accessToken = data.access_token; const refreshToken = data.refresh_token; console.log(`Successfully installed for Location ID: ${locationId}`); return { locationId, accessToken, refreshToken };
}

If you’re building a background worker or long-running app, make sure to read our walkthrough on GoHighLevel API v2 OAuth token refresh in Node.js so your tokens don’t expire after 24 hours.

Method 5: Extract Location ID from Inbound Webhooks

When HighLevel fires an outbound webhook from a workflow (like “Contact Created” or “Form Submitted”), the JSON payload includes the locationId as a top-level property.

Here is a typical payload sent by a workflow execution:

{ "type": "ContactCreate", "locationId": "ve9bAl1GtfgPwtvxomXz", "id": "c18zDq5N61KLP09xRttQ", "firstName": "Sarah", "lastName": "Connor", "email": "sarah@example.test", "phone": "+15125550199", "dateAdded": "2024-03-24T14:32:10.000Z"
}

Your webhook handler can pull req.body.locationId directly. If your workflows aren’t sending expected properties, verify your field maps using our guide to custom values and custom fields in HighLevel webhooks. If no events hit your server at all, follow our troubleshooting steps to fix HighLevel webhook not firing.

How to Use the Location ID in API v2 Requests

Once you have the ID, where does it actually go? Unlike API v1, API v2 strictly enforces where parameters live based on the HTTP method.

1. GET Requests: Pass as a Query Parameter

Endpoints that retrieve lists (contacts, appointments, pipelines, tags) require locationId as a URL query parameter.

Here is a complete Node.js script fetching contacts for a specific location:

import fetch from 'node-fetch'; async function getContacts(locationId, accessToken) { const url = new URL('https://services.leadconnectorhq.com/contacts/'); url.searchParams.append('locationId', locationId); url.searchParams.append('limit', '10'); const response = await fetch(url.toString(), { method: 'GET', headers: { 'Authorization': `Bearer ${accessToken}`, 'Version': '2021-07-28', 'Accept': 'application/json' } }); if (!response.ok) { const err = await response.text(); throw new Error(`API error ${response.status}: ${err}`); } const json = await response.json(); return json.contacts;
} // Run it
const LOCATION_ID = 've9bAl1GtfgPwtvxomXz';
const TOKEN = process.env.GHL_ACCESS_TOKEN; getContacts(LOCATION_ID, TOKEN) .then(contacts => console.log(`Found ${contacts.length} contacts`)) .catch(err => console.error(err.message));

2. POST and PUT Requests: Pass in the JSON Body

When creating or updating a resource (like creating a contact or booking an appointment), put locationId inside the JSON request body:

{ "locationId": "ve9bAl1GtfgPwtvxomXz", "firstName": "Alex", "lastName": "Stone", "email": "alex.stone@example.test", "phone": "+15125550144"
}

Gotchas and Common Location ID Errors

During my own migrations, a few predictable errors tripped me up repeatedly:

  • 401 Unauthorized / Invalid Token: You passed a valid Location ID, but your Bearer token was minted for a different sub-account or expired. Tokens expire in 86,400 seconds (24 hours).
  • 422 Unprocessable Entity / locationId is required: You sent a GET request but forgot ?locationId=... in the query string, or you sent a POST body where the key was written as location_id (snake_case) instead of locationId (camelCase). API v2 strictly enforces camelCase.
  • 403 Forbidden / Scope Not Granted: Your token belongs to the right location, but the OAuth app was installed without the scope needed for that endpoint (for example, trying to write contacts without contacts.write).
  • Company ID vs Location ID mix-up: In Agency-level endpoints (like listing sub-accounts), you need the companyId. In Sub-account endpoints (contacts, forms, pipelines), you need the locationId. They are completely separate IDs.

Frequently Asked Questions

Does a GoHighLevel Location ID ever change?

No. The Location ID is a permanent UUID created when the sub-account is first provisioned. Renaming the business, changing the email address, updating custom domains, or transferring ownership between agencies does not change the Location ID.

Can I use my API v1 API Key as a Location ID?

No. API v1 API keys are static auth tokens (usually 32 to 40 characters), while Location IDs are 22-character entity identifiers. They are not interchangeable.

What is the difference between Company ID and Location ID?

companyId represents the Agency account that owns sub-accounts. locationId represents an individual sub-account underneath that agency. Agency-level endpoints require a Company ID; sub-account operations (contacts, calendars, opportunities) require a Location ID.

Why does my GET request return 422 even though locationId is in the body?

HighLevel’s API gateway ignores JSON request bodies on HTTP GET requests. For every GET request, you must pass locationId as a URL query parameter (for example, /contacts/?locationId=ve9bAl1GtfgPwtvxomXz).

Next Steps

Now that you have your Location ID and your GET calls working, get your token refresh logic sorted before hitting production. Follow our guide on GoHighLevel API v2 OAuth token refresh in Node.js to keep your integration running without manual re-auths.

Official resources

all_in_one_marketing_tool