Skip to main content

Official Python client for the Legalize API

Project description

legalize

python-ci PyPI Python versions License: MIT

Official Python client for the Legalize API — legal texts as structured, versioned data.

pip install legalize
from legalize import Legalize

client = Legalize(api_key="leg_...")

for law in client.laws.iter(country="es", law_type="ley_organica"):
    print(law.id, law.title)

Why this SDK

  • Typed end-to-end. Pydantic v2 models generated from the canonical OpenAPI spec. py.typed ships in the wheel. mypy --strict clean.
  • Sync by default, async when you need it. Legalize and AsyncLegalize expose the same resource API and error types — swap one for the other without rewriting your call sites.
  • Retries with backoff built in. Honors Retry-After, handles 429/5xx, exponential delay with jitter, all configurable.
  • Webhook verification is a one-liner. Constant-time HMAC compare, 5-minute anti-replay window, clock-skew tolerant.
  • No magic, no frameworks. One client, one method per endpoint.

Quick tour

List, iterate, search

# One page
page = client.laws.list(country="es", page=1, per_page=50)
print(page.total, len(page.items))

# Auto-paginated iterator (fetches pages as needed)
for law in client.laws.iter(country="es", status="vigente"):
    ...

# Full-text search
results = client.laws.search(country="es", q="protección de datos")

Time-travel

Every law has a git-tracked history. Retrieve it at any past revision:

commits = client.laws.commits(country="es", law_id="ley_organica_3_2018")
past = client.laws.at_commit(
    country="es",
    law_id="ley_organica_3_2018",
    sha=commits.items[-1].sha,
)
print(past.content)  # Markdown at that revision

XML (and other raw formats)

The typed methods always return JSON-parsed models. When your app speaks XML, use request_raw to fetch any endpoint in another wire format via content negotiation — it sets Accept and hands you the body untouched:

res = client.request_raw("GET", "/api/v1/es/laws/BOE-A-1978-31229")
res.content_type        # "application/xml; charset=utf-8"
xml_text = res.text     # the raw XML string
root = res.xml()        # parsed into an ElementTree element

# format="json" or any explicit media type works the same way:
data = client.request_raw("GET", "/api/v1/countries", format="json").json()

request_raw defaults to format="xml". Errors raise the same typed exceptions as the JSON methods (the error body is in the negotiated format). See the Response formats docs.

Async

import asyncio
from legalize import AsyncLegalize

async def main():
    async with AsyncLegalize(api_key="leg_...") as client:
        page = await client.laws.list(country="es")
        async for law in client.laws.iter(country="fr"):
            print(law.id)

asyncio.run(main())

Webhooks

Verify a signed delivery in one call:

from legalize import Webhook, WebhookVerificationError

try:
    event = Webhook.verify(
        payload=request.body,                              # raw bytes
        sig_header=request.headers["X-Legalize-Signature"],
        timestamp=request.headers["X-Legalize-Timestamp"],
        secret=os.environ["LEGALIZE_WHSEC"],
    )
except WebhookVerificationError:
    return Response(status_code=400)

if event.type == "law.updated":
    ...

Working Flask and FastAPI receivers in examples/.

Configuration

Zero-config (recommended for servers + Kubernetes)

Set the environment and just instantiate:

export LEGALIZE_API_KEY=leg_live_...
# Optional:
export LEGALIZE_BASE_URL=https://legalize.dev
export LEGALIZE_API_VERSION=v1
from legalize import Legalize

client = Legalize()   # picks everything up from the environment

Explicit

from legalize import Legalize, RetryPolicy

client = Legalize(
    api_key="leg_...",
    base_url="https://legalize.dev",
    api_version="v1",              # negotiated via Legalize-API-Version
    timeout=30.0,
    retry=RetryPolicy(max_retries=5, initial_delay=0.5, max_delay=10.0),
    default_headers={"X-Correlation-Id": "..."},
)

Precedence: explicit argument > environment variable > built-in default. The full cross-SDK contract is documented in ENVIRONMENT.md.

Read rate-limit headers from the last response:

client.countries.list()
resp = client.last_response
print(resp.headers.get("X-RateLimit-Remaining"))

Errors

All errors inherit from LegalizeError. Catch the specific one you care about and let the rest bubble:

from legalize import (
    AuthenticationError,   # 401 — bad/missing key
    ForbiddenError,        # 403
    NotFoundError,         # 404
    InvalidRequestError,   # 400
    ValidationError,       # 422
    RateLimitError,        # 429 — retried automatically by default
    ServerError,           # 5xx
    APIConnectionError,    # network failure
    APITimeoutError,       # timeout
    WebhookVerificationError,
)

Every APIError exposes .status_code, .code, .body, and .response for debugging.

Compatibility

  • Python 3.10, 3.11, 3.12, 3.13
  • Linux, macOS, Windows
  • httpx ≥ 0.27, pydantic ≥ 2.6

Links

License

MIT

Project details


Download files

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

Source Distribution

legalize-0.2.0.tar.gz (26.5 kB view details)

Uploaded Source

Built Distribution

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

legalize-0.2.0-py3-none-any.whl (31.0 kB view details)

Uploaded Python 3

File details

Details for the file legalize-0.2.0.tar.gz.

File metadata

  • Download URL: legalize-0.2.0.tar.gz
  • Upload date:
  • Size: 26.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for legalize-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6733dbb66a141d240fc8d6f33a6c3bab016198a90874e32fa65a6402361834fe
MD5 23e45f7d4f4009b2b6e5e9acfd9512ce
BLAKE2b-256 ad7eb139dd39e7a1634a9deaf49bfe2acca253c17cd41bbf8dc368db789c4f4e

See more details on using hashes here.

Provenance

The following attestation bundles were made for legalize-0.2.0.tar.gz:

Publisher: python-publish.yml on legalize-dev/legalize-sdks

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

File details

Details for the file legalize-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: legalize-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 31.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for legalize-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9e17f4f39a1503eac363c9914a574e76feb489da6b7ba5484e22b9c5678c91e8
MD5 942737cf254524ae6e28392edd35d6ca
BLAKE2b-256 b940e2752c32a601280b55adfe8b3f7d18053edb8304aea193ba7c4ece5110ca

See more details on using hashes here.

Provenance

The following attestation bundles were made for legalize-0.2.0-py3-none-any.whl:

Publisher: python-publish.yml on legalize-dev/legalize-sdks

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page