Skip to main content

gratisgis (Python client)

Talk to a GratisGIS portal from Python: notebooks, scheduled scripts, CI, or an editor on your own machine.

pip install gratisgis

The full guide, with worked examples for filtering, buffering, writing a new layer, and exporting, is in the portal at /help/reference/python-client.

Releases go out on a python-v* tag through .github/workflows/publish-python.yml, using PyPI trusted publishing, so there is no API token anywhere. Bump __version__ in src/gratisgis/client.py (the single source; pyproject.toml reads it) and tag to match, or the workflow refuses the release.

Get a key

In the portal: Profile -> API keys -> Create key. The token is shown once. A key acts as the user who created it, so it sees exactly what you see, with the same sharing and geographic limits. Tick Read only unless the script needs to write.

Use it

from gratisgis import GratisGIS

gg = GratisGIS("https://your-portal.org", api_key="ggk_...")

# Which account is this key?
print(gg.whoami()["username"])

# Find a layer and read it as GeoJSON
layer = gg.find_items(type="data_layer", query="parcels")[0]
fc = gg.read_features(layer["id"], "parcels", limit=500)
print(len(fc["features"]), "features")

Prefer environment variables so the secret stays out of your source, which also means the same script runs unchanged anywhere:

# GRATISGIS_URL=https://your-portal.org
# GRATISGIS_API_KEY=ggk_...
gg = GratisGIS.from_env()

Writing

features = [
    {
        "type": "Feature",
        "geometry": {"type": "Point", "coordinates": [-79.85, 38.93]},
        "properties": {"name": "New hydrant", "status": "active"},
    }
]
gg.add_features(layer_id, "hydrants", features)

add_features batches for you. The portal refuses more than 5000 features in one request, so hand it a list or a generator of any size and it splits the work.

Edits are recorded as new observations rather than overwriting rows, so a feature's history stays intact and remains queryable:

gg.update_feature(layer_id, "hydrants", feature_id,
                  properties={"status": "needs service"})

A monthly refresh, end to end

The shape most people want. Point it at a source, replace what changed, and let cron run it:

import os
from gratisgis import GratisGIS, PortalError

def main() -> int:
    gg = GratisGIS.from_env()
    layer_id = os.environ["PARCEL_LAYER_ID"]
    try:
        incoming = fetch_from_county()          # your code
        result = gg.add_features(layer_id, "parcels", incoming)
        print(f"appended {result['appended']} features")
        return 0
    except PortalError as exc:
        # Non-zero so cron / systemd notices, and the portal's own
        # message reaches your logs verbatim.
        print(f"refresh failed: {exc}")
        return 1

if __name__ == "__main__":
    raise SystemExit(main())

Errors

Every failure raises a subclass of PortalError carrying the portal's own message, plus the status and route:

Class When
AuthError 401 / 403. Bad, expired, or revoked key; read-only key attempting a write; a key used on an admin route
NotFoundError 404. Missing, or not visible to this key
ValidationError 400 / 422. Understood and refused
ConflictError 409. Something moved under you
RateLimitError 429. Carries retry_after when the portal sends it
PortalError Anything else, including a network failure

What this does not do

It wraps the item API and the feature endpoints, which is what automation needs. It is not a mirror of every portal route.

It also has exactly one dependency, httpx. GeoPandas, Shapely and friends belong to your environment, not to a portal client: bring the stack you already use and hand this GeoJSON.

There is no hosted execution environment. Scripts run wherever you run them. Server-side scheduled execution is tracked separately.

Development

pip install -e ".[dev]"
pytest

The tests run against httpx.MockTransport, so no portal is needed.

Metadata

Release files for gratisgis 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gratisgis 0.7.0
File Size Uploaded
gratisgis-0.7.0.tar.gz 33.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gratisgis 0.7.0
File Interpreter ABI Platform
gratisgis-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 55.7 kB

Release files / gratisgis-0.7.0.tar.gz

Download URL gratisgis-0.7.0.tar.gz
Size 33.4 kB
Tags Source
SHA-256 checksum
How to use checksums
34e67b2c54bc2f79aa7a52cbda64543697442aff56f0f680badab0504b119fa1
BLAKE2b-256 checksum
How to use checksums
775217ce2ea7b9ebf6704e55b57047d3b4dbe10d5644503c755af2ed366ecf84
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 9, 2026.

Transparency log

Release files / gratisgis-0.7.0-py3-none-any.whl

Download URL gratisgis-0.7.0-py3-none-any.whl
Size 22.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d454b548d7dbb9a7676cef39fad75304e058640943181fa149a8731dae99ae78
BLAKE2b-256 checksum
How to use checksums
8c08869c0abe8eb1e8f5ba2fc81e5ca2765daebb2a7a5c101f411489804a6b28
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 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files

0.6.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