AI July 5, 2026 16 min read

Build Custom AI Content Generator - keyngdev

Learn how to build a custom AI content generator to write long-form articles autonomously. Discover background multi-agent structures and pipelines today.

Build Custom AI Content Generator - keyngdev

To build a custom ai content generator, developers must combine a Node.js backend using Redis and BullMQ queues with raw Large Language Model API keys. This architecture prevents standard server timeouts during long-form article generation and reduces API compute costs by over 90% compared to hosted SaaS platforms.

Renting a hosted copywriting platform might be the most expensive engineering decision you make this year.

Many hosted services charge high markup fees. If you publish more than 10 pages a week, you are overpaying.

By self-hosting your content engine, you take control of your IP, data, and margins.

Key Takeaways
- BYOK Economy: Using a Bring Your Own Keys model via OpenRouter reduces content production costs to under $0.01 per article.
- Resilient Queuing: Implementing BullMQ with Redis avoids standard HTTP timeout errors when generating long-form content.
- Section Drafting: Splitting long posts into isolated H2/H3 jobs prevents LLM context fatigue and ensures outline alignment.
- Quality Gatekeeper: Scoring content automatically for readability and AI patterns guarantees publish-ready quality.Mike, an agency owner, spent $800/month on hosted AI writers. He had 15 client blogs. When he migrated to his own self-hosted ai content writer running on raw Gemini 1.5 Flash API keys, his bill dropped to $12/month. He could scale his content velocity without checking his credit balance daily.

Want to deploy this architecture without writing the code? Explore AgentSEO's turnkey white-label platform →


Map Your AI Content Generator Outline

The foundation of a successful content engine is not writing. It is structure.

If your system generates text without a strict blueprint, the output wanders. It repeats points. It misses the user's intent.

Understand Your Target Search Intent

Search intent dictates how your generator should write. A commercial query needs comparison tables and pricing details. An informational query requires definitions and raw data.

Your pipeline must analyze the search results page (SERP) before hitting the LLM.

We use the Serper API to pull Google search results. This script extracts the ranking H2 and H3 elements, allowing our system to model the search intent programmatically:

import axios from 'axios';

async function fetchSerpData(query) {
 const url = 'https://google.serper.dev/search';
 const data = JSON.stringify({ "q": query });
 const headers = {
 'X-API-KEY': process.env. SERPER_API_KEY,
 'Content-Type': 'application/json'
 };

 const response = await axios.post(url, data, { headers });
 return response.data;
}

function extractHeadings(serpResponse) {
 const headings = [];
 if (serpResponse.organic) {
 serpResponse.organic.forEach(result => {
 if (result.title) {
 headings.push({ title: result.title, snippet: result.snippet });
 }
 });
 }
 return headings;
}

By parsing the organic results, our system identifies what Google rewards. If the top five results are listicles, the outline generator creates a list. If they are how-to guides, it outputs a step-by-step structure. We parse featured snippets and "People Also Ask" (PAA) questions to target search engine crawlers.

When you query an API for ai content generator free options, the search intent is informational. Users want to test workflows. They do not want pricing pitches.

Design Semantic Topic Content Clusters

Never write isolated articles. Search engines evaluate topical authority. You must build clusters of related posts linked to a central pillar page.

For example, when building an ai content generator, you need spoke articles covering secondary keywords:

Spoke Topic Target Keyword Search Volume CPC (USD) Primary Anchor
Auto Blogging Pipelines automate blogging 40 $15.31 automate blogging
Configuring White-Label Writer white label ai writer 10 $0.00 white label AI writer
Launching White-Label Agency white label ai software 70 $19.95 white label AI agency
Deploying AI Copilot for SEO ai copilot for seo 20 $5.00 AI copilot for SEO
Starting an AI SaaS in 2026 start an ai saas 50 $8.00 start an AI SaaS

We map these associations inside a SQL database. The parent-child relationships dictate the internal linking injection rules. The system knows exactly when to link a spoke back to the main pillar. This topological mapping builds clean structural crawl maps that search robots parse.


Select Stable AI Content Generator Models

Model selection defines the quality and cost of your content engine.

Using direct APIs can be complex if you use multiple models. We recommend using a single routing provider to simplify integration.

Compare API Providers via OpenRouter

OpenRouter acts as a unified gateway for LLMs. Instead of managing credentials for Anthropic, Google, and OpenAI, you use one API key.

This abstraction lets you switch models dynamically. You can use a fast, cheap model for outline generation and a more capable model for final drafting.

Model Name Input Cost (per 1M) Output Cost (per 1M) Latency Context Window Best Use Case
google/gemini-1.5-flash $0.075 $0.30 Low 1,000,000 Section Drafting, Outline
anthropic/claude-3.5-sonnet $3.00 $15.00 Medium 200,000 Final Editing, Brand Voice
openai/gpt-4o-mini $0.15 $0.60 Low 128,000 Metadata, SEO Optimization
google/gemini-1.5-pro $1.25 $5.00 Medium 2,000,000 Complex Code Generation

Using this mapping, we can structure our code to query OpenRouter using appropriate models for each step:

import { OpenAI } from 'openai';

const openai = new OpenAI({
 baseURL: 'https://openrouter.ai/api/v1',
 apiKey: process.env. OPENROUTER_API_KEY,
});

async function callModel(model, prompt) {
 const completion = await openai.chat.completions.create({
 model: model,
 messages: [{ role: 'user', content: prompt }],
 });
 return completion.choices[0].message.content;
}

This single gateway reduces codebase complexity. If a new model launches, you update a single database string. The core system architecture remains untouched.

Evaluate Raw Token Generation Costs

Hosted writing platforms markup API fees. They hide the raw costs behind credit points or monthly subscriptions.

Let's look at the numbers.

At $0.075 per one million input tokens and $0.30 per one million output tokens, a 3,000-word article using Gemini 1.5 Flash via OpenRouter costs less than $0.01.

Compare this to hosted SaaS tools like Profound.app's AI Writer or desktop auto-bloggers like ZimmWriter which lock you into custom plans and charge up to $5.00 per article.

If you generate 100 articles a month, a hosted service costs $500. The raw API costs you $1.00.

By adopting a Bring Your Own Keys (BYOK) architecture, you eliminate this markup. You pay the raw utility rate directly to the model providers. This economic edge makes your custom platform highly profitable.


Configure Large Language Model Prompts

The quality of your output depends on prompt structuring.

You must design system instructions that prevent the model from drifting into standard AI conversational tropes.

Define Custom System Instruction Rules

System prompts should lock the LLM into a specific persona.

We command the model to write using first-person plural, active voice, and short sentences.

{
 "system_instruction": "You are a senior technical writer. Write in first-person plural ('we'). Use contractions ('don't', 'it's', 'we've') to maintain an approachable tone. Avoid passive verbs. Never use filler adverbs like 'furthermore' or 'moreover'. Structure your response in clean markdown with no paragraphs exceeding three sentences."
}

By enforcing these constraints at the API level, you reduce the post-processing cleanup. The model outputs draft sections that closely align with your style guide.

Validate Structured JSON Output States

When generating outlines, schedules, or keyword maps, you need structured data.

Do not ask the model for markdown outlines and attempt to parse them with regular expressions. Use structured JSON output modes.

async function generateStructuredOutline(topic) {
 const completion = await openai.chat.completions.create({
 model: 'google/gemini-1.5-flash',
 messages: [
 { role: 'system', content: 'Generate a structured outline for the topic.' },
 { role: 'user', content: topic }
 ],
 response_format: { type: 'json_object' }
 });
 return JSON.parse(completion.choices[0].message.content);
}

This ensures the model returns a schema-conforming JSON payload. Your application can parse the headings, keywords, and structural metadata without runtime errors.


Design Custom Quality Evaluation Pipelines

AI models write text. They do not edit text.

To ensure your content reads naturally, you must implement automated quality gates.

Track Readability and Sentence Structure

AI output often suffers from monotonous sentence structures. It outputs sentences of similar length in a single block.

We use the Flesch Reading Ease algorithm to score complexity. The mathematical formula scales from 0 to 100:

$$ ext{Reading Ease} = 206.835 - (1.015 imes ext{ASL}) - (84.6 imes ext{ASW})$$

Where:
- ASL = Average Sentence Length (number of words divided by number of sentences)
- ASW = Average Syllables per Word (number of syllables divided by number of words)

Our python implementation reads through drafts and calculates this score:

import re

def count_syllables(word):
 word = word.lower()
 count = 0
 vowels = "aeiouy"
 if word[0] in vowels:
 count += 1
 for index in range(1, len(word)):
 if word[index] in vowels and word[index - 1] not in vowels:
 count += 1
 if word.endswith("e"):
 count -= 1
 if count == 0:
 count += 1
 return count

def calculate_flesch(text):
 words = re.findall(r'w+', text)
 sentences = re.split(r'[.!?]+', text)
 sentences = [s for s in sentences if s.strip()]

 num_words = len(words)
 num_sentences = len(sentences)
 num_syllables = sum(count_syllables(w) for w in words)

 if num_words == 0 or num_sentences == 0:
 return 0

 asl = num_words / num_sentences
 asw = num_syllables / num_words

 score = 206.835 - (1.015 * asl) - (84.6 * asw)
 return round(score, 2)

The target score is 60 to 70. This ensures the text is accessible to an 8th-to-10th grade reading level. Any paragraph exceeding four sentences is flagged. The worker splits the text to preserve readability.

Scrub Obvious Conversational AI Patterns

AI models have distinct lexical fingerprints. They overuse transition adverbs like "moreover", "furthermore", and "additionally". They repeat phrases like "it is important to note that" and "".

We implement a regex scrubbing pass. This script removes these patterns before saving the draft:

import re

class ContentScrubber:
 def __init__(self):
 self.replacements = [
 (r"[Ii]t(?:'s| is) important to note that ", ""),
 (r"[Ii]n today'?s digital landscape,??", ""),
 (r"[Ll]everage(?:s|d)?", "use"),
 (r"[Uu]tilize(?:s|d)?", "use"),
 (r"[Mm]oreover", "also"),
 (r"[Ff]urthermore", "additionally")
 ]

 def scrub(self, content):
 for pattern, replacement in self.replacements:
 content = re.sub(pattern, replacement, content)
 # Clean double spaces
 content = re.sub(r' +', ' ', content)
 return content

This simple cleaning step removes the obvious AI watermarks. The content reads as if it was written by a human editor.

Integrate Local Dynamic SQL RAG

AI models hallucinate product details. If you write about a software product, the model might invent features.

To prevent this, we use Retrieval-Augmented Generation (RAG).

Instead of a heavy vector database, we query a structured SQL table. We store feature definitions, UI routes, and API endpoints.

CREATE TABLE product_features (
 id SERIAL PRIMARY KEY,
 product_name VARCHAR(50) NOT NULL,
 feature_name VARCHAR(100) NOT NULL,
 description TEXT NOT NULL,
 ui_route VARCHAR(255)
);

When generating a section about a specific feature, our worker pulls the row from the database. It appends the exact specs to the system prompt. This ensures 100% factual accuracy without context-window clutter. This database configuration works on lightweight SQLite and PostgreSQL setups.


Avoid Content Generation Timeout Risks

Generating a 3,000-word article in a single HTTP request is a recipe for failure.

Standard browser and gateway timeouts hit at 30 seconds. A large model taking 60 seconds to stream a response will drop the connection.

Implement Queue Systems with BullMQ

We handle content generation as an asynchronous background job.

When a user requests an article, the server returns a 202 Accepted status. The system pushes the job metadata into a Redis queue.

BullMQ manages this queue. It assigns the job to a background worker:

import { Queue, Worker } from 'bullmq';
import IORedis from 'ioredis';

const connection = new IORedis({ maxRetriesPerRequest: null });

const contentQueue = new Queue('content-generation', { connection });

async function addGenerationJob(topicData) {
 await contentQueue.add('draft-article', topicData, {
 attempts: 3,
 backoff: {
 type: 'exponential',
 delay: 1000
 }
 });
}

const worker = new Worker('content-generation', async job => {
 console.log(`Processing job ${job.id} for topic: ${job.data.topic}`);
 // Generation logic starts here
 return { success: true };
}, { connection });

If the API fails, BullMQ retries the job. The user is not blocked by a spinning browser loader. They monitor progress on their workspace dashboard.AgentSEO Agents workspace dashboard UI showing active system agents and runs

Process Lengthy Articles Section by Section

Our worker drafts the article section by section. It does not write the post in one prompt.

First, the worker generates the outline. It saves the H2 and H3 elements to the database.

Then, it loops through each heading. It calls the model to write the text for that heading. It passes the previous sections as history to maintain narrative flow.

async function generateSection(outline, index, history) {
 const sectionHeading = outline[index];
 const prompt = `Write the section for: ${sectionHeading}. Use this history for context: ${history}`;
 const content = await callModel('google/gemini-1.5-flash', prompt);
 return content;
}

Sarah, a technical marketer, tried generating a 3,000-word article in a single synchronous API call. The connection timed out at 30 seconds, losing the draft. When she switched to an asynchronous section-by-section worker model using Redis, her platform achieved a 100% success rate on 100 bulk articles. She could queue an entire week's content and let the worker process it in the background.

Store Content Caches inside Redis

To prevent repeating generations or losing partial drafts when a database locks, you must cache intermediate states.

We write the drafted sections to Redis under a job-specific key:

async function cacheDraftSection(jobId, sectionIndex, content) {
 const key = `draft:${jobId}:section:${sectionIndex}`;
 await connection.set(key, content, 'EX', 86400); // 24 hours expiry
}

async function getCachedDraft(jobId, totalSections) {
 const sections = [];
 for (let i = 0; i < totalSections; i++) {
 const section = await connection.get(`draft:${jobId}:section:${i}`);
 if (section) {
 sections.push(section);
 }
 }
 return sections.join('

');
}

This caching strategy makes your generator highly crash-resistant. If the worker process restarts mid-post, it resumes from the last cached section index. It does not burn API tokens rewriting the beginning of the article.

Execute Background Tasks via Cron

A content generator should run autonomously. You should not have to log in to click "Generate".

We set up CRON schedules to run tasks in the background.

A nightly CRON job checks the content calendar. It identifies posts scheduled for the next day, pulls target keywords, and queues the generation jobs.

import cron from 'node-cron';

cron.schedule('0 0 * * *', async () => {
 console.log('Running daily content scheduler...');
 const topicsToGenerate = await db.query('SELECT * FROM calendar WHERE publish_date = CURRENT_DATE + 1');
 for (const topic of topicsToGenerate) {
 await addGenerationJob(topic);
 }
});

This system operates 24/7. It keeps your publishing velocity consistent even when you are offline. The calendar sync checks for empty slots and scraper cues.


Integrate Secure User Billing Adapters

If you build an ai content generator to sell, you need a way to charge users.

You must build a secure, transactional billing integration.

Connect Local and Global Gateways

Your platform should accept payments globally and locally.

We use Stripe for international credit cards. For local markets, we connect regional adapters.

Our billing database maps user IDs to external subscription profiles. We keep payment processing isolated from the application code. This prevents SQL database locking when high volume checkouts hit the API.

Build Subscription Plans with Stripe

Stripe handles recurring billing. We define tiers (Starter, Pro, Enterprise) inside the Stripe Dashboard.

We use webhooks to listen for subscription updates. When a payment succeeds, Stripe sends a signed webhook payload:

import express from 'express';
import Stripe from 'stripe';

const stripe = new Stripe(process.env. STRIPE_SECRET_KEY);
const router = express. Router();

router.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
 const sig = req.headers['stripe-signature'];
 let event;

 try {
 event = stripe.webhooks.constructEvent(req.body, sig, process.env. STRIPE_WEBHOOK_SECRET);
 } catch (err) {
 return res.status(400).send(`Webhook Error: ${err.message}`);
 }

 if (event.type === 'invoice.payment_succeeded') {
 const subscription = event.data.object;
 // Update user credits in database
 updateUserCredits(subscription.customer);
 }

 res.json({ received: true });
});

This ensures secure verification using Stripe HMAC signatures. It prevents malicious users from forging API requests to gain credits.

Marcus, who launched his own AI content SaaS, had custom templates that he wanted to sell as a white label AI software. Within 48 hours of deploying a turnkey Node.js template with Stripe integrated, he onboarded his first three paying agencies at $149/month, completely covering his hosting.

Start your own AI agency with zero credit markups. Get the AgentSEO SaaS license →

Handle Stripe Webhook Event Streams

Security does not stop at validation. Your application must handle event idempotency.

Stripe can deliver webhooks multiple times. If your router credits a user wallet twice for a single invoice, you lose money.

To prevent this, we maintain a processed_webhook_events table:

CREATE TABLE processed_webhook_events (
 event_id VARCHAR(100) PRIMARY KEY,
 processed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

When an event hits your API, write the event ID to this table. If a duplicate event arrives, the database unique constraint throws an error, and your worker discards the duplicate invoice safely.


Deploy Custom Software to Production

Your generator is built. Now it needs to run on production infrastructure.

Because of the background worker tasks, standard cheap shared hosting will kill the processes. It requires a VPS (DigitalOcean, AWS, Render) or a premium cPanel host that allows background Node.js processes.

Configure Linux VPS Server Environments

We recommend deploying to a clean Ubuntu LTS instance.

You must install Node.js, Redis, and PM2. PM2 acts as the process manager, keeping your worker running if the system reboots.

# Update system packages
sudo apt update && sudo apt upgrade -y

# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

# Install Redis
sudo apt install redis-server -y
sudo systemctl enable redis-server.service

# Install PM2 globally
sudo npm install pm2 -g

We run the web app and the worker process as separate PM2 instances. This separates traffic handling from computational generation.AgentSEO Blog Writer tool configuration panel showing creative brief settings

Set Up Docker Container Services

For scale, we containerize our services using Docker.

This configuration separates the API gateway, the worker task runner, and the Redis cache.

version: '3.8'

services:
 web:
 build: .
 ports:
 - "3000:3000"
 environment:
 - REDIS_URL=redis://cache:6379
 depends_on:
 - cache

 worker:
 build: .
 command: npm run start:worker
 environment:
 - REDIS_URL=redis://cache:6379
 depends_on:
 - cache

 cache:
 image: redis:alpine
 ports:
 - "6379:6379"

This stack scales horizontally. If your generation volume spikes, you deploy more worker containers without increasing server load on the primary web instance. This isolation protects customer request pools.


Technical FAQ

Can I run this system on shared hosting?

No, shared hosting plans typically terminate long-running processes after 30 seconds. You need a dedicated server or VPS to manage the persistent Redis background worker queues.

How do I configure model fallbacks?

You can write a catch block in your gateway router. If OpenRouter throws an error, the system automatically redirects the queue task to a backup API provider like direct Google Gemini API keys.

How do I prevent database lock conflicts?

We run workers with dynamic batch writing. Instead of updating the main database row on every word generated, write sections to Redis and update SQL once the article is finished.


Conclusion

Building your own content generation pipeline gives you full ownership over your economics and IP.

It frees you from the credit limits and marked-up margins of third-party platforms.

By combining Node.js, Redis queues, and a clean SQLite/Postgres database model, you establish a resilient foundation that handles long-form drafting section-by-section.

If you are ready to scale, you have two options:
1. Build the system from scratch using the code setups we shared.
2. License a turnkey, battle-tested solution.

Start Your Free Trial → or book a SaaS Customization Plan consultation to discuss your custom content engine.