Chrome Extension API Cheatsheet: Every API You Need for Manifest V3 (2026)
Stop digging through Chrome’s official docs every time you need an API call. This cheatsheet gives you copy-paste ready code for every Chrome Extension API that matters — organized by what you’re actually trying to do.
Bookmark this Chrome Extension API cheatsheet. You’ll come back to it.
Manifest V3 Starter Template
Every extension starts here. Get your manifest.json right and everything else falls into place.
{
"manifest_version": 3,
"name": "My Extension",
"version": "1.0.0",
"description": "One-line description",
"permissions": ["storage", "activeTab", "alarms"],
"host_permissions": ["https://*.example.com/*"],
"background": { "service_worker": "service-worker.js" },
"content_scripts": [{
"matches": ["https://*.example.com/*"],
"js": ["content.js"]
}],
"action": {
"default_popup": "popup.html",
"default_icon": { "16": "icon16.png", "48": "icon48.png", "128": "icon128.png" }
}
}Tip: Since Chrome 146, all APIs work under both
chrome.*andbrowser.*namespaces for cross-browser compatibility.
Tabs API — chrome.tabs
The most-used Chrome Extension API. Query, create, update, and close tabs programmatically.
Permission: "tabs" (only needed for url, title, favIconUrl access)
// Get the current active tab
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
// Open a new tab
await chrome.tabs.create({ url: "https://example.com" });
// Update current tab's URL
await chrome.tabs.update(tab.id, { url: "https://example.com" });
// Close a tab
await chrome.tabs.remove(tab.id);
// Listen for tab changes
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === "complete") {
console.log("Page loaded:", tab.url);
}
});Storage API — chrome.storage
Persist data across sessions. Choose local (device-only) or sync (synced across signed-in devices).
Permission: "storage"
// Save data
await chrome.storage.local.set({ key: "value", settings: { theme: "dark" } });
// Read data
const { key, settings } = await chrome.storage.local.get(["key", "settings"]);
// Remove specific keys
await chrome.storage.local.remove("key");
// Clear everything
await chrome.storage.local.clear();
// Watch for changes (works in any context)
chrome.storage.onChanged.addListener((changes, area) => {
if (area === "local" && changes.settings) {
console.log("Old:", changes.settings.oldValue);
console.log("New:", changes.settings.newValue);
}
});Storage limits: local = 10 MB, sync = 100 KB total (8 KB per item), session = 10 MB (in-memory, cleared on restart).
Runtime API — chrome.runtime
The backbone of extension communication. Send messages between popup, content scripts, and the service worker.
// Send message from popup/content script to service worker
const response = await chrome.runtime.sendMessage({ action: "getData", id: 42 });
// Listen in service worker
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "getData") {
fetchData(message.id).then(sendResponse);
return true; // keeps the message channel open for async response
}
});
// Get extension URL (for assets)
const iconUrl = chrome.runtime.getURL("images/icon.png");
// Detect install or update
chrome.runtime.onInstalled.addListener(({ reason }) => {
if (reason === "install") console.log("First install!");
if (reason === "update") console.log("Updated!");
});Chrome Extension Message Passing — Content Script ↔ Service Worker
The #1 pain point for new Chrome extension developers. Here’s the pattern that works.
// === content-script.js ===
// Send to service worker
const response = await chrome.runtime.sendMessage({
type: "FETCH_DATA",
payload: { url: window.location.href }
});
console.log("Got:", response);
// === service-worker.js ===
// Receive from content script
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === "FETCH_DATA") {
fetch("https://api.example.com/data")
.then(r => r.json())
.then(data => sendResponse({ success: true, data }))
.catch(err => sendResponse({ success: false, error: err.message }));
return true; // CRITICAL: keeps channel open for async
}
});
// === Send TO a specific content script ===
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
await chrome.tabs.sendMessage(tab.id, { type: "UPDATE_UI", data: result });Common mistake: Forgetting
return trueinonMessagewhen using async operations. Without it, the message channel closes immediately.
Scripting API — chrome.scripting
Inject JavaScript or CSS into web pages dynamically (replaces MV2’s chrome.tabs.executeScript).
Permission: "scripting"
// Inject a function into the active tab
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: (greeting) => {
document.title = greeting;
},
args: ["Hello from extension!"]
});
// Inject a CSS file
await chrome.scripting.insertCSS({
target: { tabId: tab.id },
files: ["styles/injected.css"]
});
// Inject into specific frames
await chrome.scripting.executeScript({
target: { tabId: tab.id, allFrames: true },
files: ["content.js"]
});Alarms API — chrome.alarms
Schedule recurring or delayed tasks. Essential since Manifest V3 service workers can’t use setInterval reliably.
Permission: "alarms"
// Create a repeating alarm (minimum 1 minute)
await chrome.alarms.create("sync-data", { periodInMinutes: 5 });
// One-time alarm (fires after delay)
await chrome.alarms.create("reminder", { delayInMinutes: 30 });
// Listen for alarm
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === "sync-data") {
syncDataToServer();
}
});
// Clear an alarm
await chrome.alarms.clear("sync-data");Why not
setTimeout? MV3 service workers go idle after ~30 seconds. Alarms survive this.setTimeoutdoesn’t.
Action API — chrome.action
Control your extension’s toolbar icon — badges, popups, click behavior.
// Set badge text and color
await chrome.action.setBadgeText({ text: "42" });
await chrome.action.setBadgeBackgroundColor({ color: "#FF0000" });
// Change icon dynamically
await chrome.action.setIcon({ path: "icons/active.png" });
// Disable for specific tab
await chrome.action.disable(tabId);
// Handle icon click (when no popup is set)
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ["toggle.js"] });
});Context Menus API — chrome.contextMenus
Add custom items to the browser’s right-click menu.
Permission: "contextMenus"
// Create menu items (call in onInstalled)
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "translate-selection",
title: 'Translate "%s"',
contexts: ["selection"]
});
chrome.contextMenus.create({
id: "save-image",
title: "Save to Collection",
contexts: ["image"]
});
});
// Handle clicks
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "translate-selection") {
translateText(info.selectionText);
}
});Declarative Net Request — chrome.declarativeNetRequest
Block or redirect network requests declaratively (replaces MV2’s webRequest blocking).
Permission: "declarativeNetRequest"
// rules.json
[
{
"id": 1,
"priority": 1,
"action": { "type": "block" },
"condition": {
"urlFilter": "tracker.example.com",
"resourceTypes": ["script", "xmlhttprequest"]
}
},
{
"id": 2,
"priority": 1,
"action": {
"type": "redirect",
"redirect": { "url": "https://new.example.com/page" }
},
"condition": { "urlFilter": "old.example.com/page" }
}
]// Add dynamic rules at runtime
await chrome.declarativeNetRequest.updateDynamicRules({
addRules: [{
id: 100,
priority: 1,
action: { type: "block" },
condition: { urlFilter: "ads.example.com" }
}],
removeRuleIds: [100]
});Side Panel API — chrome.sidePanel
Display persistent UI in the browser’s side panel (Chrome 114+).
Permission: "sidePanel"
// manifest.json
{
"side_panel": { "default_path": "sidepanel.html" }
}// Open side panel programmatically
chrome.sidePanel.open({ windowId: tab.windowId });
// Set panel per-tab
await chrome.sidePanel.setOptions({
tabId: tab.id,
path: "sidepanel-detail.html",
enabled: true
});Identity API — chrome.identity
OAuth2 authentication made simple for Chrome extension development.
Permission: "identity"
// Get an OAuth2 token (opens Google sign-in)
const token = await chrome.identity.getAuthToken({ interactive: true });
// Use with Google APIs
const response = await fetch("https://www.googleapis.com/gmail/v1/users/me/messages", {
headers: { Authorization: `Bearer ${token.token}` }
});
// For non-Google OAuth
const redirectUrl = await chrome.identity.launchWebAuthFlow({
url: `https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}`,
interactive: true
});Cookies API — chrome.cookies
Read and modify browser cookies.
Permission: "cookies" + host permissions
// Get a specific cookie
const cookie = await chrome.cookies.get({
url: "https://example.com",
name: "session_id"
});
// Set a cookie
await chrome.cookies.set({
url: "https://example.com",
name: "preference",
value: "dark_mode",
expirationDate: Date.now() / 1000 + 86400
});
// Get all cookies for a domain
const cookies = await chrome.cookies.getAll({ domain: "example.com" });Notifications API — chrome.notifications
Show native system notifications.
Permission: "notifications"
await chrome.notifications.create("my-notification", {
type: "basic",
iconUrl: "icon128.png",
title: "Task Complete",
message: "Your data has been synced successfully.",
priority: 2
});
// Handle click
chrome.notifications.onClicked.addListener((notificationId) => {
chrome.tabs.create({ url: "https://example.com/dashboard" });
chrome.notifications.clear(notificationId);
});Permissions API — chrome.permissions
Request optional permissions at runtime instead of install time. Better for user trust.
// manifest.json
{ "optional_permissions": ["bookmarks", "history"] }// Request permission when needed
const granted = await chrome.permissions.request({
permissions: ["bookmarks"]
});
if (granted) {
const bookmarks = await chrome.bookmarks.getTree();
}
// Check if permission exists
const has = await chrome.permissions.contains({ permissions: ["history"] });Chrome Extension API Cheatsheet — Quick Reference Table
| API | Permission | Best For |
|---|---|---|
tabs | "tabs" (for URL access) | Tab manipulation, navigation |
storage | "storage" | Persisting user data and settings |
runtime | None | Messaging, lifecycle events |
scripting | "scripting" | Injecting JS/CSS into pages |
alarms | "alarms" | Scheduled and recurring tasks |
action | None | Toolbar icon, badge, popup |
contextMenus | "contextMenus" | Right-click menu items |
declarativeNetRequest | "declarativeNetRequest" | Blocking and redirecting requests |
sidePanel | "sidePanel" | Persistent side UI |
identity | "identity" | OAuth2 authentication |
cookies | "cookies" + hosts | Read and write browser cookies |
notifications | "notifications" | System notifications |
permissions | None | Runtime permission requests |
bookmarks | "bookmarks" | Bookmark CRUD |
history | "history" | Browsing history access |
downloads | "downloads" | Manage file downloads |
devtools.* | None | DevTools panels and inspection |
5 Common Manifest V3 Gotchas — Chrome Extension API Cheatsheet
1. Service worker goes idle MV3 service workers terminate after ~30 seconds of inactivity. Use chrome.alarms instead of setInterval. For persistent connections, use chrome.runtime.connect() to keep the worker alive.
2. return true in message listeners If your onMessage handler does anything async, you must return true or the response channel closes before your promise resolves.
3. Can’t access DOM from service worker Service workers have no DOM. Use chrome.scripting.executeScript() to interact with page content from the background.
4. Host permissions are now separate In MV3, host permissions go in "host_permissions", not "permissions". Getting this wrong silently fails.
5. declarativeNetRequest replaces webRequest blocking MV3 removed blocking webRequest. Use declarativeNetRequest with JSON rule files instead. Dynamic rules can be added at runtime.
What’s Next?
Now that you have the Chrome Extension API patterns, build something. Start with our Chrome Extension Development Fundamentals guide, or check the official Chrome API reference for edge cases.
Building an extension? AppBooster helps you get more reviews, improve your Web Store ranking, and grow your user base — so your extension gets the audience it deserves.
Share this article
Build better extensions with free tools
Icon generator, MV3 converter, review exporter, and more — no signup needed.
Related Articles
Building Accessible Chrome Extensions: Keyboard, Screen Reader, and WCAG Compliance
26% of US adults have disabilities. Make your Chrome extension accessible with focus traps, ARIA, keyboard nav, and WCAG 2.1 AA compliance.
I Built the Same Chrome Extension With 5 Different Frameworks. Here's What Actually Happened.
WXT vs Plasmo vs CRXJS vs Extension.js vs Bedframe. Real benchmarks, honest opinions, and the framework with 12K stars that's quietly dying.
5 Best Email Marketing Services to Grow Your Chrome Extension (2026)
Compare the top email marketing platforms for SaaS and Chrome extension developers. MailerLite, Mailchimp, Brevo, ActiveCampaign, and Drip reviewed.