Chrome Bookmarks API Complete Reference
16 min readChrome Bookmarks API Reference
The chrome.bookmarks API lets you create, read, update, delete, search, and organize bookmarks. Bookmarks are stored in a tree structure with folders and bookmark nodes.
Permissions
{
"permissions": ["bookmarks"]
}
The bookmarks permission triggers a “Read and change your bookmarks” warning in the Chrome Web Store. Consider using optional_permissions if bookmark access is not core functionality.
See the bookmarks permission reference for details.
BookmarkTreeNode Object
Every bookmark or folder is represented as a BookmarkTreeNode:
| Property | Type | Description |
|---|---|---|
id |
string |
Unique node identifier |
parentId |
string \| undefined |
ID of the parent folder (undefined for root) |
index |
number \| undefined |
Position within the parent folder (0-based) |
url |
string \| undefined |
URL of the bookmark. Absent for folders |
title |
string |
Display title |
dateAdded |
number \| undefined |
Creation timestamp (ms since epoch) |
dateLastUsed |
number \| undefined |
Last time the bookmark was opened (not set for folders; Chrome 114+) |
dateGroupModified |
number \| undefined |
Last time folder contents changed (folders only) |
children |
BookmarkTreeNode[] |
Child nodes (present only when using getTree() or getSubTree()) |
unmodifiable |
"managed" |
Present if the bookmark is managed by policy |
Tree Structure
Chrome’s bookmark tree has a fixed root structure:
Root (id: "0")
├── Bookmarks Bar (id: "1")
├── Other Bookmarks (id: "2")
└── Mobile Bookmarks (id: "3") // synced from mobile
You cannot create, modify, or delete the root node or these top-level folders.
Core Methods
chrome.bookmarks.getTree()
Get the entire bookmark tree.
const [root] = await chrome.bookmarks.getTree();
// root.children contains: Bookmarks Bar, Other Bookmarks, Mobile Bookmarks
function walkTree(nodes: chrome.bookmarks.BookmarkTreeNode[], depth = 0) {
for (const node of nodes) {
const prefix = " ".repeat(depth);
if (node.url) {
console.log(`${prefix}[bookmark] ${node.title} — ${node.url}`);
} else {
console.log(`${prefix}[folder] ${node.title}`);
if (node.children) walkTree(node.children, depth + 1);
}
}
}
walkTree(root.children!);
chrome.bookmarks.getSubTree(id)
Get a subtree starting from a specific node.
// Get everything under "Bookmarks Bar"
const [bar] = await chrome.bookmarks.getSubTree("1");
console.log(bar.children); // direct children of the bar
chrome.bookmarks.get(idOrIds)
Get specific bookmark nodes by ID.
const [bookmark] = await chrome.bookmarks.get("42");
console.log(bookmark.title, bookmark.url);
// Get multiple
const bookmarks = await chrome.bookmarks.get(["42", "43", "44"]);
chrome.bookmarks.getChildren(id)
Get the direct children of a folder.
// Children of "Other Bookmarks"
const children = await chrome.bookmarks.getChildren("2");
children.forEach((child) => {
console.log(child.title, child.url ? "(bookmark)" : "(folder)");
});
chrome.bookmarks.getRecent(numberOfItems)
Get the most recently added bookmarks.
const recent = await chrome.bookmarks.getRecent(10);
recent.forEach((b) => console.log(b.title, b.dateAdded));
chrome.bookmarks.search(query)
Search bookmarks by title, URL, or both.
// Search by string (matches title and URL)
const results = await chrome.bookmarks.search("chrome extensions");
// Search with specific fields
const results = await chrome.bookmarks.search({
title: "My Bookmark",
});
const results = await chrome.bookmarks.search({
url: "https://example.com/page",
});
// Check if a URL is bookmarked
async function isBookmarked(url: string): Promise<boolean> {
const results = await chrome.bookmarks.search({ url });
return results.length > 0;
}
chrome.bookmarks.create(bookmark)
Create a new bookmark or folder.
// Create a bookmark in "Other Bookmarks"
const bookmark = await chrome.bookmarks.create({
parentId: "2",
title: "Example Site",
url: "https://example.com",
});
console.log("Created bookmark with ID:", bookmark.id);
// Create a folder
const folder = await chrome.bookmarks.create({
parentId: "1", // Bookmarks Bar
title: "My Extension Bookmarks",
});
// Create a bookmark inside the new folder
await chrome.bookmarks.create({
parentId: folder.id,
title: "Saved Page",
url: "https://example.com/page",
});
// Create at a specific position
await chrome.bookmarks.create({
parentId: "1",
index: 0, // first position
title: "Pinned Bookmark",
url: "https://example.com",
});
chrome.bookmarks.update(id, changes)
Update a bookmark’s title or URL.
// Update title
await chrome.bookmarks.update(bookmarkId, {
title: "New Title",
});
// Update URL
await chrome.bookmarks.update(bookmarkId, {
url: "https://example.com/new-path",
});
// Update both
await chrome.bookmarks.update(bookmarkId, {
title: "Updated Title",
url: "https://example.com/updated",
});
chrome.bookmarks.move(id, destination)
Move a bookmark to a different folder or position.
// Move to another folder
await chrome.bookmarks.move(bookmarkId, {
parentId: folderId,
});
// Move to a specific position within a folder
await chrome.bookmarks.move(bookmarkId, {
parentId: folderId,
index: 0, // move to first position
});
// Reorder within the same folder
await chrome.bookmarks.move(bookmarkId, {
index: 3,
});
chrome.bookmarks.remove(id) / chrome.bookmarks.removeTree(id)
Delete a bookmark or folder.
// Remove a single bookmark
await chrome.bookmarks.remove(bookmarkId);
// Remove a folder and all its contents
await chrome.bookmarks.removeTree(folderId);
Events
chrome.bookmarks.onCreated
chrome.bookmarks.onCreated.addListener((id, bookmark) => {
console.log(`Bookmark created: ${bookmark.title} (${bookmark.url})`);
});
chrome.bookmarks.onRemoved
chrome.bookmarks.onRemoved.addListener((id, removeInfo) => {
console.log(`Bookmark removed from parent: ${removeInfo.parentId}`);
// removeInfo.node contains the removed node data
});
chrome.bookmarks.onChanged
Fires when a bookmark’s title or URL is updated.
chrome.bookmarks.onChanged.addListener((id, changeInfo) => {
console.log(`Bookmark ${id} updated:`, changeInfo.title, changeInfo.url);
});
chrome.bookmarks.onMoved
chrome.bookmarks.onMoved.addListener((id, moveInfo) => {
console.log(`Moved from ${moveInfo.oldParentId}[${moveInfo.oldIndex}] to ${moveInfo.parentId}[${moveInfo.index}]`);
});
chrome.bookmarks.onChildrenReordered
Fires when the children of a folder are reordered (e.g. sorted by the user).
chrome.bookmarks.onChildrenReordered.addListener((id, reorderInfo) => {
console.log(`Children of ${id} reordered:`, reorderInfo.childIds);
});
chrome.bookmarks.onImportBegan / chrome.bookmarks.onImportEnded
Fires when the user imports bookmarks. Use these to suppress your event handlers during bulk import.
let importing = false;
chrome.bookmarks.onImportBegan.addListener(() => {
importing = true;
});
chrome.bookmarks.onImportEnded.addListener(() => {
importing = false;
// Refresh your UI here
});
chrome.bookmarks.onCreated.addListener((id, bookmark) => {
if (importing) return; // skip during bulk import
// handle individual bookmark creation
});
Using with @theluckystrike/webext-messaging
Build a bookmark manager with background handling and popup UI:
// shared/messages.ts
type Messages = {
searchBookmarks: {
request: { query: string };
response: Array<{ id: string; title: string; url: string }>;
};
toggleBookmark: {
request: { url: string; title: string };
response: { bookmarked: boolean; id?: string };
};
getRecentBookmarks: {
request: { count: number };
response: Array<{ id: string; title: string; url: string; dateAdded: number }>;
};
};
// background.ts
import { createMessenger } from "@theluckystrike/webext-messaging";
const msg = createMessenger<Messages>();
msg.onMessage({
searchBookmarks: async ({ query }) => {
const results = await chrome.bookmarks.search(query);
return results
.filter((b) => b.url)
.map((b) => ({ id: b.id, title: b.title, url: b.url! }));
},
toggleBookmark: async ({ url, title }) => {
const existing = await chrome.bookmarks.search({ url });
if (existing.length > 0) {
await chrome.bookmarks.remove(existing[0].id);
return { bookmarked: false };
}
const created = await chrome.bookmarks.create({ title, url });
return { bookmarked: true, id: created.id };
},
getRecentBookmarks: async ({ count }) => {
const recent = await chrome.bookmarks.getRecent(count);
return recent.map((b) => ({
id: b.id,
title: b.title,
url: b.url || "",
dateAdded: b.dateAdded || 0,
}));
},
});
Using with @theluckystrike/webext-storage
Track bookmark statistics and user preferences:
import { defineSchema, createStorage } from "@theluckystrike/webext-storage";
const schema = defineSchema({
bookmarkStats: {
totalCreated: 0,
totalDeleted: 0,
lastBookmarkedUrl: "",
},
extensionFolderId: "" as string,
});
const storage = createStorage({ schema, area: "local" });
// Ensure extension folder exists on install
chrome.runtime.onInstalled.addListener(async () => {
const folder = await chrome.bookmarks.create({
parentId: "2",
title: "My Extension",
});
await storage.set("extensionFolderId", folder.id);
});
// Track bookmark creation
chrome.bookmarks.onCreated.addListener(async (id, bookmark) => {
const stats = await storage.get("bookmarkStats");
await storage.set("bookmarkStats", {
...stats,
totalCreated: stats.totalCreated + 1,
lastBookmarkedUrl: bookmark.url || stats.lastBookmarkedUrl,
});
});
Common Patterns
Bookmark the current page with one click
chrome.action.onClicked.addListener(async (tab) => {
if (!tab.url) return;
const existing = await chrome.bookmarks.search({ url: tab.url });
if (existing.length > 0) {
await chrome.bookmarks.remove(existing[0].id);
// Update icon to indicate "not bookmarked"
} else {
await chrome.bookmarks.create({
title: tab.title || tab.url,
url: tab.url,
});
// Update icon to indicate "bookmarked"
}
});
Export bookmarks as JSON
async function exportBookmarks(): Promise<string> {
const [root] = await chrome.bookmarks.getTree();
return JSON.stringify(root, null, 2);
}
Find duplicate bookmarks
async function findDuplicates() {
const [root] = await chrome.bookmarks.getTree();
const urlMap = new Map<string, chrome.bookmarks.BookmarkTreeNode[]>();
function collect(nodes: chrome.bookmarks.BookmarkTreeNode[]) {
for (const node of nodes) {
if (node.url) {
const list = urlMap.get(node.url) || [];
list.push(node);
urlMap.set(node.url, list);
}
if (node.children) collect(node.children);
}
}
collect(root.children!);
return [...urlMap.entries()]
.filter(([, nodes]) => nodes.length > 1)
.map(([url, nodes]) => ({ url, count: nodes.length, ids: nodes.map((n) => n.id) }));
}
Gotchas
-
IDs are strings, not numbers. Always treat bookmark IDs as strings.
-
Root nodes are immutable. You cannot modify or delete nodes with IDs
"0","1","2", or"3". -
search()is case-insensitive and matches partial strings in both title and URL when given a string query. Use the object form{ url: "exact-url" }for exact URL matching. -
removeTree()is irreversible. There is no undo. Consider confirming with the user before deleting folders. -
Bookmark URLs must be valid.
chrome.bookmarks.create()will throw if you pass an invalid URL.javascript:URLs are blocked in MV3. -
Events fire during sync. If the user has Chrome Sync enabled, remote bookmark changes will trigger local events. Use
onImportBegan/onImportEndedto batch handle these.
Related
- bookmarks permission
- History API
- Storage API Deep Dive
- Chrome bookmarks API docs
Frequently Asked Questions
How do I create bookmarks programmatically?
Use chrome.bookmarks.create() with a title and URL. You can also create folders and organize bookmarks in a tree structure.
Can I search bookmarks from my extension?
Yes, chrome.bookmarks.search() lets you find bookmarks by text, URL, or other criteria.
Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.