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
1. Full-text search
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.