# I built two Etsy SEO tools in 14 days — full stack breakdown (Cloudflare Workers + KV + Stripe)

> Source: <https://dev.to/aidaserviceasglitch/i-built-two-etsy-seo-tools-in-14-days-full-stack-breakdown-cloudflare-workers-kv-stripe-1m18>
> Published: 2026-05-31 11:23:02+00:00

I spent the last two weeks building two products targeting the 5M+ Etsy seller market. Here's the honest story — what worked, what didn't, and the full technical stack.

Most Etsy sellers write listings from a maker's perspective. They describe what they made. Buyers search for what they want to find.

"Handmade ceramic mug, blue, 12oz" gets almost no search traffic.

"Handmade Ceramic Coffee Mug | Blue Stoneware 12oz | Gift for Coffee Lovers" ranks for a dozen buyer searches.

Same product. One gets found. One doesn't.

**ContentBase** — AI listing optimizer. Paste any Etsy, Shopify, or Amazon listing. Get a rewritten title, description, and 13 keyword-targeted tags in ~10 seconds. Free for 5/day, no signup. Pro is $9/month.

**ShopScan** — on-demand Etsy SEO audit. Pay $19, submit your listing, get a 12-page report (score, keyword gaps, full rewrite, action plan) by email within 24h.

Total monthly infrastructure cost: **$0**.

Most people implement Stripe webhooks for post-payment activation. I found a simpler pattern.

After payment, Stripe redirects to:

```
https://contentbase-crw.pages.dev?session_id={CHECKOUT_SESSION_ID}
```

The frontend POSTs the session_id to `/activate-pro`

on the Worker. The Worker validates the format (`cs_live_`

prefix — unguessable), generates a token, stores in KV, emails via Brevo.

No webhook endpoint. No signing secret. No HTTPS concerns. Works perfectly.

```
async function handleActivatePro(request, env) {
  const { session_id, email } = await request.json();

  if (!session_id?.startsWith('cs_live_')) {
    return json({ error: 'Invalid session' }, 400);
  }

  // Prevent replay attacks
  const existing = await env.CB_TOKENS.get(`session:${session_id}`);
  if (existing) return json({ token: existing, reused: true });

  const token = generateToken();
  await env.CB_TOKENS.put(`token:${token}`, 'active');
  await env.CB_TOKENS.put(`session:${session_id}`, token);

  await sendEmail(env, { to: email, subject: 'Your ContentBase Pro access is live' });
  return json({ token, success: true });
}
```

No database needed:

``` js
async function checkRateLimit(ip, env) {
  const key = `rate:${ip}:${new Date().toISOString().split('T')[0]}`;
  const count = parseInt(await env.CB_RATE.get(key) || '0');
  if (count >= 5) return { allowed: false };
  await env.CB_RATE.put(key, String(count + 1), { expirationTtl: 86400 });
  return { allowed: true };
}
```

Keys expire after 24h automatically. Cleanup is free.

**Cloudflare is absurdly good for this use case.** Worker + KV + Pages covers serverless functions, key-value storage, and static hosting — all free, globally distributed.

**Zero-friction free tier matters.** No email required to use ContentBase. No account. Just paste and get. The conversion to Pro happens after people see the output.

**Distribution is harder than building.** The technical side took days. Finding actual Etsy sellers to try it is taking weeks. If you've solved Etsy/niche community distribution, I'd genuinely love to hear how.

Happy to answer questions about the architecture or the Etsy seller market.
