Skip to main content

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)

Source distribution for ghl-api-extended 0.1.1
File Size Uploaded
ghl_api_extended-0.1.1.tar.gz 27.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ghl-api-extended 0.1.1
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page