Skip to main content

echr-py

echr-py

Acquire multilingual ECtHR case law, preserve its legal structure, resolve citations to exact documents and paragraphs, search locally, build datasets, and export publication-ready graphs.

CI Python 3.11–3.14 MIT license Version 0.2.3

echr-py is a research engineering toolkit for the case law of the European Court of Human Rights. It connects public HUDOC and HUDOC-EXEC acquisition to typed, source-addressed research artifacts through Python, a command-line interface, and the Model Context Protocol (MCP).

It is designed for work where provenance matters. A result can retain the exact HUDOC document, language version, section, paragraph, individual opinion, footnote, citation span, cited procedural document, cited-paragraph pinpoint, source checksum, and producing software revision.

The core acquisition, parsing, citation, and graph pipeline is deterministic.

Acquisition is concurrent and bounded. In a recorded July 2026 public-HUDOC run it hydrated nine documents and wrote 478,151 characters of source-ordered text in about 1.1 seconds at concurrency twelve, with the exact query, settings, item IDs, hashes, and elapsed time recorded alongside the figure.

One inspectable chain runs from a HUDOC record to a verified paragraph edge. Each stage keeps its own identity, so a later stage never overwrites an earlier one:

Stage Identity retained at that stage
HUDOC record item ID, ECLI, language, source checksum
Legal spine canonical sections, stable block IDs, paragraph addresses, typography
Source identity majority, individual opinion, footnote, invoking paragraph
Citation occurrence exact printed span, supporting evidence, pinpoint ownership
Exact target cited document, cited application, or an explicit unresolved scope
Target paragraph printed pinpoint mapped to a verified block in the cited document

That is the central idea: retain enough structure and provenance to move from a public HUDOC record to a multilingual paragraph, a printed citation occurrence, an exact target document, and, when the source supplies a pinpoint, a verified target paragraph. Deterministic identity and graph layers stay separate from optional, evidence-verified labels.

Start here

python -m pip install "echr-py[citations,analysis]"

Build a rich case artifact and an offline paragraph-citation viewer:

echr-py corpus build --appno 46221/99 --rich-sections \
  --citations --out ocalan/

echr-py citations locate --in ocalan/cases.parquet \
  --resolution-dir ocalan/citations --scope inclusive \
  --out ocalan/citations

echr-py graph export --kind citation-paragraph --format html \
  --in ocalan/citations --out ocalan/paragraph-citations.html

Metadata page size and full-text download concurrency are separately tunable; see acquisition performance and tuning.

Live bounded acquisition

Live echr-py case and metadata acquisition

In the recorded July 2026 public-HUDOC run, echr-py selected twelve metadata records, hydrated the nine records for which that response supplied usable official text, parsed 1,578 source-order blocks, and wrote 478,151 text characters in about 1.1 seconds with concurrency twelve. This is an observed demonstration, not a service guarantee; the provenance record contains the exact query, settings, item IDs, hashes and elapsed time.

Or use the Python API:

from hudoc_py import fetch_case, search

cases = search(
    article="3",
    respondent="ITA",
    date_from="2020-01-01",
    importance=[1, 2],
    languages=("ENG", "FRE"),
    limit=250,
)
cases.to_parquet("italy-article-3.parquet")

ocalan = fetch_case(
    appno="46221/99",
    language="ENG",
    with_text=True,
    rich_sections=True,
)

print(ocalan.sections.the_law[:500])
print(ocalan.sections.bench.judges)
print(ocalan.sections.opinions)

The async API mirrors the synchronous surface:

from hudoc_py import aio

cases = await aio.search(article="8", respondent="FRA", limit=50)

What becomes possible

Research task What echr-py provides
Build a multilingual case-law corpus Frozen selections, complete language-version discovery, resumable acquisition, checksums, typed failures, Parquet/JSONL/CSV/XLSX
Study precedent at paragraph level Exact printed citation spans, uniquely corroborated procedural targets, citation-owned pinpoints, verified cited paragraphs
Compare majority and individual opinions Stable opinion identities, type, ordinal, authors, joiners, source spans, and separate citation provenance
Analyse footnote citation practice Linked footnote bodies, invoking paragraphs, majority/opinion context, and footnote-owned citation edges
Search concepts across English and French SQLite FTS5, exact dense search, deterministic hybrid fusion, filters, stable paragraph IDs
Create a labelled research dataset Bounded YAML studies, structured outputs, exact evidence quotations, budgets, checkpoints, validation, JSONL/Parquet export
Explore citation and custom networks One graph contract with JSON, GEXF, and fully offline interactive HTML
Acquire implementation material Typed HUDOC-EXEC case and document search, resumable source downloads, text/Markdown conversion, OCR, and manifests

The citation contract

Most citation networks stop at an application number or an edge between two cases. echr-py keeps the layers researchers actually need separate:

SCL decision graph
    └── Court-supplied, selective cited-authority baseline

Inclusive occurrence ledger
    └── every occurrence accepted by the deterministic discovery rules
        ├── majority / procedure / facts / operative / appendix
        ├── individual opinion identity and authors
        └── linked footnote identity and invoking paragraph

Paragraph graph
    └── source paragraph → exact cited document → verified cited paragraph

This separation matters. One citation in a paragraph may carry § 54, while the next carries no pinpoint; two opinions may cite the same target for different propositions; and a footnote may cite an authority that never appears in the judgment's SCL field. echr-py preserves those distinctions instead of collapsing them into one count.

SCL is valuable bibliographic evidence, but it is not an exhaustive inventory of printed citations – including in the majority judgment. Inclusive discovery therefore starts from SCL without treating its absence as evidence that a paragraph contains no citation.

Occurrence v3 separates a target-independent printed locus_id from authority-specific occurrence_id rows. Compound references share a locus and citation_group_id; each authority row retains its ordinal and independently owned pinpoint. Footnote occurrences keep the physical footnote address and structured addresses for every invoking paragraph or opinion.

Bilingual Öcalan citation ledger SCL, inclusive, and paragraph citation layers
A bilingual source context with independently owned citation pinpoints. Provenance. The SCL, occurrence, and paragraph layers remain separately versioned. Provenance.

Resolution

The resolver combines printed SCL evidence, application numbers, ECLIs, HUDOC item IDs, document dates and types, procedural phases, reporter references, a packaged bilingual citation authority containing 42,161 official English and French rows plus six documented supplements, and a checksummed historical catalogue. It does not zip the unordered sclappnos pool to SCL fragments or promote an ambiguous application to an arbitrary judgment.

In a fixed July 2026 audit using authority parser 6 and the 10 July English authority, the then-current resolver assigned a document automatically to 2,479 of 2,492 SCL mentions (99.5%). That percentage measures automatic resolution coverage of the Court-supplied SCL sample only. It does not measure whether every assignment was correct, detector recall, or the share of all citations printed in the judgments. Thirteen references remained outside the graph pending review or documented exclusion. The sample, denominators, failure classes, method, and qualifications are in the citation-resolution audit.

Discovery beyond SCL

Full-text discovery parses bounded English and French citation envelopes, application numbers with compatible case names, ECLIs, reporters, dates, procedural phases, and §, §§, para. or paras. pinpoints. Once a strong anchor identifies an authority, a document-local gazetteer can recover later short forms under conservative uniqueness and context rules.

External, self-referential, ambiguous, and rejected candidates remain typed diagnostics. Only exact document resolutions enter the inclusive document graph; only verified target paragraphs enter the paragraph graph.

Commission-era report and admissibility references with Commission-specific dates or complete D.R. locators are retained as classified, unresolved occurrences. They cannot be silently promoted to a nearby Court judgment.

Rich legal structure

rich_sections=True builds a source-order document spine rather than a bag of paragraph strings. It preserves:

  • procedure, facts, complaints, law, operative text, and appendices;
  • stable physical-block and legal-paragraph addresses, including HTML continuations and repeated printed numbers;
  • inline bold and italic runs with offsets;
  • linked footnote anchors and multi-block bodies;
  • separate-opinion boundaries, types, authors, joiners, and source spans;
  • the deciding bench, independently of dissent authors;
  • represented-by metadata supplied by HUDOC;
  • individual dispositive rulings and recorded votes.

Plain-text inputs remain supported. HTML/DOCX sources provide the richer typography and footnote evidence.

echr-py versions list \
  --ecli ECLI:CE:ECHR:2005:0512JUD004622199

echr-py versions download \
  --ecli ECLI:CE:ECHR:2005:0512JUD004622199 \
  --formats html,txt,md,docx --out ocalan/

The acquisition manifest records each language-specific item ID, rendition type, requested-format outcome, HTTP status, path, byte length, and SHA-256.

Local lexical, dense, and hybrid retrieval

Build a portable paragraph index from any acquired corpus:

echr-py local index-paragraphs --data-dir corpus/ \
  --database corpus/paragraphs.sqlite

echr-py embeddings build --database corpus/paragraphs.sqlite \
  --provider sentence-transformers \
  --model intfloat/multilingual-e5-base \
  --model-revision EXACT_COMMIT \
  --section facts --section the_law --section separate_opinion \
  --include-footnotes --out corpus/embeddings/

echr-py local paragraphs '"positive obligations"' \
  --database corpus/paragraphs.sqlite --mode hybrid \
  --embeddings corpus/embeddings/ --limit 25

Dense indexes are normalized float32 vectors in Parquet. The manifest records the provider, exact model revision, dimensions, query and passage prefixes, section filter, source database hash, package version, and artifact checksum. Chunked exact cosine search is always available; FAISS is an optional, rebuildable accelerator. Hybrid mode uses deterministic reciprocal-rank fusion and returns lexical, dense, and fused scores separately.

Rich hudoc-paragraphs.v3 rows carry opinion type, authors, joiners, and footnote identity directly into retrieval results.

Optional evidence-grounded studies

The study runner is for creating datasets, not for handing control of the pipeline to an open-ended agent. A versioned YAML specification fixes:

  • corpus and unit of analysis;
  • ordered stages and retrieval settings;
  • provider and exact model for every model-backed stage;
  • JSON Schema output;
  • evidence requirements;
  • request, token, and dollar budgets;
  • output and checkpoint locations.
echr-py study init citation-use \
  --source corpus/citations/occurrences.parquet \
  --provider openai --model YOUR_EXPLICIT_MODEL \
  --taxonomy multiaxial --out citation-use.yaml

echr-py study validate citation-use.yaml
echr-py study plan citation-use.yaml --out runs/citation-use/
echr-py study run citation-use.yaml --out runs/citation-use/

Facts can be required to carry exact source quotations. Offsets are checked against the addressed source text; ambiguous repeated quotations and invalid evidence are rejected. Runs are resumable and model attempts remain distinguishable.

Supported structured-generation surfaces include Gemini, official OpenAI, OpenAI-compatible endpoints, and Anthropic. Native batch execution is explicit for Gemini and official OpenAI; local Sentence Transformers are available for embeddings. No provider or model is selected silently.

Graphs that travel with the research

hudoc-graph/v1 represents nodes and links once and exports them as:

  • typed JSON for downstream code;
  • GEXF for Gephi;
  • a single offline HTML file with vendored D3 and no CDN request.

Adapters cover the SCL decision graph, inclusive citations, paragraph citations, and custom NetworkX graphs.

echr-py graph export --kind citation-scl \
  --format gexf --in corpus/citations --out scl.gexf

echr-py graph export --kind citation-paragraph \
  --format html --in corpus/citations \
  --source-component opinion --out opinions.html

The browser viewer provides search, attribute filters, legends, direction and weight controls, component isolation, node-size and colour mappings, detail panels, and explicit top-N pruning.

Article 3 Italy citation network generated by echr-py Most-cited authorities in the Article 3 Italy demonstration
The complete reviewed SCL one-hop network for ten source documents. The same frozen artifacts summarized as the most-cited target authorities.

The pinned Article 3/Italy demonstration resolves all 1,124 SCL mentions in its ten source documents: 1,111 automatically and thirteen through documented, source-specific review overrides. Its provenance manifest records the query, source IDs, authority edition, counts, hashes, and generation date.

HUDOC-EXEC

The acquisition client connects merits judgments to the official Committee of Ministers supervision material while preserving HUDOC-EXEC metadata:

from hudoc_py.execution import fetch_case, search, search_documents

open_cases = search(
    state="ITA",
    supervision="enhanced",
    is_closed=False,
    limit=50,
)
execution = fetch_case("57818/09", with_documents=True)
plans = search_documents(collection="acp", appno="57818/09", limit=20)
echr-py exec search --state ITA --supervision enhanced \
  --out execution.jsonl
echr-py exec search-documents --collection acp --appno 57818/09 \
  --out plans.jsonl
echr-py exec fetch-case 57818/09 --out execution-case.json
echr-py exec download-raw --in plans.jsonl --out sources/ \
  --concurrency 10 --extract-text

HUDOC-EXEC support is a source-acquisition interface for building a traceable local collection. It discovers official cases and documents, downloads their source files, converts them to text or Markdown, and records the identifiers, checksums, and resumable manifests needed for reproducible work. Records are left unlabelled: research categories and substantive interpretation belong to the downstream project rather than the acquisition client.

Interfaces and commands

The same typed models and artifact identities are exposed through Python, the CLI, and MCP.

With uv and Claude Code installed, Claude Code can register the version-pinned, read-only MCP server without cloning the repository:

claude mcp add --scope user --transport stdio echr-py -- \
  "$(command -v uvx)" --from 'echr-py[mcp]==0.2.3' echr-py mcp
claude mcp get echr-py

The server itself needs no Anthropic API key. A one-click Claude Desktop MCPB will be attached only after the matching PyPI package and clean-machine bundle tests are complete; manual and source-checkout setup is documented in the MCP guide.

Live echr-py MCP terminal demo

The recorded July 2026 workflow below replays a fixed three-tool protocol: it identifies the exact Öcalan Grand Chamber judgment, recovers its structured spine, bench and separate opinions, then performs inclusive deterministic citation discovery and resolution. It uses real public HUDOC responses, but no model chooses the tools.

Live multi-tool echr-py MCP research workflow

That run recovered 553 source blocks, seventeen deciding-bench members, three individual opinions and 139 citation occurrences, including 24 opinion occurrences and 79 occurrence-owned paragraph pinpoints. Its provenance record retains the three tool arguments, timings, exact item ID, package version, and script hashes.

In the retained July 2026 model-driven test, Claude Sonnet 5 made three read-only echr-py calls: it selected the English judgment by exact ECLI, requested compact structural evidence, and asked echr-py to construct an occurrence-weighted citation network.

Claude Sonnet 5 building an Öcalan citation network through echr-py MCP

This is recorded output, not a mock terminal. The run found 139 citation occurrences across 78 resolved target documents: 115 in the majority text and 24 in separately identified opinions, with 79 occurrence-owned pinpoints. The top-five counts and resolution fraction in the animation are arithmetic over the tool response. The checked final answer confines itself to arithmetic over the returned network and makes no doctrinal claim. Inspect the portable JSON, GEXF, or downloadable single-file interactive HTML, which runs locally without a server or CDN, and the compact run provenance. Claude Code reported claude-sonnet-5 as the reasoning model and a small Haiku routing call separately.

The MCP surface makes the same source-addressed search, paragraph, case, citation, and graph operations available to an assistant without introducing a second data model. Compact structure and graph tools avoid forcing model clients to ingest entire judgments. The default server is read-only; bounded study jobs are an explicit opt-in.

Command group Purpose
search, count, fetch-case, smart-fetch HUDOC metadata and document acquisition
versions …, corpus … Language versions and reproducible corpora
local …, embeddings … Offline browsing and paragraph retrieval
gui Optional local Streamlit corpus browser
`citations authority catalog
`graph metrics export
`study validate plan
exec … HUDOC-EXEC record discovery, download, and source conversion
mcp Read-only MCP server by default; bounded jobs are opt-in
echr-py --help
echr-py citations --help
echr-py graph export --help
echr-py study --help
echr-py exec --help

The default MCP server exposes search, retrieval, case structure, citations, and graphs without mutating external state. Study jobs require an explicit --enable-jobs launch configuration with permitted roots, provider and hook allowlists, pricing, and per-job budgets.

See also

Other projects working on ECtHR material:

Benchmark

echr-py is compared against the curated Mumford annotation set. Those annotations are useful for studying selected citation treatments; they are not an exhaustive citation inventory or a target-paragraph graph. The annotation frame deliberately omits submissions, general-principles material, facts, procedure, operative text, appendices, individual opinions, and footnotes, so a full echr-py run can return citation occurrences from sections and components outside that frame. Any recovery figure measures recovery of those selected annotations, not detector precision or completeness over the judgments.

The importer and the methodological comparison remain available for researchers who need them, and the measured figures, denominators, hashes and reproduction records are kept in the methodology audit and the frozen benchmark manifest, where they can carry their conditions, rather than on the project homepage.

The evidence and dates behind external comparisons are maintained in the claim audit.

Installation options

Install only the components a project needs:

# Base HUDOC and HUDOC-EXEC client
python -m pip install echr-py

# Citation resolution and graph analysis
python -m pip install "echr-py[citations,analysis]"

# Research studies and a provider
python -m pip install "echr-py[research-agent,llm-openai]"

# Local dense retrieval
python -m pip install "echr-py[embeddings-local]"

# All user-facing extras
python -m pip install "echr-py[all]"

# Development checkout and validation
git clone https://github.com/niccoloridi/echr-py.git
cd echr-py
python -m pip install -e ".[all,dev]"

Names

The distribution and primary command are echr-py. The Python import is hudoc_py, and the earlier hudoc-py command still works; both remain available so existing code and scripts keep running.

import hudoc_py            # import name
echr-py --version          # primary command
hudoc-py --version         # legacy alias, same program

Documentation

Verification

python -m pip install -e ".[all,dev]"
ruff check .
mypy hudoc_py
pytest -q

The offline suite requires no API credentials, model downloads, or network. Live HUDOC, HUDOC-EXEC, provider, browser, and OCR tests are opt-in. Release CI builds the wheel and source distribution, checks both against the public feature boundary, installs the wheel in an isolated environment, verifies public imports, and produces provenance attestations.

Funding

This research forms part of the Human Rights Nudge project that has received funding from the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (Grant agreement No. 803981).

This work was also supported by a King’s Digital Futures Institute Fellowship.

Data responsibility

HUDOC and HUDOC-EXEC are evolving public systems. Preserve item IDs, ECLIs, languages, retrieval dates, checksums, and the producing package revision. Verify quotations, procedural status, and authoritative documents before legal or empirical reliance. Court texts and third-party translations retain their own source terms.

echr-py is an independent research tool, not an official Council of Europe or European Court of Human Rights product.

Licence

MIT for the project's original software. The licence does not cover upstream Court texts, database contents, independently published datasets, or vendored components such as D3; see Rights and third-party material.

Download files

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

Source Distribution

echr_py-0.2.3.tar.gz (15.5 MB view details)

Uploaded Source

Built Distribution

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

echr_py-0.2.3-py3-none-any.whl (15.7 MB view details)

Uploaded Python 3

File details

Details for the file echr_py-0.2.3.tar.gz.

File metadata

  • Download URL: echr_py-0.2.3.tar.gz
  • Upload date:
  • Size: 15.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for echr_py-0.2.3.tar.gz
Algorithm Hash digest
SHA256 1b09997b9244fc00c33da620374797173cae79baeca0638eb54e946a768a0de5
MD5 887c99d267f42d158a0061bf6f2b4b13
BLAKE2b-256 12378f38cc1100050badfa88e852fb059357ec3e607870173369e8a5293a3347

See more details on using hashes here.

Provenance

The following attestation bundles were made for echr_py-0.2.3.tar.gz:

Publisher: publish.yml on niccoloridi/echr-py

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

File details

Details for the file echr_py-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: echr_py-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 15.7 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for echr_py-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 a87b43bc93127b7865333d33532149d50760e54cbb74ec65760bcf0089f1e802
MD5 34c255ff97887e6258dabe5375c339b3
BLAKE2b-256 5323aff7e48bb2c2fb549ddbb15582bdaf48faf95e0ddcbe881265a670c01313

See more details on using hashes here.

Provenance

The following attestation bundles were made for echr_py-0.2.3-py3-none-any.whl:

Publisher: publish.yml on niccoloridi/echr-py

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.2.3 This release

2 files

0.2.2

2 files

0.2.1

2 files

0.2.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