ghl-api-extended (Python)
A GHL client covering the internal search-v2 endpoints GHL's own UI uses for Contacts, Opportunities, and Appointments/Calendar filtering — plus a self-contained OAuth token store, so it works standalone without any other project's auth.
This is the Python port of ghl-api-extended
(the Node/TypeScript package). One difference: the JS package is a drop-in
subclass of the official @gohighlevel/api-client SDK, so it inherits every
official route (.contacts, .opportunities, .calendars, ...) for free.
No official GHL SDK exists for Python, so HighLevel here only covers
auth + these search-v2 endpoints — for any other GHL route, call
ghl.request(method, url, ...) directly (see http.HighLevelClient.request).
These search-v2 endpoints aren't in GHL's public API docs, so
docs/filters-reference.md and the per-endpoint docs cover the field/operator
behavior directly. Check those before filtering on anything not already
covered by the fetch_*_by_date_range helpers below.
Install
pip install ghl-api-extended
Usage
from ghl_api_extended import HighLevel
ghl = HighLevel(location_access_token=location_access_token) # however you already obtain it
# Filter + auto-paginate the way the GHL UI's Contacts tab does.
contacts = ghl.fetch_contacts_by_date_range(ContactsByDateRangeParams(
location_id=location_id,
start_date="2026-01-01",
end_date="2026-01-31",
filters=[Filter(field="tags", operator="contains", value=["confirmed"])],
))
from ghl_api_extended import OpportunitiesByDateRangeParams, AppointmentsByDateRangeParams, Filter
opportunities = ghl.fetch_opportunities_by_date_range(OpportunitiesByDateRangeParams(
location_id=location_id,
date_field="last_stage_change_date", # default: date_added
start_date="2026-01-01",
end_date="2026-01-31",
filters=[Filter(field="pipeline_id", operator="eq", value=[pipeline_id])],
))
appointments = ghl.fetch_appointments_by_date_range(AppointmentsByDateRangeParams(
location_id=location_id,
start_date="2026-01-01", # ranges over startTime by default
end_date="2026-01-31",
filters=[Filter(field="appoinmentStatus", operator="eq", value="confirmed")],
))
The fetch_*_by_date_range methods auto-paginate to exhaustion, same as
scrolling a filtered list in the GHL UI — no separate page-loop needed. Each
*ByDateRangeParams dataclass takes max_results/max_pages/page_limit if
you want to bound that. For a single page, or full control over
sort/pagination/aggregations, use ghl.search_contacts(...) /
ghl.search_opportunities(...) / ghl.search_appointments(...) directly.
The same behavior is also available as standalone functions
(search_contacts(client, location_id, ...),
fetch_contacts_by_date_range(client, params), ...) that take any object with
a compatible .request(method, url, ...) method — useful if you're not using
the HighLevel class directly.
No existing auth? Use the built-in OAuth flow
Copy .env.example to .env, fill in your GHL marketplace app's
GHL_CLIENT_ID / GHL_CLIENT_SECRET / GHL_REDIRECT_URI, then:
ghl-authorize
This opens the GHL OAuth consent screen, catches the redirect on a local
server, and saves the company session to .tokens.json (gitignored). Location
tokens are minted and cached automatically as you use them.
from ghl_api_extended import find_most_recent_company_id, get_authorized_location_client
company_id = find_most_recent_company_id()
ghl = get_authorized_location_client(company_id, location_id) # a HighLevel instance
Filters
filters is a list of leaf Filter(field, operator, value) or
FilterGroup(group="AND" | "OR", filters=[...]), nestable arbitrarily.
Multiple entries at the top level are implicitly ANDed together — see
docs/filters-reference.md for the full field/operator map per endpoint,
including the traps (opportunities filter fields are snake_case and don't all
mechanically match the response's camelCase names; contacts/appointments
custom fields need a customFields.<key> dot-path — use
contact_custom_field(key, operator, value) / opportunity_custom_field(...)).
Invalid fields are rejected client-side before the network call
(GhlFilterFieldError); operator/value-shape errors from GHL itself are
classified into GhlFilterOperatorError / GhlFilterValueError so you can
branch on them instead of parsing message strings.
License
Apache-2.0
Release files for ghl-api-extended 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ghl_api_extended-0.1.1.tar.gz | 27.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ghl_api_extended-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 51.1 kB
Release files / ghl_api_extended-0.1.1.tar.gz
| Download URL | ghl_api_extended-0.1.1.tar.gz |
|---|---|
| Size | 27.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c06c1092b123f07cd958b4d5add740d062db5505158e2af87f87f97729db5320
|
|
BLAKE2b-256 checksum How to use checksums |
0b8481e9806376e1d479ff28f4e96dac7403ea0856a09ef03d5497286805fd21
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency logRelease files / ghl_api_extended-0.1.1-py3-none-any.whl
| Download URL | ghl_api_extended-0.1.1-py3-none-any.whl |
|---|---|
| Size | 24.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8375c896179e93967fc572d0ddd4cfeec5ed21b1dd61777947cb014acf06e636
|
|
BLAKE2b-256 checksum How to use checksums |
aa13eed5a892885e67193466fb9bb3a9bb24233f7fab58cf7d95801fd4e71137
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency log