Skip to main content

goable-sdk

PyPI CI License: MIT

Python client for the Goable API — 0-100 suitability scoring for outdoor activities (water, snow, air, land) from real-time weather and multi-domain physics.

Thin, typed transport over the public tenant-facing REST surface. Sync only (built on httpx) in v0.1.0. Python 3.10+.

This is the Python sibling of @goable-io/sdk (TypeScript) — same methods, same semantics, snake_case instead of camelCase.

Install

pip install goable-sdk

Quickstart

import os
from goable_sdk import GoableClient

goable = GoableClient(api_key=os.environ["GOABLE_API_KEY"])

# Score a single activity at a location + time window
result = goable.score({
    "activity": "kitesurfing",
    "location": {"lat": 43.7, "lng": 7.27},
    "window": {"from": "2026-06-01T06:00:00Z", "to": "2026-06-01T18:00:00Z"},
})

result.score       # 0-100
result.verdict      # "unsafe" | "poor" | "marginal" | "fair" | "favorable" | "excellent"
result.confidence

Inverse query — "where should I go?" — ranks sub-spots for an activity within a region:

spots = goable.recommend_spot({
    "activity": "kitesurfing",
    "region": {"center": {"lat": 43.7, "lng": 7.27}, "radiusKm": 50},
    "window": {"from": "2026-06-01T06:00:00Z", "to": "2026-06-01T18:00:00Z"},
    "topK": 5,
})

Use it as a context manager to close the underlying connection pool deterministically:

with GoableClient(api_key=os.environ["GOABLE_API_KEY"]) as goable:
    result = goable.score({"activity": "kitesurfing", "location": {"lat": 43.7, "lng": 7.27}})

Authentication

Every request carries your tenant API key. The canonical production header is X-Goable-Key, which the client sends automatically:

GoableClient(api_key="gk_...")   # -> sends "X-Goable-Key: gk_..."

The API also accepts Authorization: Bearer <key> as a legacy fallback for direct testing, but new integrations should use the default X-Goable-Key path. (Production traffic sits behind CloudFront, which reserves the Authorization header for its own signature — the custom header sidesteps that.)

Mint a key from the tenant portal at console.goable.io/portal/keys.

Configuration

GoableClient(
    api_key="...",                    # required — sent as X-Goable-Key
    base_url="https://api.goable.io", # default
    timeout=30.0,                     # seconds; default 30, 0/None disables
    client=custom_httpx_client,       # default: an owned httpx.Client(); inject for tests
)

Request bodies and query params

Every method accepts either a plain dict or the matching generated pydantic model as the request body (ScoreRequest-shaped code lives in goable_sdk._models as V1ScorePostRequest, etc. — see below). A plain dict is usually the path of least resistance:

goable.score({"activity": "kitesurfing", "location": {"lat": 43.7, "lng": 7.27}})

Query-string parameters (list_policies, recent_observations, sustainability_index, verification_export, audit_export) are always a plain Mapping[str, Any] — several of the underlying query schemas alias a field to the Python keyword from (e.g. audit_export's date range), which is far more natural to write as {"from": ..., "to": ...} than to fight a generated model's alias-only constructor for.

Methods

The client mirrors the full public tenant-facing surface — one method per OpenAPI path (camelCase in the TS SDK becomes snake_case here). Grouped by area:

Score

Method Endpoint Notes
score(input) POST /v1/score ensemble: true → probabilistic (Pro+); rider_skill_level skill-conditioned (Pro+)
score_series(input) POST /v1/score/series per-step over a window
score_multi(input) POST /v1/score/multi many activities, one location
score_historical(input) POST /v1/score/historical climatology percentiles (Pro+)
score_portfolio(input) POST /v1/score/portfolio multi-spot joint variance
score_difficulty(input) POST /v1/score/difficulty L15 skill-conditioned difficulty grids (Pro+)
explain_counterfactual(input) POST /v1/score/explain-counterfactual binding constraint, sensitivities, best window/spot
report_outcome(session_id, input, idempotency_key=None) POST /v1/score/{sessionId}/outcome close the calibration loop

Recommend

Method Endpoint Notes
recommend_spot(input) POST /v1/recommend-spot inverse query: top-K ranked sub-spots

Decision

Method Endpoint Notes
decision(input) POST /v1/decision personalized go/no-go (Pro+)
delete_user_data(pseudonym) DELETE /v1/decision/user-data/{pseudonym} GDPR erasure; returns receipt headers

Intelligence

Method Endpoint Notes
explain(input) POST /v1/intelligence/explain LLM narrative (Pro+)
briefing(input) POST /v1/intelligence/briefing LLM briefing (Pro+)
edge_case(input) POST /v1/intelligence/edge-case LLM edge-case narrative for a marginal score

Projections (Scale)

Method Endpoint Notes
projections(input) POST /v1/projections single-spot climate-decadal
projections_portfolio(input) POST /v1/projections/portfolio multi-spot
adaptation_report(input) POST /v1/projections/adaptation-report months × scenarios × decades

Underwriting (Scale)

Method Endpoint Notes
quote(input) POST /v1/underwriting/quote parametric premium
get_quote(id) GET /v1/underwriting/quote/{id} fetch a stored quote
bind_policy(input, idempotency_key=None) POST /v1/underwriting/policy/bind bind a quote; 422 DRIFT_ACTIVEDriftActiveError
list_policies(query=None) GET /v1/underwriting/policy paginated, boundAt DESC
get_policy(policy_id) GET /v1/underwriting/policy/{policyId} policy + payout events
evaluate_policy(policy_id) POST /v1/underwriting/policy/{policyId}/evaluate re-evaluate; bodyless
settle_policy(policy_id, input) POST /v1/underwriting/policy/{policyId}/settle platform-ops only

Observations / nowcasting (L5.3)

Method Endpoint Notes
create_station(input) POST /v1/observations/stations register a station
list_stations() GET /v1/observations/stations the tenant's stations
update_station(station_id, input) PATCH /v1/observations/stations/{stationId} partial update
recent_observations(station_id, query=None) GET /v1/observations/stations/{stationId}/recent most-recent observations
submit_observations(input) POST /v1/observations push into the 0-6h window (Pro+)
submit_outcome(input, idempotency_key=None) POST /v1/outcomes standalone outcome (not tied to a scored session)
void_outcomes(input) POST /v1/outcomes/void recall a batch of reported outcomes; returns the voided count

Audit & compliance

Method Endpoint Notes
audit_export(query) GET /v1/audit/export query["format"] == "csv"str, else parsed JSON

LLM BYOK (bring-your-own Anthropic key)

Method Endpoint Notes
set_llm_key(input) PUT /v1/tenant/llm-key validate + store; resolves None (204)
get_llm_key() GET /v1/tenant/llm-key masked status (never the key)
delete_llm_key() DELETE /v1/tenant/llm-key remove; resolves None (204)

Health, legal & public (no auth)

Method Endpoint Notes
health() GET /v1/health liveness
health_ready() GET /v1/health/ready readiness (503 → GoableAPIError)
legal_document(kind) GET /v1/legal/{kind}/current current published legal doc
catalog_stats() GET /v1/public/catalog-stats open catalogue coverage stats
sustainability_index(query) GET /v1/public/sustainability-index Goable Sustainability Index (JSON-LD)
public_signup(input) POST /v1/public/signup self-service tenant signup

Research (open data, CC BY — NDJSON streams returned as str)

Method Endpoint Notes
difficulty_atlas_export() GET /v1/research/difficulty-atlas/export.jsonl L15 Difficulty Atlas
verification_export(query=None) GET /v1/research/verification/export Stream F forecast verification

Full endpoint reference: goable.io/docs.

Errors

from goable_sdk import GoableAPIError, GoableNetworkError

try:
    goable.score({"activity": "kitesurfing", "location": {"lat": 43.7, "lng": 7.27}, "ensemble": True})
except GoableAPIError as err:
    err.status                # e.g. 402
    err.code                  # e.g. "PAYMENT_REQUIRED"
    err.issues                # Zod issues on 422 VALIDATION_ERROR
    err.detail                # free-form context (e.g. plan info)
    err.retry_after_seconds   # seconds from `Retry-After` on a 429 (else None)
    err.rate_limit            # {"limit", "remaining", "reset"} when the response carried X-RateLimit-* headers
except GoableNetworkError as err:
    err.kind  # "timeout" | "network" | "parse"

On a 429, back off using the server's hint:

import time

try:
    ...
except GoableAPIError as err:
    if err.status == 429 and err.retry_after_seconds is not None:
        time.sleep(err.retry_after_seconds)
        # ...then retry

DriftActiveError (a GoableAPIError subclass) is raised on 422 DRIFT_ACTIVE from bind_policy() and exposes open_drift_events.

Testing without network access

The client never hits the network directly in your own tests either — inject an httpx.Client built on httpx.MockTransport:

import httpx
from goable_sdk import GoableClient

def handler(request: httpx.Request) -> httpx.Response:
    return httpx.Response(200, json={"status": "ok"})

client = GoableClient(api_key="test-key", client=httpx.Client(transport=httpx.MockTransport(handler)))

Types are generated from the API contract

The request/response models in goable_sdk._models (re-exported from the top-level package, and fully available via goable_sdk.models) are generated from the Goable API's OpenAPI document (openapi.json) via datamodel-code-generator — they are never hand-authored, so they can't drift from the contract. Never hand-edit src/goable_sdk/_models.py; regenerate it with:

python scripts/generate_models.py

The committed openapi.json tracks the live public API contract.

Contributing & releases

Releases are automated via PyPI Trusted Publishing: merging to main (with a version bump in pyproject.toml) publishes to PyPI on tag.

License

MIT © Fabio Carucci

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

goable_sdk-0.3.0.tar.gz (48.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

goable_sdk-0.3.0-py3-none-any.whl (27.9 kB view details)

Uploaded Python 3

File details

Details for the file goable_sdk-0.3.0.tar.gz.

File metadata

  • Download URL: goable_sdk-0.3.0.tar.gz
  • Upload date:
  • Size: 48.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for goable_sdk-0.3.0.tar.gz
Algorithm Hash digest
SHA256 192870089dd27a22e157ed0ad70139311165536e9b057047f97a3632fd7096a1
MD5 3ee673e617feafcd5c4ad0517f42ba00
BLAKE2b-256 c591598d825d2735cf8880d4e3c16cd974f7fb67a7fb26cc3c557a72c7b63000

See more details on using hashes here.

Provenance

The following attestation bundles were made for goable_sdk-0.3.0.tar.gz:

Publisher: release.yml on goable-io/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file goable_sdk-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: goable_sdk-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 27.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for goable_sdk-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cdf4b10bc859be0b2e2e9d38e3ef04a19d60188f922eea2b663818daece094e5
MD5 61e3878f95613cccbbf6c449b0e052ef
BLAKE2b-256 21cc0953041312e8d46f122efe7f387b352a9cdc6504bd2e6c98a43713e0ca42

See more details on using hashes here.

Provenance

The following attestation bundles were made for goable_sdk-0.3.0-py3-none-any.whl:

Publisher: release.yml on goable-io/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.10.0

2 files

0.9.0

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

This release

0.3.0 This release

2 files

0.2.0

2 files

0.1.0

2 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