ckan-cli — Usage

Real, use-case-driven examples for the ckan CLI: a read-only client for the CKAN Action API of any CKAN open-data portal — by default the Hamburg Transparenzportal (suche.transparenz.hamburg.de). Search datasets and documents, inspect records and resources, count with facets, and switch to any other portal.

CKAN wraps every response in { help, success, result }. This CLI prints the unwrapped result and exits non-zero on an error, so the examples below pipe straight into jq. Numbers are re-serialised by JavaScript: an integer above 2⁵³ loses its last digits and one beyond the double range (1e400) prints as null (rare in CKAN metadata).

Install

npm i -g @maschinenlesbar.org/ckan-cli

This installs the ckan binary. Without a global install you can run it straight from the build output: node apps/cli/dist/src/cli/index.js <command>. The examples use ckan.

Use cases

ckan search elbe --rows 5
ckan --compact search elbe --rows 5 | jq -r '.results[] | "\(.name)  \(.title)"'

count is the total number of hits; results holds at most --rows of them.

2. Newest records first

ckan search Radverkehr --rows 10 --sort "metadata_created desc"

3. Filter — one or several --fq

# Hamburg's administrative regulations (Verwaltungsvorschriften)
ckan search --fq extras_registerobject_type:verwaltungsvorschrift --rows 5

# Two filters: both must match (sent as CKAN's fq_list)
ckan search --fq extras_registerobject_type:gutacht --fq 'metadata_created:[2025-01-01T00:00:00Z TO *]' --rows 5

On Hamburg, filter the document type with its stemmed value (gutacht, vertrageoffinteress, senatmitteil …); see the Glossary.

4. Count with facets

# Which publishers and formats, and how often (all values, not just the top 50)
ckan --compact search --rows 0 --facet organization --facet res_format --facet-limit -1 | jq '.facets'

# Hamburg's document types
ckan --compact search --rows 0 --facet extras_registerobject_type --facet-limit -1 | jq '.facets.extras_registerobject_type'

5. Page through a large result set

ckan --compact search elbe --rows 100 --start 0   | jq '.results | length'
ckan --compact search elbe --rows 100 --start 100 | jq '.results | length'

--rows is capped by the portal (1000 on Hamburg); page on with --start.

6. One record and its resources

ckan package bezirk-wandsbek-drucksache-22-3573-2
ckan --compact package bezirk-wandsbek-drucksache-22-3573-2 | jq '.resources[] | {format, url}'
ckan resource 4bb495c8-1180-434d-b1a5-10191a795a48

7. Publishers, themes and tags

ckan organizations
ckan --compact organizations --all-fields | jq '.[] | {name, title, package_count}'
ckan groups
ckan tags --query Alster

--all-fields lists are complete: CKAN caps them at 25 entries, and the client pages past that (0.1 s between pages; a server whose pages never end is stopped after 400 pages with an error, exit 1).

8. Another portal

ckan portals                                       # the known German portals
ckan --portal berlin search Radverkehr --rows 3    # one of them, by id
ckan --base-url https://ckan.govdata.de status     # any CKAN, by site URL
export CKAN_BASE_URL=https://www.opendata-hro.de   # for a whole session
ckan status

9. Check the known portals live

ckan --compact portals --check | jq -r '.[] | "\(.id)\t\(.working)\t\(.datasets)"'

10. Call any read action

ckan action package_autocomplete --param q=elbe --param limit=3
ckan action help_show --param name=package_search

Global options

Option Description
--compact Print JSON on a single line instead of pretty-printed
--log-format <format> How errors, warnings and notes are written to stderr: text (default; log4j style, 2026-10-09T14:03:12.481Z WARN [ckan.http] …) or jsonl (one JSON object per line: ts, level, topic, msg). stdout is not affected
--base-url <url> CKAN site URL (env CKAN_BASE_URL; default https://suche.transparenz.hamburg.de)
--portal <id> a known portal by id (ckan portals); cannot be combined with --base-url
--timeout <ms> Per-request timeout (default 30000; at most 2147483647)
--user-agent <ua> User-Agent header value
--max-retries <n> Retries for transient 429/503 responses (0–10, default 2; each backs off linearly, or waits the server’s Retry-After when that is longer, up to 30 s — a longer one is not retried and the error names it). Each retry logs one WARN record of ckan.http before it waits (HTTP 503 from host: retry 1 of 3 in 2 s).
--max-response-bytes <n> Cap response body size in bytes (0 = unlimited; default 100 MiB)

A base URL on plain http: (from --base-url or CKAN_BASE_URL) to a host other than loopback (localhost, 127.0.0.0/8, ::1) works, but the CLI writes one WARN record of ckan.http to stderr before the first request, e.g. … WARN [ckan.http] requests to ckan.example are sent unencrypted (http:, not https:), or … WARN [ckan.http] the base URL's credentials are sent unencrypted to ckan.example (http:, not https:) when it carries a user:password@ (never printed). stdout and the exit code are unchanged.

Blank values (search "", --fq "", an empty id, --param q=) are usage errors, never an unfiltered search. So is a single-value option given twice (--rows 5 --rows 50, a second --base-url): only --fq, --facet and --param repeat. Exit codes: 0 success, 4 not found (HTTP 404, or CKAN’s Not Found Error with HTTP 200: an unknown id, or no CKAN at this URL), 1 anything else.

View the source on GitHub →