Skip to content

CKAN-Compatible API

KUKAN provides read-only endpoints compatible with the CKAN Action API. Responses take the shapes CKAN 2.12 returns, so existing CKAN clients (such as ckanapi), CKAN harvesters and scripts written for CKAN can read KUKAN as they are.

Base path: /api/3/action (the unversioned /api/action serves the same; it is what ckanapi calls by default)

Every action answers both GET and POST.

  • GET: parameters in the query string (?id=...). A list is a key given more than once, or a JSON array as a string (fq_list=["tags:a","tags:b"]). As in CKAN, organizations / groups on organization_list / group_list may also be a comma-separated string
  • POST: parameters in a JSON body (Content-Type: application/json) or a form (application/x-www-form-urlencoded). ckanapi uses POST by default. The body may be up to 1 MiB (413 beyond it); multipart is not accepted (400)

No authentication is needed. With an API token (Authentication), the private datasets of your organizations can be read too (in package_search, only with include_private=true). The token may be sent in any of these forms:

  • Authorization: Bearer <token>
  • Authorization: <token> (as ckanapi sends it)
  • X-CKAN-API-Key: <token>
  • X-KUKAN-API-Key: <token>

When the site sits behind basic auth, use either of the last two rather than Authorization.

{
"help": "https://your-kukan-site/api/3/action/help_show?name=package_show",
"success": true,
"result": { ... }
}

As in CKAN, error.__type names the kind of failure and the HTTP status tells them apart.

Case Status error
Not found 404 {"__type": "Not Found Error", "message": "Not found: ..."}
Invalid or unsupported parameter 409 {"__type": "Validation Error", "<parameter>": ["<reason>"]}
Not permitted 403 {"__type": "Authorization Error", "message": "Access denied"}
Unknown action, malformed JSON 400 No envelope; the body is the string "Bad request: ..."
{
"help": "https://your-kukan-site/api/3/action/help_show?name=package_show",
"success": false,
"error": { "id": ["Missing value"], "__type": "Validation Error" }
}
Action Description Main parameters
package_list Names of the public datasets limit, offset
package_show Dataset detail id (name or ID)
package_search Dataset search q, fq, fq_list, sort, rows, start, facet, facet.field, facet.limit, facet.mincount, include_private (below)
resource_show Resource detail id
organization_list Organizations all_fields, sort, limit, offset, q, organizations, include_extras
organization_show Organization detail id, include_datasets
group_list Categories all_fields, sort, limit, offset, q, groups, include_extras
group_show Category detail id, include_datasets
tag_list Tag names query (q), all_fields
tag_show Tag detail id (name or ID), include_datasets
license_list Licenses —
help_show Description of an action (the help URL) name

package_count and the datasets include_datasets returns in organization_show / group_show are the ones the caller can read, private datasets of their organizations included. As in CKAN, include_datasets returns up to 10 datasets for an organization and up to 1000 for a category. tag_show’s include_datasets likewise returns the datasets the caller can read.

Defaults follow CKAN’s (package_search returns 10 rows by default, at most 1000; organization_list sorts by title asc; limit is at most 25 with all_fields; and so on). package_search’s q is at most 500 characters and start + rows at most 10,000; past either, 409.

Field names and value shapes are those of CKAN 2.12’s package_show, resource_show and so on (author_email, url_type, num_resources, isopen, license_url, …). Timestamps are UTC without an offset (2024-01-02T03:04:05.678000).

  • Datasets, organizations and categories: a list of [{"key": "...", "value": "..."}]. Values are strings; a value that is not a string is given as its JSON text
  • Resources: as in CKAN, each key of extras is spread onto the resource’s top level. There is no nested extras
  • The url of an uploaded file is its download URL (/api/v1/resources/{id}/download)
  • section (a heading on the dataset page; see the REST API) appears as a top-level field when set. Harvested into CKAN, it is kept as a resource extra. section is a reserved extras key
  • KUKAN’s internal values (pipeline state, version numbers, health check results, …) are left out: CKAN treats unknown top-level fields on a resource as extras
  • Organization and category members (users) are not returned. KUKAN does not publish who belongs to them; for the same reason there is no member_count
  • AI-written descriptions (summary), a dataset’s AI-generated fields and the quality score are not included
  • CKAN’s relationships_as_subject / relationships_as_object are always empty, as are groups and tags in organization_show / group_show; num_followers is 0
  • Draft datasets do not appear (package_show answers 404) until they are published

Search is keyword matching only, with no semantic search. q is read as keywords (*:* matches everything); Solr’s field syntax is not interpreted.

include_private defaults to false: as in CKAN, private datasets are left out unless it is set, even with a token.

fq (a string) and fq_list (a list of strings) are read as whitespace-separated terms joined by AND. A leading + and quoted values ("...") are accepted.

Term Meaning
organization:<name> Organization (by name)
owner_org:<ID> Organization (by ID)
groups:<name> Category
tags:<name> Tag
res_format:<format> Resource format (case-insensitive)
license_id:<ID> License
extras_<key>:<value> A dataset’s extras (see below)
res_extras_<key>:<value> Some resource’s extras (see below)
metadata_modified:[<from> TO <to>] Modified within the range: UTC ending in Z, * or NOW
capacity:public / capacity:private Public / private
dataset_type:dataset, state:active Always true (any other value matches nothing)

What cannot be read is refused with 409 rather than ignored (OR, negation with - or NOT, parentheses, fields not in the table, date math). Times are kept to the millisecond; a finer end is rounded outward (down for the start, up for the end), so nothing in the range is lost. So is site_id: KUKAN has no site identifier to compare it with. Ignoring it would return more than was asked for, with no way for the caller to notice.

Extras are compared differently from CKAN.

  • Values are compared whole. CKAN’s extras_* fields are of type text and match words, so KUKAN may return fewer results. Numbers and arrays are handled as in the REST API
  • fq splits field from value at :, so a key containing : or whitespace cannot be written there. Use the REST API’s extras_<key> parameter
  • Every key of a resource’s extras can be filtered on. CKAN reads only the keys listed in ckan.extra_resource_fields (none by default)
  • The bare-key fields CKAN also indexes (spatial_unit:...) are not read, and answer 409
  • Solr syntax in a value answers 409 rather than being looked for as written: outside quotes, * ? ^ ~ : ( ) [ ] { } " and a leading ! / + -; a backslash inside or outside them. To look for a value holding one, quote it: extras_k:"-5"

score desc (the search’s ranking) plus at most one of metadata_modified, metadata_created, name and id (each asc or desc). The default is score desc, metadata_modified desc. Other fields, such as title_string, answer 409.

With score first and a q, results are in ranking order, ties newest first; so with a q, only metadata_modified desc may follow score. Without a q every score is equal, and the field after score decides. score may only come first.

facet.field (a JSON list) accepts organization, groups, tags, res_format and license_id. facets and search_facets in the result take CKAN’s shapes. At most 200 values are counted per field. With facet.mincount=0, values no result carries are given at 0 too, as far as the caller can see them.

count and the page boundaries (start, rows) are the search index’s. Making a dataset private or deleting it normally reaches the index before the response. When that lags, the dataset is left out of results meanwhile, so a page may be short and count a little high. Paging up to count misses nothing and repeats nothing.

from ckanapi import RemoteCKAN
ckan = RemoteCKAN('https://your-kukan-site')
packages = ckan.action.package_search(q='population', rows=10)
for pkg in packages['results']:
print(pkg['name'], pkg['title'])
ターミナルウィンドウ
# Dataset search (modified since 2024, newest first)
curl -G "https://your-kukan-site/api/3/action/package_search" \
--data-urlencode "q=population" \
--data-urlencode "fq=metadata_modified:[2024-01-01T00:00:00Z TO *]" \
--data-urlencode "sort=metadata_modified desc"
# Dataset detail
curl "https://your-kukan-site/api/3/action/package_show?id=population-stats-2024"
  • Read-only. Write actions (package_create and so on) are not provided; create and edit datasets through the REST API (/api/v1/*)
  • Actions not listed above, such as status_show, answer 400
  • Tag vocabularies are not supported: vocabulary_id on tag_list, and include_datasets on tag_show for a vocabulary tag, answer 409