DEV Community

Mateo Ruiz
Mateo Ruiz

Posted on

Build a Programmatic SEO Pipeline with n8n, Google Sheets, AI & WordPress

Publishing 10 SEO articles a month is manageable. Publishing 40 is a production problem — and the bottleneck is rarely strategy. It's the mechanical work: pulling the next keyword, generating a draft, formatting content, filling WordPress meta fields one at a time, and tracking what went out.

This post walks through a full n8n workflow that automates that entire pipeline from a keyword row in Google Sheets to a fully formatted WordPress draft with SEO fields populated — with a human review gate before anything goes live.

Free workflow JSON at the end.

Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                   n8n SEO Workflow                       │
│                                                         │
│  [Schedule Trigger] OR [Webhook Trigger]                │
│           ↓                                             │
│  [Google Sheets] → read next row where status=pending   │
│           ↓                                             │
│  [Set node] → build AI prompt from keyword + template   │
│           ↓                                             │
│  [AI API Node] → returns structured JSON content        │
│           ↓                                             │
│  [Function Node] → parse + map fields                   │
│           ↓                          ↓                  │
│  [WordPress: Upload Image]   [WordPress: Create Draft]  │
│                    ↓                                    │
│         [Google Sheets: Update status → published]      │
│                    ↓                                    │
│         [Slack / Email: Notify reviewer]                │
│                    ↓                                    │
│              [Error Branch]                             │
│     Log failure → Alert team → Set status → failed      │
└─────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Stack

Tool Role Required?
n8n Workflow orchestration Yes
Google Sheets Keyword input + status tracking Yes
OpenAI / Claude / Gemini AI content generation Yes
WordPress REST API Draft creation + meta population Yes
Rank Math or Yoast (REST ext.) SEO meta fields Recommended
Slack or Email Review notification Recommended

Node-by-Node Breakdown

1. Trigger Configuration

Two supported trigger modes:

Schedule Trigger — runs on a cron cadence (daily, 3x/week, etc). Best for steady publishing velocity.

Cron: 0 8 * * 1,3,5   # Mon/Wed/Fri at 8am
Enter fullscreen mode Exit fullscreen mode

Webhook Trigger — on-demand generation triggered from a form, Slack command, or external system. Best for trending keyword response or client-driven requests.

Both trigger types feed into the same Google Sheets read node.

2. Google Sheets — Keyword Input

Recommended Column Structure

Column Purpose
keyword Target keyword for this article
slug URL slug (pre-defined or auto-generated)
content_type blog_post, faq_page, location_page
word_count Target word count for AI prompt
category WordPress category ID
status pending, in_progress, published, failed
draft_url Populated after draft creation
generated_at Timestamp of last run

Read Logic

Configure the Google Sheets node to filter for status = pending and return the first matching row only. Process one keyword per run to stay within API rate limits and keep runs predictable.

After reading the row, immediately update status → in_progress with a separate Sheets write node. This prevents duplicate processing if the workflow is triggered again before the first run completes.

// Set node — build processing context
{
  keyword: "{{ $json.keyword }}",
  slug: "{{ $json.slug }}",
  content_type: "{{ $json.content_type }}",
  word_count: "{{ $json.word_count || 1200 }}",
  category: "{{ $json.category }}"
}
Enter fullscreen mode Exit fullscreen mode

3. AI Content Generation

Prompt Structure

The quality of generated content is determined almost entirely by prompt quality. A vague prompt produces generic output regardless of which model you use.

You are an SEO content writer producing articles for [Site Name].

Target audience: [description]
Writing style: [conversational / authoritative / technical]
Tone: [direct, avoid filler phrases, no "In conclusion" openers]

Produce a complete SEO article for the following keyword:
Keyword: {{ $json.keyword }}
Content type: {{ $json.content_type }}
Target word count: {{ $json.word_count }}

Return ONLY valid JSON with this exact structure:
{
  "meta_title": "...",           // Under 60 chars, include keyword
  "meta_description": "...",     // Under 155 chars, include keyword, action-oriented
  "h1": "...",                   // Main headline, different from meta_title
  "outline": ["H2 1", "H2 2", "H2 3", "H2 4"],
  "body_html": "...",            // Full article in HTML, using <h2>, <p>, <ul> tags
  "focus_keyword": "...",        // Exact match target keyword
  "faq": [
    { "question": "...", "answer": "..." },
    { "question": "...", "answer": "..." }
  ]
}

No preamble. No markdown fences. Valid JSON only.
Enter fullscreen mode Exit fullscreen mode

Routing by Content Type

Use an n8n Switch node to route different content types to different prompt templates before the AI call:

content_type == "blog_post"     → Blog post prompt template
content_type == "faq_page"      → FAQ-focused prompt template
content_type == "location_page" → Location page prompt template
Enter fullscreen mode Exit fullscreen mode

Parsing the Response

// Function node — parse AI JSON response
const raw = $input.first().json.content[0].text;

// Strip any accidental markdown fences
const clean = raw.replace(/```
{% endraw %}
json|
{% raw %}
```/g, '').trim();

let parsed;
try {
  parsed = JSON.parse(clean);
} catch (e) {
  throw new Error('AI response was not valid JSON: ' + raw.substring(0, 200));
}

return [{
  json: {
    meta_title: parsed.meta_title,
    meta_description: parsed.meta_description,
    h1: parsed.h1,
    body_html: parsed.body_html,
    focus_keyword: parsed.focus_keyword,
    faq_schema: JSON.stringify(parsed.faq),
    outline: parsed.outline
  }
}];
Enter fullscreen mode Exit fullscreen mode

4. WordPress Draft Creation

Authentication

Use WordPress Application Passwords — available natively since WordPress 5.6. Create a dedicated application password for the n8n integration with the minimum role needed (Editor is sufficient for draft creation).

Store the base64-encoded username:app_password string in n8n credentials, not hardcoded in nodes.

// Authorization header value
'Basic ' + btoa('your_username:xxxx xxxx xxxx xxxx xxxx xxxx')
Enter fullscreen mode Exit fullscreen mode

Create Draft Post

// WordPress REST API — POST /wp-json/wp/v2/posts
{
  title: "{{ $json.h1 }}",
  content: "{{ $json.body_html }}",
  status: "draft",              // ALWAYS draft, never publish directly
  slug: "{{ $json.slug }}",
  categories: ["{{ $json.category }}"],
  meta: {
    // Rank Math fields
    "rank_math_focus_keyword": "{{ $json.focus_keyword }}",
    "rank_math_description": "{{ $json.meta_description }}",
    "rank_math_title": "{{ $json.meta_title }}"
  }
}
Enter fullscreen mode Exit fullscreen mode

Important: Always create as status: draft. Never set status: publish in the automation. The human review gate between draft creation and publish is what makes this workflow safe to run unattended.

Featured Image Upload

Upload the image to the WordPress media library before creating the post, then attach the returned media ID:

// POST /wp-json/wp/v2/media
// Headers: Content-Disposition: attachment; filename="post-image.jpg"
// Body: raw image binary

// Response includes: { id: 1234, ... }
// Use this ID in post creation: featured_media: mediaId
Enter fullscreen mode Exit fullscreen mode

Yoast SEO Fields (Alternative)

If the site runs Yoast instead of Rank Math:

meta: {
  "_yoast_wpseo_title": "{{ $json.meta_title }}",
  "_yoast_wpseo_metadesc": "{{ $json.meta_description }}",
  "_yoast_wpseo_focuskw": "{{ $json.focus_keyword }}"
}
Enter fullscreen mode Exit fullscreen mode

Note: Yoast meta fields via REST require the yoast/v1 endpoint or a plugin that exposes custom meta through the standard REST API. Test this before building the full workflow.

5. Status Update + Review Notification

After successful draft creation:

// Update Google Sheets row
status  "published"
draft_url  "https://yoursite.com/wp-admin/post.php?post=POST_ID&action=edit"
generated_at  new Date().toISOString()
Enter fullscreen mode Exit fullscreen mode

Then send a Slack notification:

{
  text: "📝 New SEO draft ready for review",
  blocks: [
    {
      type: "section",
      text: {
        type: "mrkdwn",
        text: `*Keyword:* ${keyword}\n*Title:* ${h1}\n*Word count:* ~${wordCount}`
      }
    },
    {
      type: "actions",
      elements: [{
        type: "button",
        text: { type: "plain_text", text: "Review Draft in WordPress" },
        url: draftUrl
      }]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

6. Error Handling

Every API call needs an error branch. Configure n8n's Error Trigger or use per-node error outputs.

// Error branch function node
const errorDetails = {
  timestamp: new Date().toISOString(),
  workflow: "SEO Content Pipeline",
  failed_node: $input.first().json.node || "unknown",
  error_message: $input.first().json.error?.message || "Unknown error",
  keyword: $('Google Sheets').first().json.keyword || "unknown",
  status_code: $input.first().json.error?.statusCode
};

return [{ json: errorDetails }];
Enter fullscreen mode Exit fullscreen mode

After logging to Sheets, send an alert to Slack and update the keyword row to status: failed.

Common Failure Points

AI API timeout on long content: Configure a 60s timeout, retry once before alerting. Long word count targets (2000+ words) increase timeout risk.

WordPress 401: Application password expired or revoked. Alert the team — this requires manual credential refresh. Do not retry.

WordPress 422 (Unprocessable Entity): Usually a malformed meta field or category ID that doesn't exist. Log the full request body alongside the error for debugging.

Google Sheets quota exceeded: Polling too frequently. Back off the trigger cadence or batch keyword reads.

JSON parse failure on AI response: The model occasionally wraps output in markdown fences despite instructions not to. The strip-and-parse pattern in step 3 catches most of these, but log the raw response for any that still fail.

What This Pipeline Can't Do

Verify factual accuracy. The AI model generates plausible content. For technical, medical, legal, or financial topics, plausible and accurate are not the same thing. Human review is mandatory, not optional.

Research the SERP. Without a SERP research step (DataForSEO, Google Search API), the AI generates against the keyword text alone — it doesn't know what's ranking, what intent signals exist, or what competing content looks like. Adding a research step before the AI call significantly improves output relevance.

Populate internal links. The model has no visibility into your site's existing content. Internal linking requires a separate step or manual addition during review.

Replace editorial judgment. The workflow handles production throughput. Which keywords to target, what angle to take, whether the output genuinely helps the reader — those remain human decisions.

Customisation Options

Swap the Keyword Source

Google Sheets is the default, but Airtable, Notion databases, and database webhooks all work as input sources with node reconfiguration. The key requirement is a status field to prevent duplicate processing.

Add SERP Research

Before the AI call, add a DataForSEO node or Google Search API call:

[Google Sheets: Read keyword]
       ↓
[DataForSEO: Get top 10 results for keyword]
       ↓
[Function: Extract titles, meta descriptions, H2s from results]
       ↓
[AI Node: Generate content informed by SERP data]
Enter fullscreen mode Exit fullscreen mode

This adds latency but significantly improves content relevance and competitive positioning.

Multi-Site or Multi-Client

Run separate workflow instances per client/site rather than conditional branching inside one workflow. Separation is easier to debug, easier to permission, and easier to hand off to a client if needed.

Approval Gate

For regulated industries or quality-sensitive publishing:

[Draft created in WordPress]
       ↓
[Send approval email with Approve/Reject links]
       ↓
[n8n Wait node — pauses workflow]
       ↓
[Webhook receives approval decision]
       ↓
[IF approved → update status → notify]
[IF rejected → flag for manual edit → notify]
Enter fullscreen mode Exit fullscreen mode

Rate Limits at Scale

Service Limit to Watch
OpenAI (GPT-4o) 10,000 TPM on Tier 1, scales with usage
Claude API 40 RPM on base tier
Google Sheets 300 reads/minute per project
WordPress REST API No hard limit, but server resources apply

For batches over 20 posts per run, add a delay node between keyword iterations (30–60 seconds). For very high volume, consider running multiple workflow instances on staggered schedules rather than one large batch.

Get the Free Workflow JSON

IT Path Solutions has published the complete n8n workflow — Google Sheets trigger, AI generation node with structured prompt, WordPress REST API publish node, Slack notification, status update logic, and error handling branch — all pre-connected.

Import into any n8n instance (self-hosted or Cloud), add credentials, run against one keyword first.

👉 Download the free n8n SEO workflow JSON + setup guide

Setup guide covers: Google Sheets OAuth, AI API key configuration, WordPress Application Password setup, Slack webhook, prompt customisation by content type.

When You Need a Custom Build

The template handles one site, one content type, standard WordPress. Common customisation needs:

  • Keyword lists with thousands of entries requiring batching, deduplication, and queue management
  • Custom post types or non-standard WordPress metadata beyond the default REST API
  • Multi-stage approval workflows for regulated industries
  • Integration with existing CRM, analytics platform, or project management tooling
  • Multi-site or multi-client deployments with separate keyword sources and review channels

IT Path Solutions builds custom n8n pipelines for content teams and agencies at scale.

Summary

The pipeline works because it eliminates the mechanical steps — keyword assignment, draft generation, meta field population, WordPress upload, reviewer notification — while keeping a human in the loop before anything goes live. The result is publishing throughput that scales with keyword volume, not headcount.

Full setup guide and workflow JSON: itpathsolutions.com/n8n-seo-automation-workflow

Running a version of this in production? What broke first? Drop it in the comments.

Top comments (0)