Skip to content

REST API

Base path: /api/v1

Auth column legend:

  • Public — no authentication required
  • Auth — login or API token required
  • Org — organization member / editor / admin
  • sysadmin — system administrator only
MethodPathDescriptionAuth
GET/packagesList datasets (with search and filters)Public
POST/packagesCreate dataset (include resources array to create resources simultaneously)Org editor
POST/packages/draftsCreate draft dataset (every field optional)Auth
GET/packages/{nameOrId}Get dataset detailsPublic
PUT/packages/{nameOrId}Update dataset (active: full replacement / draft: partial update; resources not included)Org editor
DELETE/packages/{nameOrId}Delete dataset (active: soft delete / draft: immediate permanent delete)Org editor
POST/packages/{nameOrId}/publishPublish a draft (draftactive)Org editor
POST/packages/{nameOrId}/restoreRestore soft-deleted datasetsysadmin
POST/packages/{nameOrId}/purgePermanently delete soft-deleted datasetOrg admin
GET/packages/{id}/resourcesList resourcesPublic
POST/packages/{id}/resourcesAdd resourceOrg editor
PUT/packages/{id}/resources/reorderReorder resourcesOrg editor
ParameterDescription
qSearch query (keywords or natural language; hybrid search)
semanticfalse limits results to keyword matches only (default: true)
offset / limitPagination (max 100)
organization[]Filter by organization
groups[]Filter by category
tags[]Filter by tag
res_format[]Filter by resource format (e.g., CSV)
license_id[]Filter by license
stateactive / deleted / draft (draft requires auth; see below)
my_orgLimit to user’s organizations
privateInclude private datasets
sort_byupdated / created / name
sort_orderasc / desc
include_facetsInclude facet information
Terminal window
curl "https://your-kukan-site/api/v1/packages?q=population&organization[]=stat-bureau&limit=10"

Datasets can be created as drafts and published once the publish requirements are met. Drafts never appear in search or listings (including the CKAN-compatible API) until published.

CreatePOST /packages/drafts (auth required):

Every field is optional. When name is omitted, a placeholder of the form untitled-xxxxxxxx is auto-generated (a draft cannot be published with the placeholder). Specifying ownerOrg at creation requires editor rights in that organization.

Terminal window
curl -X POST https://your-kukan-site/api/v1/packages/drafts \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{"title": "Work-in-progress dataset"}'

PublishPOST /packages/{nameOrId}/publish:

One-way draftactive transition (there is no way back to draft after publishing). Returns 400 unless all three of the following are set:

  • name set explicitly (the auto-generated placeholder is not accepted)
  • ownerOrg set
  • licenseId set

Permission is the same as dataset update, and editor rights in the ownerOrg organization are re-verified at publish time. Re-publishing an already active package is allowed and re-runs the publish-time search index sync (idempotent — the retry entry point after a failed sync).

ListGET /packages?state=draft (auth required):

Returns only drafts created by the caller plus drafts of organizations where the caller is editor or above. Supported filters: q / name / organization[] / private / sort_by / sort_order / offset / limit. Specifying tags[] / groups[] / res_format[] / license_id[] / creator_user_id / my_org / include_facets returns 400. Rows whose deletion failed partway (deletion incomplete) are included in the listing.

Get, update, delete:

  • Fetch details with GET /packages/{id}?state=draft (auth and draft access required)
  • PUT on a draft is a partial update: only the keys present in the request are applied, and an explicit null clears that field. name: null regenerates the placeholder (back to “unnamed”) and ownerOrg: null clears the organization (both block publishing again). PUT on an active package remains full replacement
  • DELETE on a draft skips the trash and permanently deletes it immediately (irreversible). If a deletion fails partway, re-running the same DELETE completes the permanent deletion
MethodPathDescriptionAuth
GET/resources/countActive resource countPublic
GET/resources/formatsList used formatsPublic
GET/resources/{id}Resource detailsPublic
GET/resources/{id}/textText content (preview-size limited)Public
GET/resources/{id}/downloadDownload file (302 redirect for external URLs)Public
GET/resources/{id}/previewServer-proxied preview (Range supported)Public
GET/resources/{id}/pipeline-statusPipeline job statusPublic
GET/resources/{id}/schemaColumn schema (names, types, row count, numeric stats). CSV/TSV onlyPublic
POST/resources/{id}/queryServer-side DuckDB query (SELECT only). CSV/TSV onlyPublic
POST/resources/{id}/upload-urlIssue presigned upload URLOrg editor
POST/resources/{id}/uploadServer-proxied uploadOrg editor
POST/resources/{id}/upload-completeNotify completion, enqueue pipelineOrg editor
POST/resources/{id}/run-pipelineManually trigger pipelineOrg editor
PUT/resources/{id}Update resource (full replacement; extras is system-managed)Org editor
DELETE/resources/{id}Delete resourceOrg editor

CSV/TSV resources (with a pipeline-generated preview Parquet, ≤50 MB) support column schema retrieval and read-only SQL queries. The MCP get_resource_schema / query_resource tools offer the same capability for AI agents.

Get schemaGET /resources/{id}/schema:

{
"id": "",
"queryable": true,
"schema": {
"rowCount": 1688,
"columns": [
{ "name": "prefecture", "type": "string", "nullable": true, "nullCount": 3 },
{
"name": "weight_limit",
"type": "integer",
"nullable": false,
"nullCount": 0,
"stats": { "min": "600", "max": "1350" }
}
]
}
}

Resources that cannot be queried (unsupported format, not yet processed) return queryable: false, schema: null. type is one of integer / float / boolean / string; stats is present only for numeric columns, and integer min/max are decimal strings.

Query dataPOST /resources/{id}/query:

Terminal window
curl -X POST https://your-kukan-site/api/v1/resources/{id}/query \
-H 'Content-Type: application/json' \
-d '{"sql": "SELECT prefecture AS city, count(*) AS n FROM data GROUP BY 1 ORDER BY n DESC LIMIT 3"}'
{
"id": "",
"columns": ["city", "n"],
"rows": [
{ "city": "Adachi", "n": "117" },
{ "city": "Edogawa", "n": "110" },
{ "city": "Ota", "n": "86" }
],
"rowCount": 3,
"truncated": false,
"elapsedMs": 31
}

Query rules:

  • The data is exposed as a table named data. Double-quote column names that contain spaces or non-ASCII characters.
  • A single SELECT / WITH statement only. Writes and DDL (INSERT / UPDATE / DELETE / CREATE / DROP / ATTACH / COPY / PRAGMA, …) are rejected.
  • Runs in a throwaway in-memory DuckDB instance; all external access (files, URLs, extension loading) is disabled.

Limits and errors:

ItemValue
Max rows10,000 (truncated: true when exceeded)
Max result size5 MB
Execution timeout15 s (408)
Concurrency1 (429 when exceeded)
ErrorCondition
400Invalid SQL / non-SELECT / over length / non-queryable resource
404Resource not found / private without access
408Execution timeout
429Concurrent query limit
MethodPathDescriptionAuth
GET/organizationsList organizationsPublic
POST/organizationsCreate organizationsysadmin
GET/organizations/{nameOrId}Organization detailsPublic
PUT/organizations/{nameOrId}Update organization (full replacement)Org admin
DELETE/organizations/{nameOrId}Soft-delete organizationOrg admin
POST/organizations/{nameOrId}/restoreRestore soft-deleted organizationsysadmin
POST/organizations/{nameOrId}/purgePermanently delete soft-deleted organization (async via SQS)sysadmin
GET/organizations/{nameOrId}/membersList membersOrg member
POST/organizations/{nameOrId}/membersAdd member / change roleOrg admin
DELETE/organizations/{nameOrId}/members/{userId}Remove memberOrg admin

Member roles are one of admin / editor / member.

MethodPathDescriptionAuth
GET/groupsList categoriesPublic
POST/groupsCreate categorysysadmin
GET/groups/{nameOrId}Category detailsPublic
PUT/groups/{nameOrId}Update category (full replacement)Category admin
DELETE/groups/{nameOrId}Delete categoryCategory admin
POST/groups/{nameOrId}/restoreRestore soft-deleted categorysysadmin
POST/groups/{nameOrId}/purgePermanently delete soft-deleted categorysysadmin
GET/groups/{nameOrId}/membersList membersCategory member
POST/groups/{nameOrId}/membersAdd member / change roleCategory admin
DELETE/groups/{nameOrId}/members/{userId}Remove memberCategory admin
MethodPathDescriptionAuth
GET/tagsList tagsPublic
GET/tags/{id}Tag detailsPublic
MethodPathDescriptionAuth
GET/users/meCurrent user infoAuth
GET/users/me/organizationsUser’s organizationsAuth
GET/usersSearch users (for member management)Auth
MethodPathDescriptionAuth
POST/api-tokensCreate tokenAuth
GET/api-tokensList own tokensAuth
DELETE/api-tokens/{id}Revoke tokenAuth

All sysadmin only.

MethodPathDescription
GET/admin/search/statsSearch index statistics (doc count, size, DB counts)
GET/admin/search/doc/{index}/{id}Get index document (index: packages / resources / contents)
GET/admin/search/browse/{index}Browse index documents (pagination, search)
POST/admin/reindex-metadataRebuild search index (async via SQS)
POST/admin/jobs/enqueue-allEnqueue pipeline for all resources
GET/admin/jobs/statsPipeline job statistics
GET/admin/jobsList pipeline jobs
DELETE/admin/dataDelete all data (preserves users)
GET/admin/health/statsHealth check statistics
GET/admin/healthHealth check results
GET/admin/users/statsUser statistics
GET/admin/usersList users
POST/admin/usersCreate user
PATCH/admin/users/{userId}Update user
DELETE/admin/users/{userId}Soft-delete user (revokes sessions/tokens)
POST/admin/users/{userId}/restoreRestore user
POST/admin/users/{userId}/purgePermanently delete user
MethodPathDescriptionAuth
GET/api/healthHealth checkPublic
GET/api/v1/site/settingsSite settings (registration etc.)Public