Scope: the Keepeek REST API ("Kpk API") exposed by every Keepeek DAM instance, plus the Keepicker web component. Papirfly platform APIs (Keepeek's parent group) are out of scope.
At a glance
https://{tenant}.keepeek.com/api (vendor examples use {KeepeekInstance}) S4 S11. Entry points below /api: /api/dam (authenticated DAM), /api/public (anonymous), /api/share/{tokenId} (share-token access), /api/stat (statistics), /api/geo (geocoding), /api/recognition (face recognition) S2 S41. Download shortcut outside /api: /download-express S26. Custom instance domains: Unknown.Accept S12. Features are tagged with the Keepeek release that introduced them (4.0.0 → 4.124.0 in the docs) S3; the doc instance runs release 4.146.0 S41. No deprecation notice found.exp claim; the vendor sample uses 60 s). No refresh flow — the integrator re-signs tokens S6. Maximum accepted lifetime: Not public.login claim, with that user's group rights (folder rights, statuses, ABAC) S5 S6 S48. Resources expose the caller's permission (e.g. READ/WRITE/ADMIN) S2 S32. Optional header X-KPK-CLIENT-ID checks the user's access to a given user interface S5./api/doc/swagger.json, rendered at test-doc.keepeek.com/api/doc S1 S2 S8; archived copy on DAM Wiki: API spec downloads. Resource-by-resource guide in the help center "Keepeek - API" section S3. Postman sample attached to a help article S37 (not copied — see specs/README).X-Gravitee-* headers) but no rate-limit headers were returned S41. Documented functional caps instead: see Limits & quirks.updateDate/importDate ranges S16. An event-messaging integration (Solace) is marketed without technical detail S100.Authentication
Credentials S4
- An administrator creates a regular Keepeek user for the integration: Settings → Users → Create a user. There is no separate "API key" object; the API user is a normal user whose group rights bound what the integration can see and do S4 S48.
- Check it with GET
https://{tenant}.keepeek.com/api/damandAuthorization: Basic base64(login:password)— 200 means OK S4. Without credentials/api/damreturns401 {"code":"security-notAuthorized"}S41.
JWT ("OAuth 2 / Token JWT") S5 S6
| Aspect | Detail |
|---|---|
| Activation | Needs platform configuration by the customer's Keepeek project manager; a secret is shared between Keepeek and the customer |
| Signing | HMAC only (RSA not supported), minimum SHA-256; the shared key must be alphanumeric only |
| Claims (vendor sample) | client_id (instance identifier), login (Keepeek login of the user to impersonate), iat, exp |
| Sending | Authorization: Bearer <jwt> on every call; Keepeek validates it with the same key |
| Errors | 401 = incomplete/invalid/expired authorization, including an expired client secret requiring renewal S7 |
This is a self-issued, shared-secret JWT scheme, not an OAuth authorization server flow: there is no token endpoint, no authorization code and no refresh token in the API documentation S6.
Identity provider (UI login, Keepicker) — each instance publishes GET/api/public/iam-configuration pointing to a Keycloak realm on auth.keepeek.com (OIDC discovery + SAML IdP descriptor), and GET/api/public/sso-configuration (ssoEnabled, googleOAuthEnabled) S41 S42. The Keepicker component authenticates through a Keycloak client (keycloak-client-id, optional keycloak-idp for an SSO button) S43. Whether third-party back-ends may call /api/dam with Keycloak-issued access tokens is Unknown Unverified (not documented; the realm's discovery document lists generic Keycloak grants that say nothing about API acceptance).
Access-control errors specific to UIs S9: 401 error.account.to_be_validated, user-need-registration-{hash} (points to a registration form), user-access-forbidden-for-ui.
Core resources
Paths relative to https://{tenant}.keepeek.com. The spec has 266 paths / 378 operations (227 GET, 65 POST, 60 DELETE, 26 PUT) S2. Keepeek asks clients to follow _links rather than build URIs, as URI layout may change S11. Minimum release per feature from the API guide S3.
| Resource | Endpoints | Notes |
|---|---|---|
| Entry points | GET/api, GET/api/dam, GET/api/public, GET/api/stat, GET/api/geo, GET/api/recognition | /api returns version, release and links; unauthenticated S11 S41 |
| Media (assets) | GET/POST/api/dam/medias; GET/PUT/DELETE/api/dam/medias/{mediaId} | GET/medias lists only Published, readable media; create from an uploaded file (from link) and/or a form (kpk:form); embedAllMetadata, embeddedFolders flags S2 S20 |
| Metadata values | GET/api/dam/medias/{id}/metadatas, GET/PUT…/metadatas/{metadataId} | One field per PUT; thesaurus values passed as [{id,title}] S20 |
| Metadata schema (forms / fields) | GET/api/dam/forms, /forms/{formId}, /forms/{formId}/fields[/{fieldId}], /forms/lookups[/{id}], /forms/{formId}/extensions; GET/api/dam/sheet-fields[/{id}] | Read-only in the API (no field creation) S2 |
| Thesaurus (controlled vocabularies) | GET/api/dam/thesauruses[/{id}], PUT/thesauruses/{id}; GET/api/dam/thesaurus-tree[/{id}], POST/thesaurus-tree/{id}/children, DELETE/thesaurus-tree/{id} | Hierarchical, multilingual (titles, descriptions, synonyms per locale); terms ≤ 100 characters S36 |
| Statuses & labels | GET/api/dam/medias/custom-statuses[/{id}]; GET/api/dam/medias/labels[/{id}]; GET/POST/medias/{id}/labels, DELETE…/labels/{labelId} | Status change goes through bulk (kpk:custom-status link) S2 S23 |
| Search | GET/api/dam/search/media, /search/advanced…, /search/folder, /search/basket, /search/board, /search/user, /search/group, /search/trash, /search/autocomplete, /search/facet-fields[/{id}] | See Search & querying S2 S16 S18 |
| Folders (classification plan) | GET/POST/api/dam/folder-tree, GET/DELETE/folder-tree/{nodeId}, GET/POST…/children, PUT…/order-children; GET/api/dam/folders[/{id}], PUT/DELETE/folders/{id}; GET/POST/DELETE/folders/{id}/medias[/{mediaId}]; …/responsible-users | Folder rights levels incl. ADMIN (4.61.0+); flat folder list may return empty above an unstated size S35 |
| Uploads | POST/api/dam/files, GET/PUT/api/dam/files/{fileId} | Temporary file descriptor, then create media S21 |
| Versions | GET/POST/api/dam/medias/{id}/file-versions, GET/PUT/DELETE…/file-versions/{versionId} | Replace/restore original file S2 |
| Renditions & links | GET/medias/{id}/permalinks[/{id}], /embed, /player; thumbnail links kpk:medium/large/xlarge/preview/whr in media JSON | See Uploads & binaries S2 S27 |
| Downloads & exports | POST/api/dam/users/{userId}/selections/{selectionId}/exports, GET/users/{userId}/exports/{id}; POST/GET/DELETE/api/dam/download-requests[/{id}] (4.54.0+); GET/download-express?mid=… | Export formats per media type via …/media-types/{type}/export-formats S25 S26 S32 |
| Selections & bulk | POST/api/dam/users/{userId}/selections, POST/DELETE…/medias-collection, GET…/bulks/fields, POST…/bulks; /api/dam/bulk-jobs | Bulk operation types UPDATE, AUTOTAG S2 S23 S24 S38 |
| Baskets (collections) | GET/POST/api/dam/baskets, GET/PUT/DELETE/baskets/{id}, …/medias, …/medias-collection, …/tags, …/share, …/share/public, …/share/tokens, …/terms-of-use, …/users | Permission READ/WRITE/ADMIN; as-admin=true for admins S2 S16 |
| Boards (kanban) | GET/POST/api/dam/boards, GET/PUT/DELETE/boards/{id}, …/columns[/{id}]/medias | S2 |
| Workflows | GET/api/dam/workflows[/{id}], GET/POST/DELETE/workflows/{id}/medias | Read + membership only; no step processing/approval endpoint in the spec S2 |
| Validation requests | /api/dam/classification-validation-requests (create/read; no delete); /api/dam/download-validation-requests; public variant /api/public/download-validation-requests (disabled by default) | S2 S3 |
| Sharing | Media/basket share tokens …/share/tokens; share templates /api/dam/share/templates (+ allowed domains); anonymous token access /api/share/{tokenId}/… (shared media, basket, search, selections, exports, download requests) | Tokens carry exportDefinition (none/BD/HD), activation/deactivation dates S28 S29 S30 |
| Rights / terms of use | GET/medias/{id}/terms-of-use, …/selections/{id}/terms-of-use, …/baskets/{id}/terms-of-use | Exports require acceptTermsOfUse; non-admins must give a usage S32. Rights configuration (ABAC, folder ACLs) not in the API S2 |
| Users & groups | GET/api/dam/users[/{id}], PUT/DELETE/users/{id}, …/assigned-groups, …/additional-attributes, …/authorized-user-interfaces, …/thumbnail-templates; GET/api/dam/groups[/{id}], POST/groups/{id}/users, …/responsible-users | Listing needs manager/admin; new users inactive until assigned group status=VALID; externally authenticated users are delete-only S31 S32 |
| Attachments & relations | …/medias/{id}/attachments (+ upload-link, download-link), …/blobs, …/media-links[/{id}]/medias, …/duplicates | S2 |
| Face recognition | POST/api/recognition/detections; GET/api/dam/faces[/{id}], /api/dam/individuals[/{id}] | Only with the face-recognition option S2 S3 |
| Statistics | POST/api/stat/hits, GET/api/stat/hit-types[/{id}[/schema]], GET/api/stat/search/hit | Documented as not requiring API authentication S33 |
| Scripts | GET/api/dam/scripts[/{id}], GET/POST/api/dam/script-runs[/{id}] | Run server-side scripts configured on the instance (4.50.0+) S34 |
| Admin / config | /api/dam/properties, /api/dam/user-interfaces (PUT), /api/dam/file-resources/{type}/{id} (admin), /api/dam/services/{id}/signatures, DELETE/api/dam/cache/{id}, /api/dam/wikis | S2 S36 |
| Semantic embeddings | POST/api/dam/medias/{id}/embeddings | Present in the spec without description — purpose Unknown S2 Unverified |
Not exposed in the documented API: webhooks/event subscriptions, metadata-schema writes (forms and fields are read-only), rights configuration (folder ACLs, ABAC rules, statuses definitions), workflow template/step actions, SSO/IP-filtering settings S2.
Data model
- Media: integer
id; top-level technical properties (title,status,statusUpdateDate,permission,downloadLevel,width,height,resolution,orientation,mediaType(MIME),fileSize,totalSize,originalFileName,creationDate,updateDate,importDate,trashDate,duration,pageCount,colorSpace,latitude/longitude/address,duplicationStatus,thumbnailGenerationStatus,statViews,statDownloads,checkSums{md5,crc32},formType,pointOfInterest,fileVersionCount…) S2. - Enumerations S2:
status∈ IMPORT, BASKET, DRAFT, PUBLISHED, ARCHIVED, TRASH (plus instance-defined custom statuses);permission∈ READ, WRITE, PUBLISH, DELETE, ADMIN;downloadLevel∈ NONE, BD (low def), HD;formType∈ PICTURE, DOCUMENT, VIDEO, SOUND, RICH_MEDIA, OTHER. - Metadata:
_embedded.metadata[]items{id, name, value, _links.kpk:field};idis the field's internal name (e.g.title,legend); empty fields omitted unlessembedAllMetadata=trueS13 S20. Localized fields are addressed with a locale suffix in search (e.g.title.FR) S19; content language negotiated withAccept-Language/ returned inContent-Language; writes target languages viaContent-LanguageS15 S23. - Forms ("fiches"): each media has a metadata form (metamodel); field types
TEXT,TEXT_AREA,HTML,HTML_AREA,DATE,DATETIME,NUMBER,EMAIL,URL,CHECK_BOX,SIMPLE_LIST,MULTIPLE_LIST,PERSON_LIST,THESAURUS,GEO,FIELD_SET; field flagsmandatory,indexed,indexSuggest,forceSelection,editable,visibleS2 S13 S23. - Taxonomy: hierarchical, multilingual thesauri attached to "microthesaurus" fields; nodes have numeric ids S16 S36. Folder tree (classification plan) with numeric folder ids; a media can be in several folders S11 S13. Labels, basket tags and statuses complete the taxonomy S2.
- HAL conventions: CURIE prefix
kpk(/api/doc/rels/{rel}); single embedded items are returned as an object unlessforceArrays=true(4.20.0+) S11 S14. - Ephemeral resources: uploaded files, selections, exports, bulk jobs and API-created advanced searches expire and may be purged — do not persist their URIs S21 S19 S24 S32.
Search & querying
S16 unless noted.
| Aspect | Behaviour |
|---|---|
| Full text | q= over indexed fields, Lucene query-parser syntax (+, -, phrases; default OR); lq=true enables raw Lucene queries such as (title.FR:a OR title.FR:b) S2 |
| Field queries | f=field:value (repeatable, AND), wildcards (title:li*), negation (title:-x), ranges [a TO b] on dates (RFC 3339 or epoch), numbers, ids |
| Filters | fq= restricts the scope without scoring: folderId:10 subtree, folderPath, formId, customStatusId, labelId, thesaurus:{field}:{node} subtree, basketId, workflowId, boardId/columnId, selectionId, associations:(…) types:(…) (≤ 1000 ids), width/height ranges, durationInSecond, facet: entries |
| Facets | Returned by default as configured in the back office; facet-fields= overrides (4.75.0+), facet-fields=[] suppresses them; facet-values-number ≤ 150 (400 otherwise) S16 S17 |
| Sorting | sort=field [desc], incl. statViews, statDownloads, fileSize, totalSize |
| Projection | fields= partial-response grammar, e.g. fields=id,title,_links{kpk:preview} or _embedded{metadata.byId(title,legend)} S13 |
| Page size | size default 20; maximum Not public S2 |
| Deep pagination | Limit Not public; collection resources (/medias, /folders) may return empty above an unstated total S20 S35 Unverified — partition by date ranges or folders |
| Advanced search | Build criteria from GET/api/dam/search/advanced/criterias (modifiers such as EQUALS_ONE, CONTAINS_ONE/ALL/NONE, EMPTY, NOT_EMPTY, date presets, FROM_TO); POST/api/dam/search/advanced returns a TEMPORARY search (expiring), then GET…/{id}/media; saved searches can be shared (4.84.0+) S18 S19 |
| Other helpers | notFoundReference=true lists values that matched nothing (reference-list search); f=duplicationStatus:DUPLICATES_FOUND; autocomplete on folders/terms S16 S2 |
| Delta queries | f=updateDate:[… TO …] / importDate ranges S16 |
Uploads & binaries
- POST
/api/dam/fileswith the raw bytes as body and headersContent-Type,Content-Length, andX-KPK-FileName(ISO-8859-1) orX-KPK-Base64FileName(recommended) →201+ temporary file descriptor (id,expirationDate,_links.self). - Large files: POST
/api/dam/fileswithContent-Length: 0,X-KPK-Upload-Content-Type,X-KPK-Upload-Content-Length; thenPUTchunks to the descriptor withContent-Range(202per chunk,200on the last;nextExpectedRangesin the body;416on a bad range;GETthe descriptor to resume, 4.31.0+). - Create the media: POST
/api/dam/folders/{folderId}/medias(4.16.0+) or POST/api/dam/mediaswith{"_links":{"from":{"href":"…/api/dam/files/{id}"}}}, optionally akpk:formlink →201+LocationS20.
| Limit | Value | |
|---|---|---|
| Single-request upload | Max 50 MB; recommended ≤ 20 MB | S21 |
| Chunk size | 20 MB max recommended | S22 |
| File size | 20 GB officially supported | S22 |
| Parallel uploads | 200 simultaneous | S22 |
Downloads & renditions
- Thumbnail relations on each media:
kpk:medium(112 px),kpk:large(224 px),kpk:xlarge(448 px),preview(607 px),kpk:whr(web hi-res, 1200 px) — default sizes, configurable S27. - Permalinks (
/medias/{id}/permalinks) are generated at import for configured formats S2 S59. - Express download: GET
/download-express?mid={id}&mid={id}(Basic or JWT) — one id returns the file, several return a ZIP S26. - Formatted exports: selection → POST
…/exportswith per-media-typekpk:*-export-formatlinks;acceptTermsOfUserequired S32 S39. Asynchronous download requests (/api/dam/download-requests, statusPROCESSING/SUCCESS/ERROR, target ZIP URL; also contact sheets, 4.68.0+) S25 S2. - Dynamic resizing service "Dynameek" (URLs of the form
kpk://dynameek-kpk/{size}/…returned by Keepicker) S43 S67; its public URL grammar is Not public.
Events & webhooks
- No webhook or event-subscription mechanism is documented in the API guide or present in the Swagger spec S2 S3.
- Webhook-style notifications can be implemented with custom scripts on the instance (project-specific, not a self-service API feature) S115.
- The only notification resource is PUT
/api/public/notifications/{email}to disable e-mail notifications S35. - Integration alternatives: poll
search/mediaonupdateDateranges S16; server-side scripts that can be triggered by API (/api/dam/script-runs) S34; statistics hit search (/api/stat/search/hit, max 1,000,000 results, ≤ 10 parallel searches) S33. Keepeek also sells custom "synchronization scripts" as integration services S88.
Limits & quirks
| Topic | Detail | |
|---|---|---|
| Rate limiting | Not documented; behaviour under load Unknown | S3 S41 |
| Bulk update | Selection ≤ 10,000 media (more via support); synchronous; FAQ says ≤ 300 media per bulk call; fields and statuses only (not folders); modes REPLACE (default), APPEND, CLEAR, REMOVE (4.124.0+) | S7 S23 Unverified the 300 cap appears only in the FAQ |
| Bulk jobs | Temporary jobs with duration 30–600 s (default 30); 4.33.0+ and need a licence allowing bulk jobs from API | S24 |
| Publishing rules | Moving media to a published status fails for the whole selection if mandatory fields are empty (unless filled in the same request) | S23 |
| Export limits | FAQ: exports generally ≤ 200 items; direct (synchronous) exports ≤ 10 items, above which the API returns 500 and an e-mail is sent instead. Help center: direct custom-format downloads limited to 50 items by default (DIRECT_EXPORT_NUMBER_LIMIT, changed by support on cloud instances). The current default is 50 S115; the FAQ's 10-item figure appears outdated | S7 S60 S115 |
Status filter on /medias | GET/api/dam/medias only returns Published media — use search for other statuses | S2 |
| Search path typo | Best-practice article uses /api/dam/media/search; the spec and API guide use /api/dam/search/media | S2 S17 Unverified |
| Single vs array | Single embedded element returned as object unless forceArrays=true | S14 |
| Mixed collaborative filters | Combining basket/workflow/board filters in one query → 400 | S16 |
| Filenames | Use X-KPK-Base64FileName to avoid encoding issues | S22 |
| JWT key | Shared key must be alphanumeric; RSA keys unsupported | S6 |
| Performance guidance | Keep responses small: paginate, use fields=, drop facets with facet-fields=[], prefer precise terms, sort server-side | S17 S7 |
| Error bodies | JSON {status, code} with string codes (e.g. security-notAuthorized, share-token-not-found) | S41 S28 |
| URI stability | Do not hard-code URI patterns; follow HAL links | S11 |
SDKs, tools & code samples
| Library / tool | Language | Maintainer | Status | |
|---|---|---|---|---|
| Server SDK | — | — | None published (no Keepeek-owned GitHub organization found — github.com/keePeek is a personal user account with no evident vendor link; no official PyPI/Packagist/NuGet package) | S44 |
Keepicker (@keepeek/keepicker-react) | JavaScript web component (<kpk-keepicker>) | Keepeek | Active: v1.27.0, 153 versions since 2023-08-03; events kpk-insert, kpk-insert-link; Drupal and React examples | S43 |
| Postman collection sample | — | Keepeek | Attached to a help article; not mirrored here | S37 |
| Java JWT sample | Java (auth0 java-jwt) | Keepeek | Snippet in the JWT article | S6 |
keepeek (PyPI 1.0.0) | Python | Unknown (placeholder metadata) | Unverified origin — do not treat as official | S44 Unverified |
media_keepeekdam | PHP (Drupal) | Community | Last push 2025-04-24 | S108 |
keepeek-360-phototheque-connecteur | PHP (WordPress) | Keepeek | Closed on WordPress.org 2023-07-06 | S107 |
Changelog of the API
| Date | Change | |
|---|---|---|
| 2026-09-30 | Doc instance reports release 4.146.0 (observation, not an announcement) | S41 |
| 2026-04-02 | "FAQ API" article published (auth recommendation, bulk/export limits) | S7 |
| 2026-01-28 | Download-express-via-API article published | S26 |
| 2025-12-17 | API guide migrated to the Papirfly help center (all articles re-created that day) | S3 |
| 2025-08-13 | Release 4.127: API fix on language display of filter names | S40 |
| Unknown (4.124.0) | Bulk REMOVE mode | S23 |
| Unknown (4.84.0) | Shared advanced searches in API responses; share options resource | S18 |
| Unknown (4.28.0) | JWT ("OAuth 2 / Token JWT") authentication | S6 |
| Unknown (4.0.0) | Hypermedia API v1 baseline (media, folders, uploads) | S3 |
Release dates for 4.x versions are Not public (no public release-notes archive beyond S40).
| File | Format | Size | Download |
|---|---|---|---|
keepeek_swagger_rest-api-v1.json | Swagger / OpenAPI | 514 KB | Download |