Integration Guide
Scrape B2B leads from Apollo, Google Maps, or the Lead Database and push them directly into GoHighLevel as contacts. Leads land in your GHL pipelines with name, email, phone, company, and custom fields - ready for workflows and outreach.
ScraperCity provides an API that returns B2B contact data - emails, phone numbers, job titles, company names, LinkedIn URLs. GoHighLevel provides an API that creates contacts in your CRM. Connect the two and you have an automated lead pipeline that fills your GHL sub-accounts with fresh prospects.
The GoHighLevel API v2 is a REST API built around Private Integration Tokens and OAuth 2.0. For the ScraperCity use case - pushing leads into a single sub-account or a set of client sub-accounts - a Private Integration Token is the simplest authentication method. You generate one in your GHL settings, scope it to contacts, and use it as a Bearer token. No expiry management, no refresh token loops.
The flow works for any ScraperCity data source: scrape Apollo contacts for $0.0039/lead, pull local businesses from Google Maps for $0.01/place, find business emails for $0.05/contact, or query the Lead Database for instant results. Map the output fields to GHL contact fields and POST them to the API.
GHL no longer issues plain API keys for new accounts. The current authentication method for internal integrations is the Private Integration Token. Here is how to generate one:
GHL recommends rotating Private Integration tokens every 90 days. When you rotate, you get a 7-day window where both the old and new tokens work - enough time to update your scripts without downtime.
In your GoHighLevel sub-account, go to Settings, then Integrations, then Private Integrations. Create a new one with the contacts.write scope enabled. Save the token - you will not be able to view it again.
Query any ScraperCity endpoint. Here is an example using the Lead Database (requires the $649/mo plan):
curl -X GET "https://app.scrapercity.com/api/v1/database/leads?title=CEO&industry=real%20estate&country=United%20States&hasEmail=true&limit=50" \
-H "Authorization: Bearer YOUR_SCRAPERCITY_KEY"Or scrape Apollo contacts (available on all plans, results in 11-48+ hours):
curl -X POST "https://app.scrapercity.com/api/v1/apollo/scrape" \
-H "Authorization: Bearer YOUR_SCRAPERCITY_KEY" \
-H "Content-Type: application/json" \
-d '{
"titles": ["CEO", "Founder"],
"industries": ["Real Estate"],
"locations": ["United States"],
"limit": 100
}'For each contact in the response, POST to the GoHighLevel Contacts API. The Version header is required for all GHL API v2 calls:
curl -X POST "https://services.leadconnectorhq.com/contacts/" \
-H "Authorization: Bearer YOUR_GHL_TOKEN" \
-H "Content-Type: application/json" \
-H "Version: 2021-07-28" \
-d '{
"firstName": "Jane",
"lastName": "Smith",
"email": "[email protected]",
"phone": "+15551234567",
"companyName": "Acme Corp",
"locationId": "YOUR_LOCATION_ID",
"tags": ["scraped-apollo"],
"customFields": [
{ "key": "job_title", "value": "CEO" },
{ "key": "linkedin_url", "value": "https://linkedin.com/in/janesmith" }
]
}'Create a workflow in GHL triggered by the tag you applied (e.g. "scraped-apollo"). The workflow can assign leads to a pipeline stage, start an email/SMS sequence, or notify your team via Slack.
ScraperCity returns a consistent JSON structure across all lead sources. Here is how those fields map to GHL contact fields and where to put extra data:
| ScraperCity Field | GHL Contact Field | Notes |
|---|---|---|
| first_name | firstName | Top-level field |
| last_name | lastName | Top-level field |
| Top-level field | ||
| phone | phone | Use E.164 format (+1...) |
| company_name | companyName | Top-level field |
| title / job_title | customFields[key="job_title"] | Create custom field first |
| linkedin_url | customFields[key="linkedin_url"] | Create custom field first |
| city | city | Top-level field |
| state | state | Top-level field |
| country | country | Top-level field |
| industry | customFields[key="industry"] | Create custom field first |
| website | website | Top-level field |
| employee_count | customFields[key="employee_count"] | Store as text or number field |
Custom fields must be created in GHL before you reference them. Go to Settings - Custom Fields in your sub-account and add each field. GHL will assign each a key that you use in the customFields array.
You do not need to write a custom server to run this pipeline. Three common approaches work well depending on your technical comfort level:
The most flexible option. Pull leads from ScraperCity, loop through the results, and POST each one to GHL. Add a small delay between requests to stay under the GHL burst limit (100 requests per 10 seconds). Here is a minimal Node.js example:
const leads = await fetch('https://app.scrapercity.com/api/v1/database/leads?title=CFO&hasEmail=true&limit=100', {
headers: { Authorization: 'Bearer YOUR_SCRAPERCITY_KEY' }
}).then(r => r.json());
for (const lead of leads.data) {
await fetch('https://services.leadconnectorhq.com/contacts/', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_GHL_TOKEN',
'Content-Type': 'application/json',
Version: '2021-07-28'
},
body: JSON.stringify({
firstName: lead.first_name,
lastName: lead.last_name,
email: lead.email,
phone: lead.phone,
companyName: lead.company_name,
locationId: 'YOUR_LOCATION_ID',
tags: ['scraped-database'],
customFields: [
{ key: 'job_title', value: lead.title },
{ key: 'linkedin_url', value: lead.linkedin_url }
]
})
});
// Pause 150ms between requests to stay under GHL burst limit
await new Promise(r => setTimeout(r, 150));
}In n8n, use an HTTP Request node to call the ScraperCity endpoint. Connect it to a second HTTP Request node configured as a POST to https://services.leadconnectorhq.com/contacts/ with the GHL token in the Authorization header and Version: 2021-07-28 as a second header. Use the Split In Batches node between the two to pace requests and respect the GHL rate limit. You can schedule the workflow to run on a cron - every morning, every hour, or after a ScraperCity webhook fires.
Zapier has a native GoHighLevel integration. Use a Webhook trigger (or a Schedule trigger) to kick off a Zap, call the ScraperCity API via a Zapier Webhooks step to pull leads, then use the GoHighLevel "Create/Update Contact" action to push each one. Zapier handles the auth handshake for GHL natively - you just connect your GHL account and map fields. This works well for low-to-medium volume (a few hundred leads per run) without writing any code.
If you run a GoHighLevel agency serving multiple clients, ScraperCity lets you fill client sub-accounts with targeted leads at scale. Scrape leads matching each client's ICP, push them into the right sub-account using that location's ID, and let GHL workflows handle the nurture. Apollo leads cost $0.0039 each - at that price, loading 10,000 prospects into a client's pipeline costs under $40.
The GHL API supports both Location Level Access (for a single sub-account) and Agency Level Access (for managing data across the whole agency). For most lead-push use cases, a per-sub-account Private Integration Token is the right choice - it scopes access tightly to that client's data and keeps things clean.
A practical agency workflow looks like this:
On the $149/mo ScraperCity plan you can run this for multiple clients simultaneously. On the $649/mo plan you also get access to the Lead Database endpoint, which returns results instantly - no waiting for a scrape to complete.
Scrape Google Maps for businesses matching a category in a city ($0.01/place). Each result includes the business name, phone, website, and often an email. Push them into a GHL sub-account as contacts and trigger a cold outreach workflow. Google Maps leads are available on all plans and return in 5-30 minutes.
Target specific job titles at companies in a defined industry. Apollo leads include work email, phone, LinkedIn, company size, and funding stage. At $0.0039/lead, you can fill an entire GHL pipeline with 1,000 vetted prospects for under $4. Apollo scrapes complete in 11-48+ hours.
Use the Store Leads endpoint to find Shopify and WooCommerce stores ($0.0039/lead, instant). Results include store owner contact info, revenue estimates, and tech stack. Push them into GHL with a tag like "shopify-prospect" and trigger a workflow for your agency's pitch sequence.
Pull real estate agent contacts from Zillow Agents or commercial listings from Crexi ($0.029/listing). Map agent name, phone, email, and brokerage into GHL contact fields. Tag by region so you can run geo-targeted SMS or email sequences.
Export a list of GHL contacts that are missing phone numbers. Run each through the ScraperCity Mobile Finder ($0.25/input) to append mobile numbers, then update the contacts in GHL via the PATCH /contacts/{id} endpoint. Enriched contacts respond better to call-based workflows.
Your Private Integration Token is missing, expired, or does not have the right scope. Check that the token is included in the Authorization: Bearer header (not a query parameter). Confirm the token has contacts.write scope enabled in your GHL Private Integrations settings. Note that GHL recommends rotating tokens every 90 days - if it has been a while, generate a fresh one.
This usually means a required field is missing or a field value is in the wrong format. The most common culprits: locationId is missing or wrong, a phone number is not in E.164 format (+15551234567), or a custom field key does not match what exists in GHL. Double-check the locationId in your GHL sub-account settings and verify custom field keys under Settings - Custom Fields.
You have hit the burst rate limit of 100 requests per 10 seconds. Add a delay between API calls - 150ms between each POST is a safe pace for batches of any size. Watch the X-RateLimit-Remaining and X-RateLimit-Daily-Remaining headers in the GHL response to monitor your quota in real time.
All GHL API v2 calls require the Version: 2021-07-28 header. If you omit it, some endpoints return errors or unexpected results. Make sure this header is present on every request.
If a Lead Database query returns zero leads, try broadening the filters - remove the hasEmail=true constraint, widen the industry, or remove the location filter. For Apollo scrapes, results arrive in 11-48+ hours - poll the status endpoint rather than assuming the run failed. You can find the webhook setup at app.scrapercity.com/dashboard/webhooks to get notified when results are ready.
GHL will create a duplicate if a contact with the same email already exists and you do not use the upsert endpoint. Consider using POST /contacts/upsert instead of plain POST /contacts/. The upsert endpoint matches on email and updates the existing record rather than creating a new one.
For reference, here are the key GHL API v2 details you need for this integration:
Base URL: https://services.leadconnectorhq.com
Create contact: POST /contacts/
Upsert contact: POST /contacts/upsert
Update contact: PUT /contacts/{contactId}
Auth header: Authorization: Bearer YOUR_TOKEN
Version header: Version: 2021-07-28
Burst limit: 100 requests / 10 seconds
Daily limit: 200,000 requests / day
Rate limit headers: X-RateLimit-Remaining, X-RateLimit-Daily-Remaining
Full docs: marketplace.gohighlevel.com/docs