DEV Community

Cover image for How I Built a Self-Feeding SEO Engine with GPT-4o Vision + Next.js 15
Paul ROW
Paul ROW

Posted on

How I Built a Self-Feeding SEO Engine with GPT-4o Vision + Next.js 15

Two weeks ago, I launched an ai tattoo generator called TattooRed. Today, I have 200 pages generated and 15 indexed by Google. But here's the interesting part: users generate the content, GPT-4o Vision enriches it, and Next.js 15 serves itβ€”automatically.

The architecture is designed to scale from 0 to 100K+ pages at $0.006 per page. Here's exactly how it worksβ€”including the disasters I didn't anticipate.


🎯 The Problem: 135K Monthly Searches I Couldn't Cover Manually

I started building an ai tattoo generator. The obvious keyword was "ai tattoo generator" (22K monthly searches). But then I discovered "tattoo ideas" gets 135K monthly searches with similar difficulty (56% vs 50%).

The opportunity was massive. But here's the challenge:

How do you create 10,000+ unique, SEO-optimized pages for long-tail keywords like:

  • "minimalist lion tattoo ideas"
  • "watercolor phoenix back tattoo"
  • "geometric dragon sleeve for men"
  • "small butterfly ankle tattoo for women"

Manual content creation? Impossible. Hiring writers? $50-100 per article = $500K+ for 10K pages.

The solution: Turn user creativity into SEO content automatically.


πŸ—οΈ The Architecture: User Content β†’ AI Analysis β†’ SEO Pages

Here's how the system works:

1. User Generates a Tattoo

Users describe their tattoo idea (e.g., "phoenix rising from ashes on my back"). The system:

  • Sends the prompt to ModelsLab's Flux API
  • Generates a professional tattoo design (white background, no body parts)
  • Returns the image in ~15 seconds

Cost: $0.0047 per image

2. Auto-Publish Flow (Free Users)

Free users get their tattoos published automatically to the tattoo ideas gallery. This is the exchange: free generation in return for SEO content contribution.

When published, the system:

  • Uploads image to Supabase Storage
  • Sends image + user prompt to GPT-4o-mini Vision
  • Receives structured SEO content back
  • Creates a new Next.js page automatically

3. GPT-4o Vision Analyzes & Generates Content

This is where the magic happens. Here's a simplified version of the API call:

// lib/openai-seo-generator.ts
export async function analyzeTattooAndGenerateSEO(
  imageUrl: string, 
  userPrompt: string,
  existingCategories: string[]
) {
  const response = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [
      {
        role: "system",
        content: `You are an expert tattoo analyst and SEO content writer.`
      },
      {
        role: "user",
        content: [
          { 
            type: "text", 
            text: `User prompt: "${userPrompt}"

            Return JSON with SEO-optimized content including:
            title, meta description, 500+ word analysis, categories, etc.`
          },
          { 
            type: "image_url", 
            image_url: { url: imageUrl } 
          }
        ]
      }
    ],
    response_format: { type: "json_object" }
  });

  return JSON.parse(response.choices[0].message.content);
}
Enter fullscreen mode Exit fullscreen mode

Cost: $0.0015 per analysis (GPT-4o-mini Vision is 10x cheaper than GPT-4o)

4. Next.js 15 Creates Pages Automatically

The returned data creates a new page at /tattoo-ideas/[slug]:

// app/tattoo-ideas/[slug]/page.tsx
export async function generateMetadata({ params }: Props) {
  const tattoo = await getTattooBySlug(params.slug);

  return {
    title: tattoo.seo_title,
    description: tattoo.seo_description,
    openGraph: {
      title: tattoo.seo_title,
      images: [tattoo.image_url],
    },
  };
}

// ISR: Revalidate every hour
export const revalidate = 3600;
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Side note: I built this with Claude Code as my coding assistant. The architecture decisions are mine, but Claude helped me write cleaner TypeScript and avoid Next.js pitfalls.

5. Multi-Category Magic

Each tattoo automatically appears in multiple category pages. A "phoenix rising back tattoo" shows up in:

  • /tattoo-ideas/category/animals
  • /tattoo-ideas/category/birds
  • /tattoo-ideas/category/mythology
  • /tattoo-ideas/category/unisex/animals (combined)

One user generation = 5-10 SEO entry points. This is the multiplier effect.


πŸ’° The Economics: Infinite Scale at $0.006 Per Page

Let's break down the cost per generated SEO page:

Image generation (Flux):        $0.0047
GPT-4o-mini Vision analysis:    $0.0015
Supabase storage:               $0.0001
-----------------------------------------
Total per page:                 $0.0063
Enter fullscreen mode Exit fullscreen mode

At scale:

  • 1,000 pages = $6.30
  • 10,000 pages = $63
  • 100,000 pages = $630

Compare to traditional content:

  • Freelance writers: $50-100/article
  • Content agencies: $200+/article
  • 10,000 articles = $500K - $2M

ROI calculation:
If I get 100 paying users at $13.95/month = $1,395 MRR, that covers 221,000 generated pages. The economics work at any scale.


😱 The "Oh Shit" Moment (aka What Could Go Wrong)

So there I was, 2 AM, super proud of my creation. System running smoothly. Users generating tattoos. SEO content flowing like a river.

I went to bed feeling like a genius.

Then, 10 minutes later, lying in the dark, the thoughts started:

"Wait... what if OpenAI's API goes down?"

"What if I run out of credits and don't notice?"

"What if the background process fails silently and I have 1,000 orphaned tattoos with no SEO content?"

I opened my laptop. Checked the database.

Yep. 37 orphaned tattoos. Published images, but the GPT-4o enrichment process had failed silently. No SEO content. No categories. Just... floating in the void.

The Problem: Silent Failures

Here's what was happening:

  1. User generates tattoo βœ…
  2. Image uploads to Supabase βœ…
  3. Tattoo appears in gallery βœ…
  4. Background job triggers to generate SEO content... ❌ (fails silently)

Why it failed:
At first, I blamed the usual suspects:

  • OpenAI API timeout
  • Rate limits
  • Network hiccups
  • Low credits
  • A process crash

But the real issue was something different.
Everything worked perfectly locally… but failed silently once deployed on Vercel.

Locally, my enrichment function ran synchronously:
I sent the GPT-4o-mini Vision request β†’ waited for the response β†’ stored the SEO content.
No queue, no background jobs, no race conditions.

But on Vercel, the story changed:

  • Vercel serverless functions have strict execution time limits
  • After a few seconds, the function gets hard-killed
  • No error is thrown back into your app
  • No logs unless you dig deep

The tattoo gets created… but the enrichment never finishes

This leads to the weirdest bug:
Everything looks successful from the user’s perspective, but the SEO content is missing.

I also tried a β€œfire-and-forget” strategy (trigger enrichment and return immediately), but serverless environments don’t guarantee the background execution will completeβ€”so enrichment jobs were still randomly failing.

In other words:

πŸ‘‰ My code wasn’t the problem β€” the serverless runtime model was.

The tattoos existed, but they were SEO zombiesβ€”alive but without the content that makes them discoverable.

The Solution: The "Orphan Tattoo Recovery System"

I built an admin panel with a simple but powerful feature:

"Check for Orphan Tattoos" button

Here's what it does:

// app/api/admin/pending-tattoos/route.ts (simplified)

export async function GET() {
  // Find tattoos that are published but missing SEO content
  const orphanTattoos = await supabase
    .from('tattoos')
    .select('*')
    .is('seo_content', null) // No SEO content yet
    .eq('published', true)   // But already published
    .order('created_at', { ascending: false });

  return Response.json({ 
    orphans: orphanTattoos.data,
    count: orphanTattoos.data?.length 
  });
}

export async function POST(request: Request) {
  const { tattooIds } = await request.json();

  // Process orphans one by one
  const results = [];
  for (const id of tattooIds) {
    try {
      const tattoo = await getTattooById(id);

      // Retry SEO generation
      const seoContent = await analyzeTattooAndGenerateSEO(
        tattoo.image_url,
        tattoo.user_prompt,
        existingCategories
      );

      // Update tattoo with SEO content
      await supabase
        .from('tattoos')
        .update({
          seo_title: seoContent.title,
          seo_description: seoContent.meta_description,
          content: seoContent.content,
          // ... other fields
        })
        .eq('id', id);

      results.push({ id, status: 'success' });

    } catch (error) {
      results.push({ id, status: 'failed', error: error.message });
    }
  }

  return Response.json({ processed: results });
}
Enter fullscreen mode Exit fullscreen mode

In the admin panel:

Orphan Tattoos Detected: 37

[Check for Orphans] 

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ phoenix-back-a1b2c3                    β”‚
β”‚ Created: 2 hours ago                   β”‚
β”‚ Status: Missing SEO content            β”‚
β”‚ [Process Now]                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ dragon-sleeve-d4e5f6                   β”‚
β”‚ Created: 5 hours ago                   β”‚
β”‚ Status: Missing SEO content            β”‚
β”‚ [Process Now]                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

[Process All (37 tattoos)]
Enter fullscreen mode Exit fullscreen mode

Click the button. Boom. All 37 orphans get their SEO content in ~2 minutes.

Why this works:

  • βœ… Manual recovery when automation fails
  • βœ… Retry logic for failed API calls
  • βœ… Visual confirmation that everything is caught up
  • βœ… Peace of mind (I can sleep now)

Bonus feature I added:
Daily cron job that checks for orphans automatically and sends me a notification:

🚨 Found 12 orphan tattoos from the last 24 hours
πŸ‘‰ Check admin panel: tattoored.com/admin/tattoos
Enter fullscreen mode Exit fullscreen mode

πŸ“Š Early Results & Lessons Learned (2 Weeks In)

Current status:

  • βœ… 200 pages generated
  • βœ… 15 pages indexed by Google
  • βœ… Average content length: 520 words
  • βœ… Generation time: <15 seconds per page
  • βœ… Zero orphaned tattoos (thanks to recovery system)

What's working:

  1. User prompts are natural long-tail keywords

    • User: "minimalist geometric lion with sunset background"
    • Auto-targets: "minimalist lion tattoo", "geometric lion", "sunset tattoo ideas"
    • No keyword research neededβ€”users do it for me
  2. GPT-4o-mini Vision quality is excellent

    • 90% of content needs zero editing
    • Understands tattoo culture (placement, style, symbolism)
    • 10x cheaper than GPT-4o ($0.0015 vs $0.015)
  3. The orphan recovery system is essential

    • Caught 37 failures in the first week
    • Now runs as daily cron + manual check
    • Converts "oh shit" moments into "handled"

What I'm still figuring out:

  1. Indexing speed varies wildly

    • Some pages indexed in 24h, others take 7+ days
    • Testing XML sitemap optimization
  2. Category page optimization

    • Auto-generated intro content needs improvement
    • Testing different layouts for gallery grids

πŸš€ What's Next: Scaling to 10K Pages

Short-term (next 30 days):

  • Reach 1,000 generated pages
  • Target 100+ indexed pages
  • Track first keyword rankings

Mid-term (3 months):

  • Scale to 10,000 pages
  • Monitor conversion: organic traffic β†’ free users β†’ paid users
  • Add A/B testing for content formats

Long-term (6+ months):

  • 50K+ pages (depending on user growth)
  • Multi-language support (Spanish, Portuguese, French)
  • Artist marketplace integration

πŸ—οΈ Building in Public: BoomPath + TattooRed

I'm documenting this entire journey on Twitter (@PaulR_o_w) as part of my #buildinpublic and #rankinpublic experiment.

Here's the meta part that makes this interesting: I built BoomPath, an AI-powered SEO platform that goes beyond basic automation. BoomPath:

  • Analyzes your business to understand what you actually do
  • Finds strategic keywords (not just volumeβ€”intent and competition)
  • Monitors your competitors (what's working for them)
  • Writes SEO-optimized content tailored to your niche
  • Builds backlinks automatically through directory submissions and strategic outreach

It's not just another SurfSEO or AiSEO cloneβ€”it's the entire SEO workflow, automated.

And now I'm using BoomPath to rank TattooRed. So the stack is:

BoomPath (my SEO platform)
    ↓
TattooRed (AI tattoo generator)
    ↓
User-generated content
    ↓
GPT-4o enrichment
    ↓
Automatic SEO pages
    ↓
Rankings (tracked publicly on Twitter)
Enter fullscreen mode Exit fullscreen mode

It's turtles all the way down. 🐒

The beautiful irony? I'm proving BoomPath works by using it on TattooRed, and documenting both journeys publicly. If you want to follow the day-to-day wins, fails, and metrics, I'm posting regular updates on X.


πŸ› οΈ The Tech Stack

Core infrastructure:

  • Next.js 15 (App Router + Server Components)
  • Supabase (PostgreSQL + Auth + Storage)
  • Vercel (hosting + ISR)

AI services:

  • GPT-4o-mini Vision (content generation)
  • ModelsLab Flux (tattoo image generation)

SEO:

  • BoomPath (keyword research, competitor analysis, content strategy, backlink building)
  • Automated sitemap generation
  • Internal linking automation

Development tools:

  • Claude Code (coding assistant)
  • TypeScript
  • Tailwind CSS

πŸŽ“ Key Takeaways for Builders

If you're considering programmatic SEO with AI:

  1. User-generated content is gold

    • Let users create the initial input
    • AI enriches it into SEO-ready pages
    • Everyone wins: users get free value, you get content
  2. GPT-4o-mini Vision is underrated

    • 90% of GPT-4o quality at 10% the cost
    • Perfect for high-volume content generation
  3. Build recovery systems from day one

    • Background jobs WILL fail
    • Silent failures are the worst
    • Manual recovery buttons save your sanity
  4. Many-to-many category relations scale

    • One piece of content β†’ multiple entry points
    • Automatic internal linking
    • SEO multiplier effect
  5. Economics matter more than tech

    • $0.006 per page = infinite runway
    • Focus on user acquisition, not content costs
  6. Use your own products

    • Building BoomPath forced me to understand SEO deeply
    • Using it on TattooRed proves it works
    • Public accountability creates urgency

πŸ“ Meta: How This Article Was Written

Full transparency: I wrote this article with Claude's help.

My role:

  • Architecture decisions and implementation
  • Real-world disasters and solutions (orphan tattoos, API failures)
  • Metrics and learnings
  • Business strategy

Claude's role:

  • Article structure and flow
  • Technical explanation clarity
  • Editing for dev.to audience

The result: This article in 2 hours instead of 8-10 hours. That's AI as a thinking partner.


πŸ’¬ Let's Discuss

I'm curious about your experiences:

  • Have you built programmatic SEO systems?
  • What's your backup plan for AI API failures?
  • How do you handle silent background job failures?
  • Ever had an "oh shit" moment at 2 AM?

Drop your thoughts in the comments below. I'll reply to everyone.

Follow my build-in-public journey: Twitter @PaulR_o_w β€’ TattooRed.com πŸ”₯

Top comments (0)