tutorial 11 min read

Czech Business Registry Deep Dive: ARES, Justice.cz, and Beyond

A technical guide to querying Czech business registries programmatically. Covers ARES XML API, Justice.cz scraping patterns, Insolvency Registry, and how Prismatic unifies these sources.

Tomas Korcak (korczis) 790 words

The Czech Republic maintains several public business registries that provide rich data about companies, their ownership, financial health, and legal status. For OSINT practitioners, due diligence analysts, and compliance teams, these registries are essential data sources. This guide covers how to query them programmatically and how Prismatic unifies them into a single intelligence feed.

The Czech Registry Landscape

RegistryOperatorDataAccess
ARESMinistry of FinanceBusiness register aggregationREST/XML API
Justice.czMinistry of JusticeCommercial register, directorsWeb + SOAP
Insolvency RegistryMinistry of JusticeInsolvency proceedingsREST API
Trade RegisterMunicipal officesTrade licensesWeb scraping
Beneficial OwnershipMinistry of JusticeUBO dataRestricted API
CUZKCadastral OfficeLand and property recordsRestricted

ARES: The Primary Gateway

ARES (Administrativni Registr Ekonomickych Subjektu) is the Czech government's aggregation point for business data. It pulls from multiple source registries and provides a unified API.

Basic ICO Lookup

defmodule PrismaticOsintSources.Adapters.Czech.ARES do
  @base_url "https://ares.gov.cz/ekonomicke-subjekty-v-be/rest"

  def search(query, opts \\ []) do
    url = "#{@base_url}/ekonomicke-subjekty/vyhledat"

    params = %{
      "obchodniJmeno" => query,
      "start" => Keyword.get(opts, :offset, 0),
      "pocet" => Keyword.get(opts, :limit, 25)
    }

    case HTTPClient.get(url, params: params) do
      {:ok, %{status: 200, body: body}} ->
        {:ok, parse_ares_response(body)}
      {:ok, %{status: status}} ->
        {:error, {:http_error, status}}
      {:error, reason} ->
        {:error, reason}
    end
  end
end

ARES returns JSON with company details: ICO (identification number), name, registered address, legal form, date of establishment, and industry classification (CZ-NACE codes).

Rate Limiting

ARES does not publish official rate limits but enforces them. Through empirical testing, we have established safe limits:

  • Search queries: 30 requests per minute
  • ICO lookups: 60 requests per minute
  • Burst: up to 5 concurrent requests

Prismatic's adapter declares these limits in metadata/0, and the pipeline's circuit breaker enforces them automatically.

Justice.cz: The Commercial Register

The Justice Registry (or.justice.cz) provides the official commercial register with director information, shareholder details, and corporate filings. Unlike ARES, Justice.cz does not provide a clean REST API.

Data Available

  • Company officers (directors, board members, supervisory board)
  • Shareholders and ownership percentages
  • Registered capital and share structure
  • Historical changes (date-stamped)
  • Filed documents (annual reports, articles of association)

Access Pattern

Justice.cz exposes a SOAP endpoint and structured HTML pages. Prismatic's adapter handles both:

def search(query, opts \\ []) do
  # First: try the structured endpoint
  case fetch_from_api(query) do
    {:ok, results} -> {:ok, results}
    {:error, _} ->
      # Fallback: structured HTML parsing
      fetch_from_web(query, opts)
  end
end

The HTML parsing extracts structured data from the registry's table-based layout, handling Czech diacritics and date formats.

Insolvency Registry

The Czech Insolvency Registry (ISIR) provides data about insolvency proceedings -- a critical signal for due diligence:

def search(query, opts \\ []) do
  url = "https://isir.justice.cz/isir/ueu/vysledek_lustrace.do"

  params = %{
    "ic" => query,           # ICO lookup
    "typ_osoby" => "P"       # P = pravnicka osoba (legal entity)
  }

  case HTTPClient.get(url, params: params) do
    {:ok, %{status: 200, body: body}} ->
      {:ok, parse_insolvency_results(body)}
    _ ->
      {:error, :insolvency_lookup_failed}
  end
end

A clean insolvency record is a positive signal. Active proceedings are a red flag. Historical proceedings require investigation into the resolution.

Unifying Multiple Registries

The real power emerges when you combine data from all registries:

def comprehensive_lookup(ico) do
  tasks = [
    Task.async(fn -> ARES.search(ico) end),
    Task.async(fn -> Justice.search(ico) end),
    Task.async(fn -> Insolvency.search(ico) end),
    Task.async(fn -> TradeRegister.search(ico) end)
  ]

  results = Task.await_many(tasks, timeout: 15_000)

  merge_results(results)
end

The merge_results/1 function applies entity resolution to combine data from all sources into a single company profile with confidence scores per field.

Data Quality Considerations

Czech registries have known quirks:

  • Address formats vary across registries (ARES uses structured addresses, Justice uses free text)
  • Historical data may show outdated information (company moved but registry not updated)
  • Diacritics handling differs (some registries normalize, others preserve)
  • Response times vary significantly (ARES: 200ms, Justice: 1-5s, ISIR: 500ms)

Prismatic handles these by normalizing all data to a common schema, tracking data freshness per source, and weighting more authoritative sources higher in conflict resolution.

Confidence Scoring for Czech Sources

Czech official registries receive high confidence scores in Prismatic's evidence chain:

SourceConfidence RangeRationale
ARES0.95 - 1.00Official government aggregation
Justice.cz0.95 - 1.00Legal authority for commercial data
ISIR0.95 - 1.00Official insolvency records
Trade Register0.85 - 0.95Municipal, slightly less structured
Beneficial Ownership0.90 - 0.98Official but self-reported

Getting Started

Query Czech registries through Prismatic's unified OSINT interface:

# Using the OSINT API
curl -X POST http://localhost:4004/api/v1/osint/execute_tool \
  -H "Content-Type: application/json" \
  -d '{"slug": "czech-ares", "input": {"query": "27082440"}, "params": {}}'

Or use the interactive toolbox at /hub/osint/tools to explore all Czech adapters with a visual interface.

Elixir Acronym Aliases: The ISIR Casing Lesson (2026-04-24)

A subtle bug hid in the compliance platform for months. Callers reference PrismaticOsintSources.Adapters.Czech.ISIR (all-caps, the natural Czech acronym form). But Elixir's module-case convention derives module names from filenames: isir.ex -> Isir, not ISIR. The compiler flags ISIR.search/1 as undefined but defers the crash to runtime.

On a production call this would UndefinedFunctionError the instant the polling worker asks ISIR for insolvency updates. Silent until it isn't.

The fix is a backward-compatible alias:

alias PrismaticOsintSources.Adapters.Czech.{Ares, CommercialRegister, Isir}
alias Isir, as: ISIR

With this, every caller that types the acronym form (ISIR.search_by_ico(ico), ISIR.health_check(), etc.) resolves to the real Isir module --- no call-site rewrites, no breakage. Nine files across prismatic_cer + prismatic_osint_czech_legal carried the bug; all fixed in one sweep.

Lesson: when a Czech (or any) registry uses an all-caps acronym (ARES, ISIR, CEDR, RZP, DPH, CNB, CTU, CUZK, ISVS, RES, UOHS, SUKL, SZIF), pick one stance and hold it:

  1. CamelCase module, acronym alias per file --- what we adopted, works everywhere.
  2. All-caps module names --- feasible but fights Elixir's code-generators (mix phx.gen.* emit CamelCase), breaks test generators, and surprises any newcomer who skimmed the docs once.

Option 1 wins. Always name the file isir.ex, make the module Isir, and drop alias Isir, as: ISIR where legacy naming bleeds through.


Browse all 128 OSINT adapters at OSINT Sources or learn adapter development in the Interactive Academy.