Pasting URLs into ChatGPT one at a time to write meta descriptions is a process that does not scale, does not enforce character limits reliably, and leaves no audit trail of what got published where. If you are doing this for more than a handful of pages, you are spending hours on a task that an n8n workflow can run unattended, with consistent character counts, brand voice rules baked in, and a direct write to your CMS — no copy-paste required.
This post walks through a working n8n workflow that pulls page content, generates SEO-compliant meta descriptions through an LLM API call, validates the output against length and quality rules, and pushes the result straight into WordPress (or any CMS with an API) — all without a human touching ChatGPT’s web interface.
⚡ Direct Answer
Manually generating meta descriptions in ChatGPT breaks down past about 20-30 pages because there is no enforced character limit, no consistency check across batches, and no automated way to push the result into your CMS. The fix is an n8n workflow that reads your page list, scrapes or pulls existing content, sends it to an LLM API with a strict prompt template (including character count and brand voice rules), validates the response programmatically, and writes it directly to your CMS via API. This post covers the exact workflow structure, the prompt template that keeps output within Google’s display limits, and the validation step that catches the failures ChatGPT’s chat interface lets through silently.
The chat interface version of this task feels productive in the moment. You paste a URL or some page content, ChatGPT writes a description, you copy it, you paste it into your CMS field, you move to the next page. For five pages this is fine. For five hundred, the cracks show up fast, and they show up in ways that are easy to miss until a content audit catches them months later.
| Problem | What Actually Happens |
|---|---|
| No enforced character limit | ChatGPT will happily write a 187-character meta description even when told to stay under 160. Google truncates it in search results with no warning to you. |
| Inconsistent voice across sessions | Each new chat session drifts slightly in tone, formality, and structure — especially if you’re not pasting the exact same system instructions every time, which most people aren’t. |
| No deduplication check | ChatGPT has no memory of what it wrote for page 40 when it’s writing page 240. Near-duplicate meta descriptions across a site are a real and common outcome of manual batch work. |
| Manual copy-paste introduces errors | Wrong description pasted to the wrong page, partial copies, accidental whitespace or formatting characters carried over from the chat interface. |
| No audit trail | There’s no record of what was generated, when, for which page, or what the original page content was at generation time — which matters if you ever need to regenerate after a content update. |
| Does not scale past a few dozen pages | The time cost is linear and entirely manual. Doubling your page count doubles your hours with zero efficiency gain. |
None of these problems are about ChatGPT being bad at writing meta descriptions — it is generally fine at the actual writing task. The problems are entirely about using a conversational chat interface for what is fundamentally a batch data-processing job. That is the wrong tool for the job, not a quality issue with the model.
The workflow below handles the full pipeline: read a list of target pages, pull their current content, generate a meta description through an LLM API, validate the output against your rules, and write the approved result to your CMS. Here is the structure end to end.
| Step | n8n Node Type | What It Does |
|---|---|---|
| 1 | Google Sheets / Airtable Trigger or Read | Reads the list of target URLs or post IDs to process, plus any existing meta descriptions for comparison |
| 2 | HTTP Request or WordPress Node | Pulls the current page content (title, body text, existing meta if any) directly from the CMS via API |
| 3 | Function Node | Strips HTML, truncates body content to a reasonable token budget, and assembles the prompt payload |
| 4 | HTTP Request (LLM API) | Sends the structured prompt to the API (OpenAI, Anthropic, or your provider of choice) with strict formatting instructions |
| 5 | Function Node (Validation) | Checks character count, flags banned phrases, checks for near-duplicate against previously generated descriptions in the batch |
| 6 | IF Node | Routes passing descriptions to the write step; routes failures to a regeneration loop or a manual review sheet |
| 7 | WordPress Node / HTTP Request | Writes the approved meta description directly to the CMS field (Yoast, Rank Math, or custom meta field depending on your setup) |
| 8 | Google Sheets Append | Logs the result — original content snapshot, generated description, character count, timestamp — for audit purposes |
The single biggest improvement over freestyle ChatGPT prompting is forcing structured, constrained output through the API rather than asking nicely in a chat message. Asking ChatGPT in conversation to “keep it under 160 characters” is a request the model treats as a soft guideline. Asking the same thing through an API call with explicit formatting constraints and a validation step downstream turns it into an enforced rule, because you check it programmatically rather than trusting the model’s adherence.
System prompt:
You write SEO meta descriptions. Rules, all mandatory:
1. Length: 140-155 characters, never exceeding 155.
2. Include the primary keyword naturally within the first 110 characters.
3. End with a clear value proposition or implicit call to action — no ellipsis, no trailing "...".
4. No emojis, no exclamation points, no title case.
5. Do not repeat phrasing patterns from previous descriptions in this batch (provided below as context).
6. Output ONLY the meta description text. No preamble, no quotation marks, no explanation.
User prompt template:
Page title: {{title}}
Primary keyword: {{keyword}}
Page content summary: {{content_excerpt}}
Previously generated descriptions in this batch (avoid repeating structure): {{recent_batch_samples}}
The fourth field — passing in a sample of recently generated descriptions from earlier in the same batch run — is what actually solves the near-duplicate problem. The model sees a handful of its own recent outputs as context and is explicitly told not to repeat their structure. This single addition is responsible for most of the variety improvement compared to running each page through a fresh, context-free prompt.
Even with a tightly constrained prompt, LLMs do not perfectly follow character limits or formatting rules every time. This is exactly why the workflow needs a programmatic check rather than trusting the model’s output at face value — the validation Function node is what makes this approach reliable at scale in a way that manual chat usage never is.
// n8n Function node — validation logic
const description = $input.first().json.generated_text.trim();
const length = description.length;
const keyword = $input.first().json.primary_keyword.toLowerCase();
const issues = [];
if (length < 120 || length > 155) {
issues.push(`Length out of range: ${length} characters`);
}
if (!description.toLowerCase().includes(keyword)) {
issues.push('Primary keyword missing');
}
if (/[!"\u2026]|"/.test(description)) {
issues.push('Contains banned punctuation (exclamation, ellipsis, or quotes)');
}
return [{
json: {
description,
length,
passed: issues.length === 0,
issues
}
}];
Anything that fails this check routes through the IF node back into a regeneration loop (capped at two retries to avoid infinite loops on a stubborn page) or, after exhausting retries, into a manual review sheet rather than getting silently published with a broken meta description. This routing is the entire reason the workflow is more reliable than a human eyeballing ChatGPT’s output and assuming it’s fine.
Worth noting: Google does not strictly enforce a hard character cutoff — it’s based on pixel width, and the practical safe range most SEOs target is roughly 120-155 characters to avoid truncation across both desktop and mobile result rendering. The validation script above uses that range rather than a single hard number, which is more realistic than the “exactly 160 characters” advice you’ll see repeated in a lot of older SEO content.
The final write step depends on which SEO plugin your WordPress site uses, since meta description data lives in different custom fields depending on the plugin.
| Plugin | Meta Field Key | n8n Approach |
|---|---|---|
| Yoast SEO | _yoast_wpseo_metadesc | WordPress node’s “Update” operation on the post, targeting custom field meta via the REST API’s meta object (requires the field to be registered as visible to the REST API, or a small companion plugin/snippet to expose it) |
| Rank Math | rank_math_description | Same approach — HTTP Request node calling the WordPress REST API directly, since Rank Math’s field is not exposed by default either |
| Custom/headless CMS | Varies | Direct API call to whatever endpoint your CMS exposes for SEO metadata — usually more straightforward than WordPress since these fields are typically first-class API citizens |
The most common setup hurdle: by default, the WordPress REST API does not expose Yoast or Rank Math’s meta description fields for reading or writing, even with valid authentication. You need to register the field with register_meta() and set show_in_rest to true, either through a small custom plugin or a snippet in your theme’s functions file. Both Yoast and Rank Math also offer their own REST API extensions in some versions that handle this more cleanly — worth checking your specific plugin version before writing custom registration code.
Do not point this workflow at your entire site and let it run unattended on the first execution. The practical approach:
| Factor | Manual ChatGPT (chat interface) | n8n Workflow |
|---|---|---|
| Time for 200 pages | 10-14 hours of active copy-paste work | Roughly 1-2 hours of unattended runtime, plus initial setup time (one-time cost) |
| Character limit enforcement | Soft suggestion to the model, frequently ignored | Programmatically validated, with automatic regeneration on failure |
| Consistency across batch | Drifts across sessions | Maintained via context-aware prompting (recent samples included in each call) |
| Audit trail | None unless manually logged | Automatic log of every generation, original content, and timestamp |
| CMS integration | Manual copy-paste per page | Direct API write, no manual step |
| Repeatability for content updates | Start over from scratch each time | Re-trigger the same workflow on updated content |
ChatGPT itself is not bad at the writing task — the problem is using its conversational chat interface for what is a batch data-processing job. The chat interface does not enforce character limits programmatically, has no memory of previous outputs in a batch to prevent near-duplicate descriptions, and requires manual copy-paste into your CMS for every single page. None of those limitations exist when the same model is called through an API inside a structured workflow with validation steps.
Google’s display limit is based on pixel width rather than a fixed character count, but the practical safe range most SEOs target is roughly 120-155 characters to avoid truncation across desktop and mobile search results. Older advice citing an exact 160-character cutoff is outdated and does not reflect how Google actually renders the snippet.
Yes. The same logic — pull content, call an LLM API with a structured prompt, validate the output, write to the CMS — works in Make.com, Zapier, or a custom script. n8n is a strong fit because of its native Function nodes for writing validation logic directly in JavaScript without needing a separate code execution service, which keeps the entire pipeline in one tool.
Both plugins store their SEO metadata as custom post meta fields, and WordPress does not automatically expose custom meta fields through the REST API for security reasons. You need to explicitly register the field with register_meta() and set show_in_rest to true, either via a small custom plugin or a functions.php snippet, before an external tool like n8n can read or write to it.
Pass a sample of recently generated descriptions from earlier in the same batch run into the prompt as context, with an explicit instruction not to repeat their structure or phrasing pattern. This gives the model awareness of what it has already produced, which a fresh, context-free prompt for each page does not provide. It significantly reduces — though does not completely eliminate — repetitive patterns across a large batch.
Not if the validation layer is doing its job. The validation Function node should catch hard failures (length violations, missing keywords, banned punctuation) automatically and route them to regeneration or manual review rather than letting them pass through. For passing descriptions, spot-checking a sample per batch is usually sufficient rather than reviewing every single page, since the programmatic checks handle the failure modes that matter most.
Bottom line: ChatGPT’s chat interface was never built for batch SEO production work, and using it that way produces inconsistent character lengths, near-duplicate descriptions, and zero audit trail — problems that have nothing to do with the model’s writing quality and everything to do with the tool you’re running it through. An n8n workflow with a structured prompt, programmatic validation, and a direct CMS write solves all of it, and it scales to thousands of pages with the same setup that handles your first batch of ten.
I built an n8n workflow that uses LLM embeddings to find internal linking opportunities across…
I deleted a production workflow once. Not archived it — deleted it. Three months of…
Claude can call APIs and MCP connectors now, so a one-prompt dashboard is a real…
Server-Side Google Tag Manager lets you relay conversion events to Meta through a server you…
TL;DR For WordPress content curation, Gemini is usually the better choice when you care most…
A ground-level guide to RPA and business automation in 2026 — covering where Droven.io fits…