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:
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.