TheKetoVibe.com
A content automation pipeline: LLM-drafted keto recipes and AI images published to a Laravel site and scheduled to Pinterest through its API.
The idea
The keto diet niche is massive on Pinterest — millions of monthly searches for keto recipes, keto meal plans, keto snack ideas. The problem is that creating recipe content at scale is tedious. You need:
- A tested recipe (ingredients, steps, timing)
- Nutrition information (calories, carbs, fat, protein)
- A high-quality food photo
- A Pinterest-optimized pin (2:3 aspect ratio, text overlay, SEO description)
- A recipe page on the website (structured data for Google)
Doing this manually for even 100 recipes takes weeks. I wanted to automate the entire pipeline so new recipes go live every few hours without human intervention.
How the content pipeline works
The pipeline runs as a Laravel scheduled job that executes every 4 hours. Each run generates one recipe end-to-end:
Step 1: Topic selection
A queue of pre-defined keto topics is maintained in the database. Topics are weighted by search volume and competition level. The system picks the topic with the highest priority_score (a function of search volume, competition, and days since last published):
SELECT topic, priority_score
FROM recipe_topics
WHERE category = 'keto'
AND last_published_at < NOW() - INTERVAL 7 DAY
ORDER BY priority_score DESC
LIMIT 1;
This ensures high-demand topics get covered more frequently and recently published topics rotate out.
Step 2: Recipe generation (OpenAI)
The prompt to GPT-4 is carefully structured to produce valid JSON:
You are a professional keto recipe developer. Generate a recipe for: {topic}
Return JSON with this exact structure:
{
"title": "string",
"servings": number,
"prep_time_minutes": number,
"cook_time_minutes": number,
"ingredients": [
{ "amount": "string", "unit": "string", "name": "string" }
],
"steps": ["string"],
"nutrition": {
"calories": number,
"net_carbs_grams": number,
"fat_grams": number,
"protein_grams": number
},
"tags": ["string"]
}
Rules:
- Net carbs must be under 5g per serving
- Use common ingredients (no exotic specialty items)
- Steps should be 5-8, each 1-2 sentences
- Include realistic nutrition values
The response is parsed and validated. If parsing fails (it happens about 5% of the time — GPT sometimes adds commentary before/after the JSON), the recipe is discarded and the next topic is picked.
Step 3: Image generation (Replicate SDXL)
This was the hardest part to get right. Early AI food images looked plastic, weirdly lit, or just wrong. After two weeks of prompt iteration, I landed on a combination that produces consistently good results:
prompt: "professional food photography, {recipe_title},
natural lighting, shallow depth of field,
rustic wooden table, overhead shot,
clean composition, appetizing,
Canon EOS R5, 50mm lens"
negative_prompt: "artificial, plastic, oversaturated,
text, watermark, logo, blurry,
deformed, ugly, cartoon, illustration"
The key insights:
- Specifying "Canon EOS R5, 50mm lens" makes the AI produce more realistic depth of field
- "Rustic wooden table" gives a consistent background style
- Avoiding words like "delicious" or "appetizing" actually improves results — those words somehow push the model toward exaggerated, unrealistic images
- The negative prompt is as important as the positive one
Each image takes about 15-20 seconds to generate on Replicate's A100 instances.
Step 4: Pinterest posting
The Pinterest API requires:
- A 2:3 aspect ratio image (1000x1500px)
- A title (max 100 characters)
- A description (max 500 characters) with keywords
- A link back to the recipe page
I resize the generated image to 1000x1500 using Sharp, overlay the recipe title as text (using a custom font), and post it via the Pinterest API. The description is generated by a simpler GPT prompt that focuses on Pinterest SEO keywords.
Step 5: Website publishing
The recipe is saved to the database with:
- Full recipe data (ingredients, steps, nutrition)
- The generated image
- SEO metadata (title, description, slug)
- Structured data (Recipe schema for Google)
- Published status
The page uses a server-rendered template with react-markdown for the description field and a custom recipe card component.
The tech
Laravel handles the pipeline orchestration. The scheduler runs every 4 hours, picks the next topic, and dispatches a job that runs the full pipeline. If any step fails, the job is retried twice before moving to the next topic.
Alpine.js on the frontend because the site is mostly static content — no need for Vue or React here. Alpine handles the interactive bits (category filters, search, mobile menu) without the overhead. The entire frontend JS bundle is under 15KB.
Image storage is on S3-compatible storage (DigitalOcean Spaces). Images are served through a CDN with aggressive caching — recipe images change once (when generated) and never again.
The image quality problem
This deserves its own section because I spent way too long on it.
The first 50 generated images were almost unusable. Food looked like plastic, colors were oversaturated, and the compositions were bizarre (a steak floating in mid-air, a salad in a bowl that doesn't exist).
What finally worked:
- Seed locking — once I found a seed that produced good compositions, I locked it and only varied the prompt. This eliminated the randomness problem
- Style consistency — specifying "Canon EOS R5, 50mm lens, natural lighting" gave every image a consistent photographic style
- Negative prompts — "artificial, plastic, oversaturated, text, watermark" prevented the most common failure modes
- Post-processing — every generated image gets a slight contrast boost and color correction via Sharp before being saved
The result: about 85% of generated images are good enough to publish without manual editing. The other 15% are either regenerated or skipped.
Results
- 500+ recipes published
- 10,000+ monthly Pinterest impressions
- Average time from generation to live: 5 minutes
- ~30% of traffic comes from Google (organic SEO)
- 85% of AI-generated images pass quality check on first attempt
- Zero manual content creation required after pipeline setup
Building something similar?
Tell me where you are and what's blocking you. I'll give you an honest read on how I'd approach it.