/
Discover the API from the listener root. Available while locked.
Request
Response · 200
Example
curl 'http://localhost:9876/'Errors: Standard routing errors only.
Shelf HTTP API v1
The complete contract for automating a running Shelf desktop app over local HTTP.
http://localhost:9876/api/v1Open Settings → API server, enable it, choose the bind interface and port, then save.
BASE='http://localhost:9876/api/v1'
curl "$BASE/health" should return {"status":"ok","version":"v1","locked":false}.
The API uses plain HTTP, has no API key or token, and does not enable browser CORS. Keep it bound to localhost unless every device and user that can reach it is trusted.
camelCase. JSON request bodies require content-type: application/json.200 OK.AppSnapshot.null.423 Locked while Shelf is locked.Used by bookmark create and replace requests.
| Property | Type | Required | Behavior |
|---|---|---|---|
id | string | null | No | Ignored by HTTP create/replace routing. |
url | string | Yes | Normalized; only HTTP and HTTPS. |
title | string | null | No | URL domain when absent or blank. |
description | string | null | No | Blank becomes null. |
imageUrl, faviconUrl | string | null | No | Page image and favicon URLs. |
siteName, author, publishedAt | string | null | No | Optional source metadata. |
tags | string[] | No | Defaults to []; normalized and deduplicated. |
collectionId | string | null | No | Unknown IDs become null. |
favorite, archived | boolean | No | Each defaults to false. |
notes | string | null | No | Blank becomes null. |
archiveMode | "none" | "html" | No | Defaults to "none"; html queues capture. |
fetchMetadata | boolean | No | Create only; defaults to false. |
metadataBehavior | "add-new-only" | "override" | No | Create only; defaults to add-new-only. |
Returned by the bookmark list endpoint.
Discover the API from the listener root. Available while locked.
curl 'http://localhost:9876/'Errors: Standard routing errors only.
Return the same discovery document from the versioned base path.
curl "$BASE"Errors: Standard routing errors only.
Check the listener and current app-lock state. Available while locked.
curl "$BASE/health"Errors: Standard routing errors only.
Return the complete library, saved queries, tag counts, storage paths, archive path, and pending archive queue.
curl "$BASE/snapshot"Errors: 423 Shelf is locked.
List bookmarks, including archived bookmarks, using zero-based offset pagination. Shelf’s desktop filter row is evaluated locally against its current snapshot; these controls are not additional query properties on this endpoint.
| Desktop filter | Bookmark data and behavior |
|---|---|
| Date added | Compare the local calendar date derived from createdAt with an inclusive From/To range. Either boundary may be omitted. |
| Domain | Match domain against any selected domain. The desktop multi-select is searchable; multiple selections use OR. |
| Has saved copy | Require archive.htmlAvailable to be true. |
| Favorite | Require favorite to be true. |
| Never opened | Require openCount to be 0 and lastOpenedAt to be null. |
| Has notes | Require non-empty notes. |
| No tags | Require an empty tags array. |
GET /api/v1/snapshot for a complete client-side filter pass, or paginate through this endpoint before filtering.total is the complete count at request time, independent of offset and limit. Request the next page with offset + limit while that offset is less than total. An offset at or beyond total returns an empty bookmarks array. Pages may shift if bookmarks change between requests; restart for a consistent traversal.curl "$BASE/bookmarks?offset=0&limit=100"Errors: 400 malformed offset/limit or limit outside 1–1,000; 423 Shelf is locked.
Create a bookmark. Metadata is fetched only when requested; HTML mode queues background capture.
curl -X POST "$BASE/bookmarks" \
-H 'content-type: application/json' \
-d '{
"url":"https://example.com/article#intro",
"tags":["reading","example"],
"favorite":true,
"fetchMetadata":true,
"metadataBehavior":"add-new-only",
"archiveMode":"none"
}'Errors: 400 invalid input; 409 duplicate URL; 423 locked; 502 requested metadata fetch failed.
Atomically create 1–1,000 bookmarks. Validation and requested metadata fetches complete before anything is inserted.
curl -X POST "$BASE/bookmarks/batch" \
-H 'content-type: application/json' \
-d '{"bookmarks":[
{"url":"https://example.com/a","tags":["imported"]},
{"url":"https://example.org/b","fetchMetadata":true,
"metadataBehavior":"override"}
]}'Errors: 400 empty/oversized batch or invalid item; 409 duplicate existing/in-batch URL; 423 locked; 502 any requested metadata fetch failed. No items are inserted on failure.
Return one bookmark.
curl "$BASE/bookmarks/$ID"Errors: 404 bookmark missing; 423 locked.
Replace all editable fields. Omitted defaulted fields reset to their defaults; send the complete desired state.
curl -X PUT "$BASE/bookmarks/$ID" \
-H 'content-type: application/json' \
-d '{
"url":"https://example.com/article",
"title":"Updated article",
"tags":["reading","updated"],
"collectionId":null,
"favorite":true,
"archived":false,
"notes":"Review later",
"archiveMode":"html"
}'Errors: 400 invalid input; 404 missing; 409 duplicate URL; 423 locked.
Delete the bookmark, pending archive work, and saved-copy folder.
curl -X DELETE "$BASE/bookmarks/$ID"Errors: 404 missing; 423 locked.
Update either or both status flags. Omitted flags remain unchanged.
curl -X PATCH "$BASE/bookmarks/$ID/status" \
-H 'content-type: application/json' \
-d '{"favorite":true,"archived":false}'Errors: 404 missing; 423 locked.
Increment openCount and set lastOpenedAt. Does not open a browser.
curl -X POST "$BASE/bookmarks/$ID/opened"Errors: 404 missing; 423 locked.
Fetch and parse metadata without changing the library. Available while locked.
curl -X POST "$BASE/metadata" \
-H 'content-type: application/json' \
-d '{"url":"https://example.com/article"}'Errors: 400 invalid URL/page or response over 3 MB; 502 mapped fetch/timeout failure.
Refresh stored URL and metadata. Tags, collection, flags, and notes remain unchanged.
curl -X POST "$BASE/bookmarks/$ID/metadata"Errors: 400 invalid/concurrently changed data or response over 3 MB; 404 missing; 409 final URL duplicates another bookmark; 423 locked; 502 fetch failure.
Set archiveMode: "html" during create/replace to enable capture. The PUT route refreshes an enabled copy; DELETE removes and disables it.
Capture or refresh saved HTML. A JSON body is required; mode defaults to html when omitted.
curl -X PUT "$BASE/bookmarks/$ID/archive" \
-H 'content-type: application/json' \
-d '{"mode":"html"}'Errors: 400 capture/filesystem/page failure; 404 missing; 423 locked; 502 fetch failure.
Remove the saved-copy folder, clear pending work, and reset mode to none.
curl -X DELETE "$BASE/bookmarks/$ID/archive"Errors: 400 filesystem failure; 404 missing; 423 locked.
Return saved raw HTML and the captured URL used as its render base.
curl "$BASE/bookmarks/$ID/archive/html"Errors: 400 no copy configured; 404 bookmark/copy missing; 423 locked.
Write saved raw HTML to a path on the Shelf host.
curl -X POST "$BASE/bookmarks/$ID/archive/export" \
-H 'content-type: application/json' \
-d '{"path":"/Users/example/Exports/page.html"}'Errors: 400 no configured copy/filesystem failure; 404 bookmark/copy missing; 423 locked.
Search saved-copy text using exact, prefix, typo-tolerant, and subsequence scoring. Blank queries return [].
curl --get "$BASE/archives/search" \
--data-urlencode 'q=rust archive'Errors: 400 archive read failure; 423 locked.
Process each currently queued job at most once. Retryable failures remain queued.
curl -X POST "$BASE/archives/process-pending"Errors: 400 queue/state failure; 423 locked. Individual capture failures stay queued instead of failing the request.
Saved queries store search text, not bookmark membership or cached result IDs. Selecting one reruns the existing search against the latest bookmark metadata and saved-page text.
List saved queries in creation order.
curl "$BASE/saved-queries"Errors: 423 locked.
Save a named query. Names are unique without regard to case; query text must use supported Boolean operators, non-empty terms, and balanced parentheses.
curl -X POST "$BASE/saved-queries" \
-H 'content-type: application/json' \
-d '{"name":"Rust tutorials","query":"#rust AND tutorial -video"}'Errors: 400 invalid input; 409 duplicate name; 423 locked.
Replace a saved query’s name and query text. The path ID is authoritative.
curl -X PUT "$BASE/saved-queries/$SAVED_QUERY_ID" \
-H 'content-type: application/json' \
-d '{"name":"Rust learning","query":"#rust AND (tutorial OR guide) -video"}'Errors: 400 invalid input; 404 missing; 409 duplicate name; 423 locked.
Delete a saved query without changing any bookmarks.
curl -X DELETE "$BASE/saved-queries/$SAVED_QUERY_ID"Errors: 404 missing; 423 locked.
List collections.
curl "$BASE/collections"Errors: 423 locked.
Create a collection. Blank/omitted color defaults to #e87752.
curl -X POST "$BASE/collections" \
-H 'content-type: application/json' \
-d '{"name":"Reading","color":"#e46f4b"}'Errors: 400 missing/blank name; 409 duplicate case-insensitive name; 423 locked.
Replace a collection’s name and color. The path ID wins over any body ID.
curl -X PUT "$BASE/collections/$COLLECTION_ID" \
-H 'content-type: application/json' \
-d '{"name":"Long reads","color":"#788b6b"}'Errors: 400 invalid input; 404 missing; 409 duplicate name; 423 locked.
Delete a collection and set collectionId to null on affected bookmarks.
curl -X DELETE "$BASE/collections/$COLLECTION_ID"Errors: 404 missing; 423 locked.
Return app-lock status. Available while locked.
curl "$BASE/security"Errors: Standard routing errors only.
Enable, change, or remove the password and set the inactivity timeout. Passwords must be 8–1,024 Unicode characters; timeout must be 1–1,440 minutes.
curl -X PUT "$BASE/security" \
-H 'content-type: application/json' \
-d '{
"newPassword":"correct horse battery staple",
"timeoutMinutes":15,
"removePassword":false
}'currentPassword. For removal, set removePassword: true. To change only the timeout, omit newPassword.Errors: 400 invalid timeout/password/current credentials/removal request; 423 existing lock is currently locked.
Lock Shelf immediately when a password exists. Available while locked.
curl -X POST "$BASE/security/lock"Errors: Standard routing errors only.
Unlock Shelf. With no configured password, returns the current snapshot. Available while locked.
curl -X POST "$BASE/security/unlock" \
-H 'content-type: application/json' \
-d '{"password":"correct horse battery staple"}'Errors: 400 incorrect password or invalid stored password settings.
These routes use paths on the Shelf host and never open native file dialogs.
Copy the SQLite library and archives to a new location, activate that database, and persist the preference. Directory must be absolute; a .sqlite3 extension is added when absent.
curl -X PUT "$BASE/storage" \
-H 'content-type: application/json' \
-d '{
"directory":"/Users/example/Documents/Shelf",
"fileName":"bookmarks.sqlite3",
"overwrite":false
}'Errors: 400 invalid path/name or copy/write failure; 409 target exists without overwrite; 423 locked.
Import a native Shelf CSV from a host path. The complete file is validated before mutation; invalid rows are skipped.
curl -X POST "$BASE/import/csv" \
-H 'content-type: application/json' \
-d '{"path":"/Users/example/Imports/bookmarks.csv"}'url/link/uri, title/name, collection/folder/group, and tags/labels/keywords. Raindrop source selection is desktop-only.Errors: 400 file/CSV parsing failure; 423 locked.
Export all bookmarks to a CSV file on the host. Collections use names and tags are joined with |.
curl -X POST "$BASE/export/csv" \
-H 'content-type: application/json' \
-d '{"path":"/Users/example/Exports/shelf.csv"}'Errors: 400 host file/write/CSV failure; 423 locked.
Shelf application errors use this JSON shape:
{ "error": "That bookmark no longer exists" }| Status | Meaning |
|---|---|
200 | Request completed successfully. |
400 | Validation, credentials, URL, storage, CSV, archive, or general operation failure; also malformed JSON. |
404 | Resource or route not found. |
409 | Duplicate bookmark URL, collection name, saved-query name, or existing storage target. |
413 | Request body exceeded the framework limit. |
415 | JSON body sent without application/json. |
422 | JSON properties or types do not match the request schema. |
423 | Protected library data was requested while Shelf was locked. |
502 | Mapped remote-page fetch or timeout failure. |
Framework-generated routing, unsupported method, query, or JSON-extraction failures may be plain text. Unsupported methods normally return 405 Method Not Allowed.
pendingArchives.