Elixir client for the Croatian Court Register (Sudski registar) public open-data REST API served at https://sudreg-data.gov.hr/.

The portal exposes machine-readable data about Croatian legal entities (limited companies, sole traders, institutions, cooperatives, …) — court of jurisdiction, MBS, OIB, status, name, registered seat, share capital, legal form, activities, proceedings, financial reports and more. SudregEx wraps all 39 v3 "javni" (public) endpoints, handles OAuth2 auth and token caching, and adds pagination, snapshot and formatting helpers.

Installation

Add :sudreg_ex to your dependencies in mix.exs:

def deps do
  [
    {:sudreg_ex, "~> 0.1"}
  ]
end

Getting access

  1. Register (free) at https://sudreg-data.gov.hr/ and verify your e-mail to obtain a client_id and client_secret.

    The generated credentials end with two dots (..) — that is part of the credential, not a display artifact. Pass them verbatim.

  2. The API uses the OAuth2 client-credentials flow; SudregEx exchanges the credentials for a bearer token (valid 6 hours) and caches it for you.

Getting 401 Unauthorized on every data call? The token endpoint can issue a token before your account is fully activated, but the data endpoints stay 401 until you confirm your registration e-mail — open the verification message and click "Potvrdi" (Confirm). After that the same credentials and token work. (So a working SudregEx.Auth.fetch_token/1 plus 401s everywhere else almost always means an unconfirmed account, not bad credentials.)

A test environment mirrors production at https://sudreg-data-test.gov.hr/.

Configuration

Provide credentials via application config (merged into every SudregEx.Client.new/1):

config :sudreg_ex, :client,
  client_id: System.get_env("SUDREG_CLIENT_ID"),
  client_secret: System.get_env("SUDREG_CLIENT_SECRET")

A token-cache process starts by default; disable it with config :sudreg_ex, start_token_cache: false and call SudregEx.Auth.fetch_token/1 yourself. To point at the test environment, override the URLs on the client:

SudregEx.Client.new(
  base_url: "https://sudreg-data-test.gov.hr/api/javni",
  token_url: "https://sudreg-data-test.gov.hr/api/oauth/token"
)

Usage

client = SudregEx.Client.new(client_id: "your-id..", client_secret: "your-secret..")

# one record type for all subjects (paginated)
{:ok, %SudregEx.Response{data: rows, total_count: total}} =
  SudregEx.Api.subjekti(client, only_active: true, limit: 50)

# everything about one subject (the API caps this at 6/min — see Throttling)
{:ok, %SudregEx.Response{data: subject}} =
  SudregEx.Api.detalji_subjekta(client, tip_identifikatora: "oib", identifikator: "12345678901")

# code tables (šifrarnici)
{:ok, %SudregEx.Response{data: countries}} = SudregEx.Api.drzave(client)

Each endpoint is a function on SudregEx.Api taking (client, opts). opts mixes the endpoint's query params (whitelisted per endpoint — an unsupported param raises ArgumentError) with the pipeline opts :token, :token_cache, :format. Booleans become "1"/"0"; nil params are dropped.

Pagination

SudregEx.Api.stream/3 lazily walks a paginated endpoint. Pin a snapshot so the walk isn't split across the daily refresh:

{:ok, snapshot_id} = SudregEx.Api.latest_snapshot_id(client)

SudregEx.Api.stream(client, :tvrtke, snapshot_id: snapshot_id)
|> Stream.map(& &1["ime"])
|> Enum.each(&IO.puts/1)

Throttling detalji_subjekta

detalji_subjekta is rate-limited to 6 requests/minute for public users. The optional SudregEx.RateLimiter enforces it client-side:

{:ok, _} = SudregEx.RateLimiter.start_link(name: :sudreg_limiter)

:ok = SudregEx.RateLimiter.acquire(:sudreg_limiter)
SudregEx.Api.detalji_subjekta(client, tip_identifikatora: "mbs", identifikator: "080000014")

Display helpers

Identifiers come back as numbers without leading zeros; timestamps carry no timezone:

SudregEx.Format.format_mbs(80_000_014)        #=> "080000014"
SudregEx.Format.format_oib(12_345_678_901)    #=> "12345678901"
SudregEx.Format.parse_timestamp("2026-06-15T00:00:00")
#=> {:ok, ~N[2026-06-15 00:00:00]}

Results and errors

Success is {:ok, %SudregEx.Response{}}data (the decoded rows) plus metadata harvested from response headers (snapshot_id, total_count, rows_returned, log_id, seconds_elapsed, timestamp). Failure is {:error, %SudregEx.Error{}} carrying http_status and, for API JSON errors, error_code / error_message / log_id (quote log_id when contacting support).

Key API concepts

  • Identifiers — subjects are keyed by MBS (9-digit court register number) and OIB (11-digit tax number), returned as numbers without leading zeros (use SudregEx.Format).
  • Snapshots — data refreshes once per working day; each refresh is a numbered snapshot. Pass snapshot_id: to keep multi-call reads consistent.
  • Two usage modesdetalji_subjekta returns everything about one subject in one call (rate-limited, not for bulk); the per-table endpoints return one kind of record for all subjects (for building a local replica via stream/3).
  • Historisation — rows carry status (1 active, 0 historical, 5 deleted, 8/9 inconsistent) and prbu_od/prbu_do (entry ordinals). Pass only_active: true for just the currently-valid rows.

Reference

The OpenAPI 3.0 spec for the public service ships with the package at priv/doc/open_api_javni_v3.json (39 endpoints). The official developer guide (Croatian PDF) is committed in the repository at priv/doc/upute-za-razvojne-inzenjere-v3.0.0.pdf and available on the portal.

License

MIT — see LICENSE.