cd /news/ai-tools/free-ai-for-my-chrome-extension-s-us… · home › topics › ai-tools › article
[ARTICLE · art-152754] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=↑ positive

Free AI for my Chrome extension's users, with no API key and no surprise bill

A developer behind the Groupify Chrome tab organizer built a Cloudflare Worker that gives extension users AI-powered tab grouping without requiring an API key, running on Workers AI's free daily allocation so requests simply fail rather than bill once the quota is exhausted. The Worker identifies the extension via CORS, caps requests per IP per minute, sends only tab titles and site names (never full URLs), and stores no data, while the extension tries on-device AI first, then the cloud, then date-based naming. The developer also implemented a 15-second "ownChangesUntil" window so the extension's automatic sorting never overrides groups users created by hand.

by read6 min views1 publishedOct 19, 2026

TL;DR I wanted my tab extension to name and sort tab groups with AI. Chrome's built-in AI isn't on most computers, and nobody wants to paste an API key into a tab tool. So I put a tiny Worker on Cloudflare in between, running on the free Workers AI allocation: when it runs out it simply stops, and nothing is billed. Users without on-device AI flip one switch in Settings and get AI names.

Groupify is my Chrome tab organizer. When I start a new project I usually have Pinterest, the Next.js docs, Cloudflare and Vercel open at once, and grouping by website just splits them apart. What I wanted was one click to put those tabs in one group, with a sensible name such as “Website deploy”.

That needs AI. The question was which AI.

I wanted a free allocation that stops when it's used up, like the Azure speech free tier I use in another extension.

Workers AI gives every account a free daily allocation. On the Workers Free plan, requests past it fail instead of being charged, so the most it can ever cost me is nothing. I only needed a tiny Worker in the middle that does three things: recognise my extension, limit how much each person can use, and turn the tabs into a prompt.

One endpoint names a set of tabs; the other sorts a pile of tabs into groups by task. The Worker stores nothing and logs nothing, so it never remembers what anyone sent.

The extension sends each tab's title and site name, never the full address, because addresses often carry tokens or private parameters. At most 40 tabs go in one request, and long titles are cut short.

The Worker answers only my extension's origins (the store build and my development build) through CORS. That means the extension needs no new permission, so users see no new warning when it updates. An origin can be faked, so there's also a per-IP limit per minute on top of the free allocation's hard cap. The worst case is a day without AI.

Most of my users read Traditional Chinese, so I picked a cheap open model that handles Chinese well and told it to skip its thinking output to save allocation. Naming a group uses very little, and at my current number of users the free allocation goes a long way.

Cloud AI is a switch in Settings, off by default, with a line saying tab titles and site names go to my server and nothing is kept. The extension always tries on-device AI first, then the cloud, then names by date, so whichever step fails, users never see an error.

Once Groupify could sort tabs automatically, I hit a problem: automatic sorting must not pull apart groups I made by hand. If I put my own blog's tabs in a group called “Me”, sorting by purpose shouldn't move them into “Reading”.

But Chrome doesn't record who changed a tab or a group, only how things are now. So the extension has to tell for itself. Before it changes any group, it writes down “the next few seconds of changes are mine”. A background worker listens to Chrome's tab and group events, and treats anything outside that window as the user's decision, including dragging tabs and renaming groups in Chrome's own tab bar.

// The extension's own changes are wrapped, so they never count as the user's
async function asExtension<T>(change: () => Promise<T>): Promise<T> {
  await storage.session.set({ ownChangesUntil: Date.now() + 15_000 })
  try {
    return await change()
  } finally {
    // Chrome's events can arrive a little late, so keep the window open for a second
    await storage.session.set({ ownChangesUntil: Date.now() + 1_000 })
  }
}

const isOwnChange = async () => (await storage.session.get('ownChangesUntil')) > Date.now()

// Background worker: every other change is the user's decision
tabs.onUpdated.addListener(async (tabId, change, tab) => {
  if (change.groupId === undefined || change.groupId === NO_GROUP) return
  if (await isOwnChange()) return
  const group = await tabGroups.get(change.groupId)
  if (group.title) rememberSite(siteOf(tab.url), group.title) // e.g. "amsyn.org" -> "Me"
})

tabGroups.onUpdated.addListener(async (group) => {
  const before = lastKnownTitle(group.id) // from a snapshot the worker keeps
  if (before === group.title || (await isOwnChange())) return
  renameRulesAndLock(before, group.title) // rules and locks follow the new name
})

// Reclassify: what the user decided goes first, automatic sorting gets the rest
function reclassify(tabs: Tab[], rules: SiteRule[], mode: Mode) {
  const movable = tabs.filter((tab) => !tab.pinned && !isLocked(tab.group))
  const { claimed, rest } = splitByRules(movable, rules) // sites with a rule
  return [...groupsFromRules(claimed), ...plan(mode, rest)]
}

A few details:

Before Now
Users without on-device AI Groups named after the date One switch for AI names and sorting
API key Users would need their own None at all
Cost risk A budget alert only notifies Stops when the free allocation runs out
Privacy — Off by default, titles and site names only, nothing kept

If usage grows or someone abuses it, the worst that happens is a day without AI, and the extension goes back to naming by date. For a side project with a handful of users, that's the setup I'm most comfortable with.

If you want to add free AI that can't run up a bill to your own extension or small tool, paste this into your AI coding assistant:

Add an optional shared AI feature to my browser extension, using this design:

1. Server: a Cloudflare Worker using the Workers AI binding (free daily allocation, so on the
   Workers Free plan it fails instead of billing). Pick a cheap model that handles my users'
   languages. Expose only the endpoints the feature needs, keep no logs or state, and validate
   every request body (cap the number of items and the length of each string).
2. Access: answer CORS only for my extension's origins (store ID plus my unpacked dev ID, read
   from a variable), so the extension needs no new permission. Add a per-IP rate limit with the
   Workers rate-limiting binding.
3. Data: send only what the prompt needs (for tabs: title and hostname, never full URLs).
4. Answers: ask for JSON, but parse leniently (find the first JSON object, ignore any thinking
   block) and validate every field. Retry once if the answer contains U+FFFD.
5. Extension: an opt-in setting, off by default, that says exactly what is sent and where. Try
   any on-device AI first, then the Worker, then a non-AI fallback, so no error ever reaches the
   user. Treat every HTTP error or timeout as "no AI".
6. Tests for request parsing, answer parsing and the fallback order, plus a README for the
   Worker with the deploy steps.

Don't ask me for any secret. Stop after writing the code so I can review and deploy it.

Cover: Mail sorting room, U.S. Post by libraryofcongress, CC0 1.0.

── more in #ai-tools 4 stories · sorted by recency
── more on @groupify 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/free-ai-for-my-chrom…] indexed:0 read:6min 2026-10-19 · —