> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bkmark.it/llms.txt
> Use this file to discover all available pages before exploring further.

# Bookmarks API Reference — Bkmark REST API Endpoints

> Create, read, update, delete, search, and bulk-manage bookmarks via the Bkmark REST API. Covers all bookmark endpoints including trash, import, and export.

The Bookmarks API is the core of Bkmark. You can store any URL as a bookmark, attach tags and groups, mark items as favorites or archived, and manage an entire bookmark library programmatically. All endpoints require a valid Bearer token in the `Authorization` header — see the [Authentication](/api/authentication) guide to obtain one.

<Note>
  All endpoints are relative to the base URL `https://api.bkmark.it/api/v1`. Pagination uses cursor-based navigation: pass the `nextCursor` value from one response as the `cursor` parameter in your next request. When `nextCursor` is `null`, you have reached the last page.
</Note>

***

## GET /api/v1/bookmarks

List bookmarks for the authenticated user. Results are paginated and can be filtered by group, tag, type, source, and read/favorite/archive status.

<ParamField query="cursor" type="string">
  Pagination cursor. Pass the `nextCursor` value from a previous response to fetch the next page.
</ParamField>

<ParamField query="limit" type="number" default="20">
  Number of items to return per page. Accepted range: 1–100.
</ParamField>

<ParamField query="groupId" type="string">
  Filter to bookmarks that belong to this group UUID.
</ParamField>

<ParamField query="tagId" type="string">
  Filter to bookmarks that have this tag UUID applied.
</ParamField>

<ParamField query="search" type="string">
  Full-text search query. Searches across title, description, and URL.
</ParamField>

<ParamField query="isFavorite" type="boolean">
  When `true`, returns only favorited bookmarks.
</ParamField>

<ParamField query="isArchived" type="boolean">
  When `true`, returns only archived bookmarks.
</ParamField>

<ParamField query="isUnread" type="boolean">
  When `true`, returns only bookmarks that have never been marked as read (i.e., `readAt` is `null`).
</ParamField>

<ParamField query="type" type="string">
  Filter by bookmark type. One of: `link`, `note`, `code`, `image`.
</ParamField>

<ParamField query="source" type="string">
  Filter by the source that created the bookmark. One of: `web`, `extension`, `api`, `import`, `slack`.
</ParamField>

<ParamField query="sortField" type="string" default="createdAt">
  Field to sort by. One of: `createdAt`, `title`, `url`.
</ParamField>

<ParamField query="sortDir" type="string" default="desc">
  Sort direction. One of: `asc`, `desc`.
</ParamField>

```bash theme={null}
curl "https://api.bkmark.it/api/v1/bookmarks?limit=10&isFavorite=true&sortField=createdAt&sortDir=desc" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

```json theme={null}
{
  "data": [
    {
      "id": "3f7a1c2e-84b0-4d9e-a3f2-1c2e3f7a84b0",
      "url": "https://www.typescriptlang.org/docs/handbook/2/types-from-types.html",
      "title": "Types from Types — TypeScript Handbook",
      "description": "A deep dive into template literal types, conditional types, and mapped types.",
      "type": "link",
      "source": "extension",
      "isFavorite": true,
      "isArchived": false,
      "readAt": null,
      "tags": [
        { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "typescript", "color": "blue" },
        { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "name": "reading", "color": "amber" }
      ],
      "groups": [
        { "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "name": "Engineering" }
      ],
      "createdAt": "2025-03-15T09:22:11.000Z",
      "updatedAt": "2025-03-15T09:22:11.000Z"
    }
  ],
  "nextCursor": "d4e5f6a7-b8c9-0123-defa-234567890123"
}
```

***

## POST /api/v1/bookmarks

Create a new bookmark. If you omit `title` or `description`, Bkmark automatically fetches and parses the URL to populate them. Tags named in `tagNames` are created on the fly if they don't already exist.

<ParamField body="url" type="string" required>
  The URL to bookmark. Required for `link` and `image` types.
</ParamField>

<ParamField body="title" type="string">
  Title override. If omitted, Bkmark fetches it from the URL's `<title>` tag.
</ParamField>

<ParamField body="description" type="string">
  Short description or note. Auto-populated from the page's meta description if omitted.
</ParamField>

<ParamField body="type" type="string" default="link">
  Bookmark type. One of: `link`, `note`, `code`, `image`.
</ParamField>

<ParamField body="tagNames" type="string[]">
  Array of tag names to apply. Tags that don't exist are created automatically.
</ParamField>

<ParamField body="groupIds" type="string[]">
  Array of group UUIDs to add this bookmark to.
</ParamField>

<ParamField body="source" type="string">
  The originating surface. One of: `web`, `extension`, `api`, `import`, `slack`. Defaults to `api` for programmatic requests.
</ParamField>

<ParamField body="force" type="boolean">
  Set to `true` to bypass duplicate URL detection and create a second bookmark for the same URL.
</ParamField>

<Warning>
  If the URL already exists in the library and `force` is not set, the API returns `409 Conflict` with error code `duplicate_url`. Use `force: true` if you intentionally want a duplicate entry.
</Warning>

```bash theme={null}
curl -X POST "https://api.bkmark.it/api/v1/bookmarks" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://react.dev/learn/thinking-in-react",
    "title": "Thinking in React",
    "description": "The React mental model explained with a practical example.",
    "type": "link",
    "tagNames": ["react", "frontend"],
    "groupIds": ["c3d4e5f6-a7b8-9012-cdef-123456789012"],
    "source": "api"
  }'
```

Returns `201 Created` with the newly created bookmark object.

```json theme={null}
{
  "id": "e5f6a7b8-c9d0-1234-efab-345678901234",
  "url": "https://react.dev/learn/thinking-in-react",
  "title": "Thinking in React",
  "description": "The React mental model explained with a practical example.",
  "type": "link",
  "source": "api",
  "isFavorite": false,
  "isArchived": false,
  "readAt": null,
  "tags": [
    { "id": "f6a7b8c9-d0e1-2345-fabc-456789012345", "name": "react", "color": "cyan" },
    { "id": "a7b8c9d0-e1f2-3456-abcd-567890123456", "name": "frontend", "color": null }
  ],
  "groups": [
    { "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "name": "Engineering" }
  ],
  "createdAt": "2025-04-01T14:05:33.000Z",
  "updatedAt": "2025-04-01T14:05:33.000Z"
}
```

***

## GET /api/v1/bookmarks/:id

Retrieve a single bookmark by its UUID.

<ParamField path="id" type="string" required>
  The UUID of the bookmark to retrieve.
</ParamField>

```bash theme={null}
curl "https://api.bkmark.it/api/v1/bookmarks/e5f6a7b8-c9d0-1234-efab-345678901234" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

```json theme={null}
{
  "id": "e5f6a7b8-c9d0-1234-efab-345678901234",
  "url": "https://react.dev/learn/thinking-in-react",
  "title": "Thinking in React",
  "description": "The React mental model explained with a practical example.",
  "type": "link",
  "source": "api",
  "isFavorite": false,
  "isArchived": false,
  "readAt": null,
  "tags": [
    { "id": "f6a7b8c9-d0e1-2345-fabc-456789012345", "name": "react", "color": "cyan" }
  ],
  "groups": [
    { "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "name": "Engineering" }
  ],
  "createdAt": "2025-04-01T14:05:33.000Z",
  "updatedAt": "2025-04-01T14:05:33.000Z"
}
```

***

## PATCH /api/v1/bookmarks/:id

Update a bookmark's metadata. All body fields are optional — only send what you want to change.

<ParamField path="id" type="string" required>
  The UUID of the bookmark to update.
</ParamField>

<ParamField body="title" type="string">
  New title for the bookmark.
</ParamField>

<ParamField body="description" type="string">
  New description.
</ParamField>

<ParamField body="isFavorite" type="boolean">
  Set to `true` to favorite the bookmark, `false` to unfavorite it.
</ParamField>

<ParamField body="isArchived" type="boolean">
  Set to `true` to archive the bookmark, `false` to unarchive it.
</ParamField>

<ParamField body="readAt" type="string">
  ISO 8601 timestamp to mark the bookmark as read (e.g. `"2025-04-01T15:00:00.000Z"`). Send `null` to mark it as unread again.
</ParamField>

```bash theme={null}
curl -X PATCH "https://api.bkmark.it/api/v1/bookmarks/e5f6a7b8-c9d0-1234-efab-345678901234" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Thinking in React — Official Guide",
    "isFavorite": true,
    "readAt": "2025-04-02T09:00:00.000Z"
  }'
```

Returns `200 OK` with the updated bookmark object.

***

## DELETE /api/v1/bookmarks/:id

Delete a bookmark. By default this is a **soft delete** — the bookmark moves to trash and can be restored. Add `?permanent=true` to bypass trash and delete immediately.

<ParamField path="id" type="string" required>
  The UUID of the bookmark to delete.
</ParamField>

<ParamField query="permanent" type="boolean">
  Set to `true` to permanently delete the bookmark instead of moving it to trash. This action is irreversible.
</ParamField>

```bash theme={null}
# Soft delete (moves to trash)
curl -X DELETE "https://api.bkmark.it/api/v1/bookmarks/e5f6a7b8-c9d0-1234-efab-345678901234" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

# Permanent delete (no recovery)
curl -X DELETE "https://api.bkmark.it/api/v1/bookmarks/e5f6a7b8-c9d0-1234-efab-345678901234?permanent=true" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

Returns `204 No Content` on success.

***

## POST /api/v1/bookmarks/:id/tags

Replace all tags on a bookmark in one call. Any tag names that don't exist are created automatically. Sending an empty array removes all tags from the bookmark.

<ParamField path="id" type="string" required>
  The UUID of the bookmark.
</ParamField>

<ParamField body="tagNames" type="string[]" required>
  Complete list of tag names the bookmark should have after this call. Replaces the existing tag set entirely.
</ParamField>

<Note>
  This is a replace operation, not an append. If the bookmark currently has tags `["dev", "reading"]` and you POST `["ai"]`, the result is only `["ai"]`. To add a tag without removing others, first fetch the current tags and include them in your new list.
</Note>

```bash theme={null}
curl -X POST "https://api.bkmark.it/api/v1/bookmarks/e5f6a7b8-c9d0-1234-efab-345678901234/tags" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{ "tagNames": ["react", "frontend", "reading"] }'
```

Returns `200 OK` with the updated bookmark object.

***

## POST /api/v1/bookmarks/:id/groups

Replace all group assignments for a bookmark. Sending an empty array removes the bookmark from all groups.

<ParamField path="id" type="string" required>
  The UUID of the bookmark.
</ParamField>

<ParamField body="groupIds" type="string[]" required>
  Complete list of group UUIDs the bookmark should belong to after this call. Replaces the existing group assignments entirely.
</ParamField>

```bash theme={null}
curl -X POST "https://api.bkmark.it/api/v1/bookmarks/e5f6a7b8-c9d0-1234-efab-345678901234/groups" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{ "groupIds": ["c3d4e5f6-a7b8-9012-cdef-123456789012", "d4e5f6a7-b8c9-0123-defa-234567890123"] }'
```

Returns `200 OK` with the updated bookmark object.

***

## POST /api/v1/bookmarks/bulk

Apply a single action to up to 500 bookmarks in one request. Use this to batch-tag, move, archive, favorite, or delete bookmarks without looping over individual endpoints.

<ParamField body="bookmarkIds" type="string[]" required>
  Array of bookmark UUIDs to act on. Maximum 500 items.
</ParamField>

<ParamField body="action" type="string" required>
  The operation to perform. See the table below for available actions and their extra fields.
</ParamField>

<ParamField body="tagNames" type="string[]">
  Required when `action` is `tag`. Tag names to add to all selected bookmarks.
</ParamField>

<ParamField body="groupIds" type="string[]">
  Required when `action` is `move`. Group UUIDs to move all selected bookmarks into.
</ParamField>

<Expandable title="Available bulk actions">
  | Action             | Extra Fields Required | Description                                        |
  | ------------------ | --------------------- | -------------------------------------------------- |
  | `tag`              | `tagNames`            | Add the listed tags to all selected bookmarks      |
  | `move`             | `groupIds`            | Move all selected bookmarks into the listed groups |
  | `delete`           | —                     | Soft-delete (move to trash)                        |
  | `delete_permanent` | —                     | Permanently delete with no recovery                |
  | `archive`          | —                     | Archive all selected bookmarks                     |
  | `favorite`         | —                     | Favorite all selected bookmarks                    |
  | `export`           | —                     | Export selected bookmarks as JSON                  |
</Expandable>

<Warning>
  `delete_permanent` and `export` cannot be undone. Double-check your `bookmarkIds` array before issuing these actions.
</Warning>

```bash theme={null}
curl -X POST "https://api.bkmark.it/api/v1/bookmarks/bulk" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "bookmarkIds": [
      "e5f6a7b8-c9d0-1234-efab-345678901234",
      "f6a7b8c9-d0e1-2345-fabc-456789012345"
    ],
    "action": "tag",
    "tagNames": ["archived-project"]
  }'
```

```json theme={null}
{
  "affected": 2
}
```

***

## GET /api/v1/bookmarks/export

Export your entire bookmark library as a JSON file. The response body is a JSON array of bookmark objects in the same shape as the list endpoint.

```bash theme={null}
curl "https://api.bkmark.it/api/v1/bookmarks/export" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -o my-bookmarks.json
```

Returns `200 OK` with `Content-Type: application/json`.

***

## POST /api/v1/bookmarks/import

Import bookmarks from a supported format. You can migrate from a browser export, Pocket, Raindrop.io, or a previous Bkmark export.

<ParamField body="format" type="string" required>
  Source format. One of: `json` (Bkmark export), `html` (browser bookmark export), `pocket`, `raindrop`.
</ParamField>

<ParamField body="data" type="string" required>
  The raw file contents as a string (JSON string or HTML string depending on `format`).
</ParamField>

<ParamField body="skipDuplicates" type="boolean">
  When `true`, bookmarks whose URLs already exist in your library are silently skipped instead of triggering a conflict error.
</ParamField>

<Note>
  This endpoint is rate-limited to **5 imports per hour** per user to protect system performance during large batch jobs.
</Note>

```bash theme={null}
curl -X POST "https://api.bkmark.it/api/v1/bookmarks/import" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "format": "pocket",
    "data": "<html>...exported pocket HTML...</html>",
    "skipDuplicates": true
  }'
```

```json theme={null}
{
  "imported": 248,
  "skipped": 12,
  "failed": 0
}
```

***

## GET /api/v1/bookmarks/trash

List soft-deleted bookmarks. Supports the same cursor-based pagination as the main list endpoint.

```bash theme={null}
curl "https://api.bkmark.it/api/v1/bookmarks/trash?limit=25" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

```json theme={null}
{
  "data": [
    {
      "id": "b8c9d0e1-f2a3-4567-bcde-678901234567",
      "url": "https://old-resource.example.com/deprecated-guide",
      "title": "Deprecated Guide",
      "description": null,
      "type": "link",
      "source": "web",
      "isFavorite": false,
      "isArchived": false,
      "readAt": null,
      "tags": [],
      "groups": [],
      "createdAt": "2024-11-20T08:00:00.000Z",
      "updatedAt": "2025-03-28T17:44:00.000Z"
    }
  ],
  "nextCursor": null
}
```

***

## GET /api/v1/bookmarks/trash-count

Return the number of bookmarks currently in trash. Useful for displaying a badge in your UI.

```bash theme={null}
curl "https://api.bkmark.it/api/v1/bookmarks/trash-count" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

```json theme={null}
{
  "count": 7
}
```

***

## POST /api/v1/bookmarks/:id/restore

Restore a soft-deleted bookmark from trash back to your active library. The bookmark's original tags and group assignments are preserved.

<ParamField path="id" type="string" required>
  The UUID of the trashed bookmark to restore.
</ParamField>

```bash theme={null}
curl -X POST "https://api.bkmark.it/api/v1/bookmarks/b8c9d0e1-f2a3-4567-bcde-678901234567/restore" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

Returns `200 OK` with the restored bookmark object.

***

## DELETE /api/v1/bookmarks/:id/permanent

Permanently delete a specific bookmark. Unlike `DELETE /bookmarks/:id`, this endpoint always performs a hard delete regardless of query parameters. The bookmark is unrecoverable after this call.

<ParamField path="id" type="string" required>
  The UUID of the bookmark to permanently delete.
</ParamField>

<Warning>
  This action is irreversible. The bookmark and all its associations (tags, group memberships) are removed permanently.
</Warning>

```bash theme={null}
curl -X DELETE "https://api.bkmark.it/api/v1/bookmarks/b8c9d0e1-f2a3-4567-bcde-678901234567/permanent" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```

Returns `204 No Content` on success.
