Chrome Extension API Cheatsheet: Every API You Need for Manifest V3 (2026)

AppBooster Team · · 9 min read
Chrome browser developer tools with code

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.* and browser.* 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 true in onMessage when 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. setTimeout doesn’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

APIPermissionBest For
tabs"tabs" (for URL access)Tab manipulation, navigation
storage"storage"Persisting user data and settings
runtimeNoneMessaging, lifecycle events
scripting"scripting"Injecting JS/CSS into pages
alarms"alarms"Scheduled and recurring tasks
actionNoneToolbar 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" + hostsRead and write browser cookies
notifications"notifications"System notifications
permissionsNoneRuntime permission requests
bookmarks"bookmarks"Bookmark CRUD
history"history"Browsing history access
downloads"downloads"Manage file downloads
devtools.*NoneDevTools 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