We Built a Link Shortener With AI OG Image Generation — Here's What We Learned

Most link shortener posts are the same. Someone builds a Bitly clone in a weekend, writes about it, and the headline is "I Built X in Y Hours." This isn't that post.
Peeksy started from a real frustration: every time I shared a link on Twitter or LinkedIn, I had zero control over how it looked. Bad image. Wrong title. The destination site had set some OG tags five years ago and never touched them. I'd write a thoughtful post, attach a link that looked like garbage, and wonder why nobody clicked.
The solution seemed obvious: a short link that serves its own Open Graph tags — ones you set yourself, regardless of what the destination page has. Simple idea. Surprisingly hard to do well. Here's everything we learned.
The Core Technical Bet: Cloudflare Workers + Hono
The product only works if the short link responds fast. When LinkedIn, Twitter, or Slack sends a crawler to read our OG tags, we have milliseconds. If we're slow, the platform times out and falls back to the destination — defeating the entire point.
This made the infrastructure choice easy: Cloudflare Workers. No cold starts, sub-millisecond response times globally, and the crawlers from every major platform hit a Cloudflare data center that's physically close to them.
For the framework, we picked Hono — the Express equivalent for Cloudflare Workers. TypeScript-native, under 14KB, GA on Workers as of 2025. The routing code for the entire link resolution system is about 80 lines.
Storage is split between Cloudflare KV (link metadata, user records) and Cloudflare R2 (OG images). Both live within the same Cloudflare infrastructure as the Worker — zero egress latency on reads.
The Hard Part: Generating OG Images in a Worker
Cloudflare Workers run in a V8 isolate — not Node.js. No file system. No native binaries. Most image generation libraries won't work at all.
The solution is a pipeline: Satori converts a JSX template into SVG entirely in the JS runtime, then resvg-wasm renders that SVG to PNG using WebAssembly. Both run entirely inside the Worker — no external render service, no queue, no second round-trip.
The thing that killed us initially: fonts. Satori needs font data to render text. Loading from R2 on every request was too slow. Bundling them hit the 3MB Worker limit. The fix: store fonts in R2, load once on Worker startup, cache in module scope. Each instance loads the font once, not per-request.
Image generation time went from 2.1 seconds (cold, per-request font loading) to under 180ms warm. The PNG is stored in R2 with a content-hash URL so Cloudflare's CDN caches it indefinitely.
Adding AI: What We Expected vs. What Actually Happened
We expected AI image generation to be the feature everyone talked about. We were wrong.
Early users would say "cool" to AI backgrounds — then immediately switch to one of the five clean design templates. Template-based images were more consistent, faster, and on-brand.
The AI feature people actually wanted: AI-generated copy. Give me a good title and description for this URL. Paste a link, approve the suggestion in one click, move on. We pivoted accordingly.
We use Gemini Flash via OpenRouter for copy generation — fast (under a second), inexpensive ($0.00015 per 1K input tokens), and excellent quality for a focused task. The prompt engineering took longer than the integration.
Billing: Why We Chose Paddle and What Surprised Us
We chose Paddle over Stripe for one reason: Paddle is a Merchant of Record, handling VAT and GST globally on our behalf. For a solo-founder SaaS, the alternative is registering for VAT in every country you have customers — a nightmare.
Paddle's sandbox is excellent — the full checkout flow, webhooks, subscription state changes all testable end-to-end. It saved us from shipping at least two billing bugs to production.
What annoyed us: you need your payment link domain approved before taking any transactions. Submit it for approval far earlier than you think. It's a hard blocker.
What the Community Told Us
Three patterns came up consistently across HN, Twitter, and indie hackers:
- Marketers wanted control over every link they share — not just their own content. The question was always "can I do this for links I don't own?" Yes, that's the entire point.
- Developers wanted an API and SDK to integrate Peeksy into their own tools and CMS workflows. This became the foundation of our Pro tier (REST API + TypeScript SDK) and Business tier (MCP server).
- Creators and newsletter writers wanted platform-level analytics — which platform drove the most clicks, and whether their chosen OG image outperformed the default. Platform attribution is now a first-class metric.
What We'd Do Differently
- KV's eventual consistency caused subtle billing bugs. A user would upgrade via Paddle webhook, we'd update KV, and within 200ms another request would read the old plan tier because the write hadn't propagated. We added a cache-bust pattern on plan reads to fix it.
- Structured logging from day one. Cloudflare Workers logs are great (
wrangler tailis instant) but adding logging reactively cost us hours of debugging intermittent 502s.
The Full Stack
- Frontend: Next.js + Tailwind + shadcn/ui on Vercel
- Backend: Hono on Cloudflare Workers
- Storage: KV (metadata) + R2 (images)
- Image gen: Satori + resvg-wasm in-Worker
- AI backgrounds: Flux.2 via OpenRouter
- AI copy: Gemini Flash via OpenRouter
- Billing: Paddle
- CMS: Sanity
- Domains: peeksy.space (marketing), peeksy.link (short links)
Entire backend under 50ms globally — no cold starts.
Where We're Going
A/B testing for OG images is next — create two versions of a preview and auto-split traffic to see which drives more clicks. Then team workspaces (Q2 2026), a Figma plugin, and Zapier + Make integrations.
The bigger bet is AI agent integration. The MCP server shipping with Business means Peeksy can be used as a tool by Claude, GPT, and other agents — create a short link with a custom OG image as part of a larger workflow with no human in the loop.
Peeksy is at peeksy.space. The free plan is genuinely free, no credit card. Find us on Twitter if you want to talk stack decisions. We build in public.