Skip to main content

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")

# ...page by page
page2 = client.laws.search(country="es", q="protección de datos", page=2)

# ...or every match, paginated for you
for law in client.laws.search_iter(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

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.1.tar.gz (26.9 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.1-py3-none-any.whl (31.1 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for legalize-0.2.1.tar.gz
Algorithm Hash digest
SHA256 b9ec26e02ae43812e4bd47d9a764d79884b3c8df429473bb5dd5ad77af19e27a
MD5 5efb631a8c3a88e16391bbd8128439e0
BLAKE2b-256 b670eb0946979aac407b5564a2c5d91801bc357385c88a729d6a8d78e3f5517a

See more details on using hashes here.

Provenance

The following attestation bundles were made for legalize-0.2.1.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.1-py3-none-any.whl.

File metadata

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

File hashes

Hashes for legalize-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f1c5719f0739b5fcb47ee18e673f4c91947ff733871c225ca9a881a6ac64eaaa
MD5 91b5e15913ae8ee8dac1b817d386788a
BLAKE2b-256 1e97d2b670c94744e8e4e578a66e8c056f656f864c20d5980fa7c477ce2aab9f

See more details on using hashes here.

Provenance

The following attestation bundles were made for legalize-0.2.1-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.

Release history Release notifications | RSS feed

0.3.0

2 files

This release

0.2.1 This release

2 files

0.2.0

2 files

0.1.1

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