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

Or skip the SHA lookup entirely and ask by date:

at = client.laws.at_date(country="es", law_id="ley_organica_3_2018", date="2019-05-13")
print(at.sha, at.version_date)  # which version answered, so you can cite it

The rule is published on or before the date, not in force on it.

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.3.0.tar.gz (27.7 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.3.0-py3-none-any.whl (31.8 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for legalize-0.3.0.tar.gz
Algorithm Hash digest
SHA256 7cccfd52165d3afe73ef5f1732321a40ceeacb61ed7c1d8a23661faf49ace7aa
MD5 ac96550a0354ec0ec39963277e3bca38
BLAKE2b-256 2247249c595be8091be4701de4462094fe29e47cb957aa3d30227e14d32a29fd

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: legalize-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 31.8 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.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 37ddb98f442273b09b5051f2970cffe34a828cbcd39cb312e9ab1836ce0df5bb
MD5 647840ac890196b861df50f73f2c4d7a
BLAKE2b-256 a4148985f3b948fac707f7e898185a7181b3f53eb6a6f45dcd8a712940937fe9

See more details on using hashes here.

Provenance

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

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.1

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