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"}
]
endGetting access
Register (free) at https://sudreg-data.gov.hr/ and verify your e-mail to obtain a
client_idandclient_secret.The generated credentials end with two dots (
..) — that is part of the credential, not a display artifact. Pass them verbatim.The API uses the OAuth2 client-credentials flow;
SudregExexchanges the credentials for a bearer token (valid 6 hours) and caches it for you.
Getting
401 Unauthorizedon every data call? The token endpoint can issue a token before your account is fully activated, but the data endpoints stay401until you confirm your registration e-mail — open the verification message and click "Potvrdi" (Confirm). After that the same credentials and token work. (So a workingSudregEx.Auth.fetch_token/1plus 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 modes —
detalji_subjektareturns 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 viastream/3). - Historisation — rows carry
status(1 active, 0 historical, 5 deleted, 8/9 inconsistent) andprbu_od/prbu_do(entry ordinals). Passonly_active: truefor 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.