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)
Calling
Section titled “Calling”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/groupsonorganization_list/group_listmay also be a comma-separated string - POST: parameters in a JSON body (
Content-Type: application/json) or a form (application/x-www-form-urlencoded).ckanapiuses POST by default. The body may be up to 1 MiB (413beyond it); multipart is not accepted (400)
Authentication
Section titled “Authentication”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>(asckanapisends 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.
Response format
Section titled “Response format”Success
Section titled “Success”{ "help": "https://your-kukan-site/api/3/action/help_show?name=package_show", "success": true, "result": { ... }}Failure
Section titled “Failure”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" }}Endpoints
Section titled “Endpoints”| 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.
Response shapes
Section titled “Response shapes”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).
Custom fields (extras)
Section titled “Custom fields (extras)”- 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
extrasis spread onto the resource’s top level. There is no nestedextras
Resources
Section titled “Resources”- The
urlof 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.sectionis a reservedextraskey- 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
Where it differs from CKAN
Section titled “Where it differs from CKAN”- Organization and category members (
users) are not returned. KUKAN does not publish who belongs to them; for the same reason there is nomember_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_objectare always empty, as aregroupsandtagsinorganization_show/group_show;num_followersis 0 - Draft datasets do not appear (
package_showanswers 404) until they are published
package_search
Section titled “package_search”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 typetextand match words, so KUKAN may return fewer results. Numbers and arrays are handled as in the REST API fqsplits field from value at:, so a key containing:or whitespace cannot be written there. Use the REST API’sextras_<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 answer409 - Solr syntax in a value answers
409rather 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.
Facets
Section titled “Facets”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.
Counts and paging
Section titled “Counts and paging”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.
Examples
Section titled “Examples”ckanapi (Python)
Section titled “ckanapi (Python)”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 detailcurl "https://your-kukan-site/api/3/action/package_show?id=population-stats-2024"Limitations
Section titled “Limitations”- Read-only. Write actions (
package_createand so on) are not provided; create and edit datasets through the REST API (/api/v1/*) - Actions not listed above, such as
status_show, answer400 - Tag vocabularies are not supported:
vocabulary_idontag_list, andinclude_datasetsontag_showfor a vocabulary tag, answer409