Skip to content

Python SDK

The official Python client is geojibe. It is an HTTPS client for an existing GeoJibe installation. Processing stays on the server. It does not run Docker, GDAL, DuckDB, QGIS, or a GeoJibe server on your computer.

The CLI (geojibe) is a thin wrapper around the same GeoJibe object. Use either from the same package.

This page is the remote SDK. Canvas Python scripts that run inside a workflow are documented under Use Python for custom changes.

Install

pip install geojibe

or

pipx install geojibe

Python 3.10 or newer.

Authenticate

Create a token in GeoJibe → Account → API Tokens, then:

from geojibe import GeoJibe

client = GeoJibe(url="https://<host>", token="gjp_...")
client.me()

url is the installation origin (no trailing slash, not /api/v1). Do not hard-code a hosted GeoJibe URL — self-hosted deployments use their own host.

url and token may also come from GEOJIBE_URL and GEOJIBE_TOKEN when omitted. Explicit constructor arguments always win. The token is sent as Authorization: Bearer and is never logged.

Credential precedence, highest wins:

  1. Constructor url= / token=
  2. GEOJIBE_URL / GEOJIBE_TOKEN
  3. Saved CLI configuration (geojibe auth login)

Jobs vs Workspaces

Resource Meaning
Jobs Persistent automation. Can be scheduled and run repeatedly.
Workspaces Temporary interactive processing. Upload → preview → transform → convert → download. Automatically expire.

client.jobs is saved Jobs. client.workspaces is ephemeral processing. They are not interchangeable.

GeoJibe

from geojibe import GeoJibe, ValidationError

with GeoJibe(url="https://<host>", token="gjp_...") as client:
    profile = client.me()
Attribute Resource
client.sources Saved Sources
client.destinations Saved Destinations
client.jobs Saved Jobs and Runs
client.recipes Saved Recipes
client.workspaces Temporary Workspaces
client.transformations Server transformation catalog

timeout (default 30s) is the ordinary HTTP timeout. run_timeout (default 600s) is used for upload, preview, convert, and download.

Sources

sources = client.sources.list()
src = client.sources.get(sources[0]["id"])
created = client.sources.create({
    "name": "Public neighborhoods",
    "type": "url",
    "configuration": {"url": "https://example.com/neighborhoods.geojson"},
})
client.sources.update(created["id"], {"name": "Neighborhoods URL", "type": "url", "configuration": {"url": "https://example.com/neighborhoods.geojson"}})
client.sources.delete(created["id"])

create sends POST /api/v1/sources. type and configuration match the public Source contract. Secrets in GET responses are omitted; use flags such as has_password.

Destinations

destinations = client.destinations.list()
dest = client.destinations.get(destinations[0]["id"])
created = client.destinations.create({
    "name": "Production PostGIS",
    "type": "postgis",
    "configuration": {
        "host": "db.example.com",
        "database": "gis",
        "username": "etl",
        "password": "...",
        "ssl_mode": "prefer",
    },
})
client.destinations.delete(created["id"])

create sends POST /api/v1/destinations. Per-run targets (schema, table, object key) belong on the Job, not the saved Destination.

Jobs

jobs = client.jobs.list()
job = client.jobs.get(jobs[0]["id"])
run = client.jobs.run(job["id"])
runs = client.jobs.list_runs(job["id"])

run(..., wait=True) polls until the Run finishes if the first response is still queued or running. The CLI equivalent is geojibe jobs run <id> --wait.

transform_mode is manual, recipe, or sql (not basic). Use recipe_id with recipe.

from geojibe import PipelineStages, job_pipeline, job_write_body, pipeline_stages, stage_included

created = client.jobs.create({
    "name": "Neighborhoods",
    "source_id": "...",
    "transform_mode": "sql",
    "sql": "SELECT * FROM source WHERE population > 1000",
    "output_format": "GeoJSON",
    "destination_id": "...",
    "destination_target": {
        "schema": "public",
        "table": "neighborhoods",
        "write_mode": "replace",
    },
    "schedule": {"kind": "daily", "timezone": "UTC", "hour": 2, "minute": 0},
})
client.jobs.enable(created["id"])
client.jobs.disable(created["id"])
client.jobs.delete(created["id"])

create and update send the public Job body (POST /api/v1/jobs, PUT /api/v1/jobs/{id}). Existing JSON without pipeline is unchanged and keeps legacy/inferred stage presence.

Optional Canvas stage flags:

created = client.jobs.create({
    "name": "Neighborhoods",
    "source_id": "...",
    "output_format": "GeoJSON",
    "pipeline": pipeline_stages(transform=True, convert=True, python=False),
})

job_pipeline(created)
stage_included(created, "python")
job_write_body({"name": "Neighborhoods", "pipeline": {"transform": False}})
{
  "pipeline": {
    "transform": false,
    "convert": false,
    "python": false
  }
}
pipeline Meaning
omitted Legacy/inferred behavior (stages stay present)
{} Legacy/inferred behavior (stages stay present)
explicit false That stage is absent
explicit true That stage is present

Create and update preserve explicit false flags. Leftover operations, format, or Python on the body do not restore a stage marked false.

Recipes

recipes = client.recipes.list()
recipe = client.recipes.get(recipes[0]["id"])
created = client.recipes.create({
    "name": "Rename shape_len",
    "operations": [
        {"type": "rename_column", "from": "shape_len", "to": "perimeter"}
    ],
})
client.recipes.delete(created["id"])

A Recipe stores Transform operations. It is not a Job.

Transformations

Discover operations from the server. Do not hard-code a catalog.

ops = client.transformations.list()

Stable identifiers are catalog id values such as buffer, simplify, and rename_column. SQL and Python are sibling fields on a Workspace pipeline, not fake operation type values. See Transform operations.

Workspaces

A Workspace is temporary interactive processing. Typical flow:

from geojibe import GeoJibe, ValidationError

client = GeoJibe(url="https://<host>", token="gjp_...")

ws = client.workspaces.upload("bees.gpkg")
print(ws.id, ws.status, ws.layers)

rows = ws.preview_data("apiary", limit=50)
geojson = ws.preview_map("apiary")["geojson"]

ops = client.transformations.list()

ws.set_transformations(
    layers=["apiary"],
    operations=[{"type": "buffer", "distance": 10, "units": "source"}],
)

ws.preview_transformed_data("apiary", limit=50)
ws.preview_transformed_map("apiary")

ws.convert(output_format="GeoJSON", layers=["apiary"])
output = ws.download("buffered-bees.geojson")

ws.clear_transformations()

SQL and Python are sibling fields, not operation types:

ws.set_transformations(layers=["apiary"], sql="SELECT * FROM source")
ws.set_transformations(python={"script": script, "filename": "script.py"})

Download streams to disk. An existing file is not overwritten unless you pass overwrite=True. Convert is currently synchronous (status=completed).

preview_data and preview_map return the server JSON, including fields, counts, truncation, and CRS. preview_map()["geojson"] is a GeoJSON FeatureCollection. Visualization is up to the caller.

Preview responses are truncated samples. Convert and download are the full result.

Errors

try:
    ws.preview_transformed_map("other_layer")
except ValidationError as exc:
    print(exc.status_code, exc.code, exc.message, exc.details)
Exception Typical cause
AuthenticationError HTTP 401 / missing credentials
AuthorizationError HTTP 403 / missing PAT scope
NotFoundError HTTP 404
ValidationError HTTP 400 / 409 / 422
NetworkError DNS, connection refused, TLS, timeout
ServerError HTTP 5xx
WaitTimeoutError Timed out waiting for a Job Run
ConfigError Missing URL, token, or local file

See also