How to Auto-Generate SEO Meta Titles and Descriptions Using Local Ollama

You have 300 product, documentation, or blog pages ready to publish, but the metadata column is still blank. Writing every title and meta description by hand is slow; copying one pattern across the site produces repetitive results; and asking a local language model for “an SEO title” often returns extra commentary, invented claims, or inconsistent formatting.

A better approach is to treat metadata generation as a small data pipeline: give the model only page-specific evidence, require structured output, validate it in code, then review exceptions before publishing. Ollama is well suited to this workflow because its local API is available at http://localhost:11434/api after installation, and Ollama supports JSON-schema structured outputs. The Ollama and Google Search documentation referenced here was checked on September 13, 2026.

Why automatic SEO metadata goes wrong

Most failures come from the workflow around the model rather than from a single “bad prompt.” Common causes include feeding too little page content, letting the model answer in free-form prose, applying the same keyword formula to every URL, and publishing output without site-level duplicate checks.

There is also a persistent misconception about character limits. Google says there is no fixed length limit for a page’s <title> element or meta description; title links and snippets are truncated as needed, typically according to device width. Google may also generate a different title link or snippet from page content when its systems consider another representation more useful. See Google’s official guidance for title links and snippets and meta descriptions.

That means a script can enforce editorial length bounds for consistency, but it should not label those numbers as Google requirements. In the example below, 65 characters for the title and 165 for the description are internal guardrails only. Change them to match your CMS, brand, localization needs, and review process.

Step 1: Install Ollama and confirm the local API works

Ollama’s official quickstart lists macOS, Windows, and Linux support. After installation, download a local model. Ollama’s current documentation frequently uses gemma3 in API examples, so it is a convenient neutral example for this tutorial:

ollama pull gemma3

Then test the local endpoint:

curl http://localhost:11434/api/generate -d '{"model":"gemma3","prompt":"Reply with the word ready.","stream":false}'

The Ollama API documentation states that the local API is served at http://localhost:11434/api. Ollama also states that local access on this address does not require authentication. If your local service is not running, ollama serve is the documented CLI command for starting it. Avoid substituting a -cloud model if your goal is genuinely local inference.

Terminal workflow showing Ollama commands for checking the installation, pulling a model, listing local models, and starting a localhost service.
A terminal workflow for the setup stage: verify Ollama, pull a local model, confirm it appears in the local model list, and make sure the service is available on localhost. The model name and length figures shown are example workflow values rather than SEO requirements.

Choose a model according to your hardware

This article intentionally does not claim that one model is universally best for SEO metadata. Model availability and hardware fit change over time, and larger models require more memory and generally respond more slowly. Start with a model your machine can run comfortably, generate a small review set, then compare factual accuracy, uniqueness, latency, and consistency before processing thousands of pages.

Step 2: Force structured output instead of parsing prose

Free-form text is fragile for automation. A response such as “Here are three great options…” forces your program to guess where the actual title begins. Ollama’s structured outputs documentation supports passing a JSON schema in the format field. The documentation also recommends reusable schemas through Pydantic in Python and notes that a low temperature, such as 0, can improve determinism.

Install the two Python packages used in this example:

python -m pip install -U ollama pydantic

Then create generate_meta.py:

from ollama import Client
from pydantic import BaseModel, Field

MODEL = "gemma3"
client = Client(host="http://localhost:11434")

class SeoMeta(BaseModel):
    title: str = Field(min_length=10, max_length=65)
    description: str = Field(min_length=70, max_length=165)

SYSTEM = """
You create SEO metadata from the page content supplied by the user.

Rules:
- Accurately describe only what is present in the page content.
- Write a concise, descriptive title.
- Write a useful meta description that distinguishes this page from other pages.
- Do not repeat keywords unnaturally.
- Keep the same primary language as the page.
- Do not invent prices, dates, features, guarantees, statistics, or claims.
- The length bounds in the JSON schema are our editorial limits, not Google rules.
- Return only the structured result required by the schema.
"""

def generate_meta(url: str, source_title: str, h1: str, body_text: str) -> SeoMeta:
    prompt = f"""
URL: {url}
Current title: {source_title}
H1: {h1}

Page text:
{body_text[:12000]}

Create one meta title and one meta description for this page.
"""
    response = client.chat(
        model=MODEL,
        messages=[
            {"role": "system", "content": SYSTEM},
            {"role": "user", "content": prompt},
        ],
        format=SeoMeta.model_json_schema(),
        options={"temperature": 0},
    )
    return SeoMeta.model_validate_json(response.message.content)

if __name__ == "__main__":
    result = generate_meta(
        url="https://example.com/guides/clean-cast-iron",
        source_title="Cast Iron Guide",
        h1="How to Clean a Cast-Iron Skillet Without Damaging the Seasoning",
        body_text=(
            "This guide explains how to remove stuck food, wash a cast-iron "
            "skillet, dry it thoroughly, and apply a thin layer of oil..."
        ),
    )
    print(result.model_dump_json(indent=2))
Code editor showing a Python SEO metadata generator with an Ollama chat call, a page-content prompt, and JSON-oriented output instructions.
The generator should be explicit about the page evidence, the output fields, and the editorial constraints. In production, prefer Ollama’s JSON-schema structured output and application-side validation over relying on prompt wording alone.

Why the prompt includes the current title and H1

Google says its title-link system can consider the page’s <title>, main visual title, headings such as <h1>, og:title, prominent text, anchor text, and other signals. Your local generator does not need to reproduce Google’s system, but giving it the source title, H1, URL, and representative body text helps keep the proposed metadata aligned with the page readers will actually see.

Do not feed unrelated keyword lists and ask the model to “fit them in.” Google explicitly recommends descriptive, concise titles and warns against keyword stuffing and repeated boilerplate. For descriptions, Google says page-specific programmatic generation is appropriate for large database-driven sites when the descriptions are accurate, human-readable, and diverse.

Step 3: Run one page, then validate before you scale

Run the script:

python generate_meta.py

A successful result should be a JSON object that Pydantic can validate. More importantly, a human reviewer should be able to compare it with the source page and answer four questions quickly:

  • Does the title describe this exact page rather than the general website?
  • Does the description avoid claims, dates, prices, or features that are absent from the source content?
  • Would this metadata still make sense if read without the page body?
  • Is it distinct from metadata already used on nearby pages?
PowerShell window showing a Python metadata script returning a JSON object with a title and description.
Test one representative page first and inspect the structured result before starting a batch job. A parseable JSON response is necessary, but factual accuracy and page-level relevance still require validation.

Add retries for editorial failures, not just API errors

A schema can restrict data types and lengths, but it cannot guarantee that a sentence is truthful or sufficiently specific. Build retry logic around checks you can measure. For example, reject an empty field, a duplicate title, forbidden boilerplate, or a description that repeats the title almost word for word. If the page contains regulated or high-stakes claims, require human review rather than trying to automate confidence away.

Keep retry prompts narrow. Instead of regenerating everything with “make it better,” tell the model what failed: “The proposed title duplicates another page. Rewrite it using the unique topic in the H1 while keeping every claim grounded in the supplied content.” This makes failures easier to audit.

Step 4: Batch pages to CSV for review and publishing

Once the one-page test is reliable, store your inputs in a CSV with columns such as url, source_title, h1, and content. The content field should contain cleaned page text, not navigation menus, cookie notices, or thousands of unrelated footer links.

Add this batch logic below the generator function or place it in a separate script:

import csv

rows = []
seen_titles = set()

with open("pages.csv", newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        meta = generate_meta(
            url=row["url"],
            source_title=row.get("source_title", ""),
            h1=row.get("h1", ""),
            body_text=row["content"],
        )

        flags = []
        normalized = meta.title.casefold().strip()
        if normalized in seen_titles:
            flags.append("duplicate_title")
        seen_titles.add(normalized)

        rows.append({
            **row,
            "generated_title": meta.title,
            "generated_description": meta.description,
            "title_length": len(meta.title),
            "description_length": len(meta.description),
            "qa_flags": ",".join(flags),
        })

with open("seo_meta_output.csv", "w", newline="", encoding="utf-8") as f:
    fieldnames = list(rows[0].keys())
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(rows)

The output CSV can be reviewed in Excel, LibreOffice, a database import tool, or your CMS workflow. Do not automatically overwrite live metadata on the first run. Keep the original title and description alongside the generated proposal so reviewers can compare them and so rollback is straightforward.

Spreadsheet view with article titles in one column and generated SEO meta titles and descriptions in adjacent review columns.
A spreadsheet review queue makes bulk generation easier to audit: retain the source page identifier, generated title, generated description, lengths, and QA flags before publishing changes.

Make the batch pipeline safer

CheckWhat to flagWhy it matters
Schema validationMissing or invalid fieldsPrevents malformed output from entering the CMS.
Duplicate title checkSame normalized title on two URLsHelps keep pages distinguishable.
Language checkMetadata language differs from page languageGoogle recommends matching the primary language and writing system of the page.
Evidence checkNumbers, prices, dates, brands, or guarantees absent from sourceReduces hallucinated claims.
Boilerplate checkLong repeated site-wide phrasesGoogle advises against repeated boilerplate titles.
Editorial length checkOutside your chosen house rangeKeeps CMS output consistent; this is not a Google character-limit rule.
Human reviewStrategic, sensitive, high-traffic, or ambiguous pagesAutomation should surface exceptions, not hide them.

Common problems, from easiest fix to hardest

The API connection fails

First confirm that Ollama is running and that http://localhost:11434 is reachable. The official authentication documentation states that authentication is not required for the local API. If your script is accidentally pointing at https://ollama.com/api, that is a different hosted endpoint and requires authentication.

The response contains prose around the JSON

Use the schema through format=SeoMeta.model_json_schema() rather than asking for JSON only in natural language. Structured output is the right control layer for a production pipeline.

The titles all sound the same

Feed page-specific H1 text and meaningful body content, remove global navigation noise, and explicitly ask for a page-specific description. Then run a cross-page duplicate and similarity check. Repetition is often an input problem before it is a model problem.

The model invents features or statistics

Reduce the input to authoritative page text and strengthen the grounding rule: no factual claims beyond the supplied content. For important pages, compare generated assertions against structured source fields before approval. If the source itself is ambiguous, send the row to human review instead of repeatedly prompting the model to guess.

The model is too slow for thousands of pages

Measure before changing architecture. Log generation time per page, input size, model name, and failure rate. Truncate irrelevant body text, process stable page sections, and test a smaller local model if quality remains acceptable. The fastest model is not useful if its metadata creates more review work than it saves.

Optional: use an existing OpenAI-style integration

If you already have code written for an OpenAI-compatible client, Ollama documents partial OpenAI API compatibility at http://localhost:11434/v1/. That can reduce migration work. For a new local-only metadata tool, the native Ollama Python client is simpler and exposes structured-output examples directly in Ollama’s documentation.

How to self-check the finished system

Before calling the automation complete, create a review set containing different content types: a short article, a long guide, a product page, a category page, a page with numbers, and a page in every supported language. Run the same model and prompt across all of them, then verify the following checklist.

  • The local endpoint is localhost and the selected model is a local model, not a cloud-tagged model.
  • Every response validates against the schema without manual JSON cleanup.
  • No generated title is duplicated across the review set.
  • Every factual claim appears in the supplied page content.
  • The metadata uses the same primary language and writing system as the page.
  • The title is descriptive and concise without repeated keywords.
  • The description summarizes the specific page rather than the site as a whole.
  • Your internal length guardrails are labeled as editorial rules, not Google requirements.
  • The old metadata, generated proposal, QA flags, model name, and prompt version are saved for audit and rollback.
  • A reviewer knows which flags require manual approval before publishing.

Finally, remember what automation can and cannot control. A good <title> and meta description give search engines useful page-level signals, but Google can generate title links and snippets from other page content. The goal is therefore not to force a particular search-result rendering. The goal is to produce accurate, distinct, useful metadata at scale while preserving enough validation and review to catch mistakes before they reach production.

Leave a Comment

How to Auto-Generate SEO Meta Titles and Descriptions Using Local Ollama

How to Auto-Generate SEO Meta Titles and Descriptions Using Local Ollama

Build a local Ollama workflow that generates structured SEO titles and meta descriptions in bulk, validates output, and flags results for human review.

Modern Real Estate Listing Presentation PowerPoint Template Free: A Practical Seller-Ready Slide Blueprint

Modern Real Estate Listing Presentation PowerPoint Template Free: A Practical Seller-Ready Slide Blueprint

Use this free modern real estate listing presentation PowerPoint blueprint to organize pricing, marketing, property prep, agent value, and seller next steps.

Printable Construction Job Estimate Template for Excel: A Practical Setup for Small Contractors

Printable Construction Job Estimate Template for Excel: A Practical Setup for Small Contractors

Build a printable construction job estimate template in Excel with clear labor, materials, formulas, markup, exclusions, and print-ready client totals.

How to Generate Cinematic Lighting Prompts for Luma Dream Machine

How to Generate Cinematic Lighting Prompts for Luma Dream Machine

Build stronger Luma Dream Machine video prompts with a practical cinematic lighting formula, reusable recipes, examples, and a fast troubleshooting checklist.

Minimalist Investor Pitch Deck PowerPoint Template for Tech Startups: What to Include and How to Structure It

Minimalist Investor Pitch Deck PowerPoint Template for Tech Startups: What to Include and How to Structure It

Build a minimalist tech startup investor pitch deck in PowerPoint with the right slide order, clear design rules, traction metrics, and investor-ready appendix.

How to Stop Excel from Automatically Changing Numbers to Dates

How to Stop Excel from Automatically Changing Numbers to Dates

Stop Excel from turning codes, IDs, and fractions into dates. Use Text format, apostrophes, import controls, and current Microsoft 365 options.

How to Reduce Render Time in ComfyUI for an SDXL Workflow

How to Reduce Render Time in ComfyUI for an SDXL Workflow

Speed up ComfyUI SDXL renders by reducing sampling work, controlling resolution and batch size, simplifying nodes, using caching, and tuning VRAM and attention safely.

Free Rental Property Expense & Income Tracker Excel Template: Simple Landlord Workbook

Free Rental Property Expense & Income Tracker Excel Template: Simple Landlord Workbook

Build a practical rental property income and expense tracker in Excel with clear categories, formulas, monthly summaries, and tax-ready recordkeeping guidance.

How to Set Up a Private Local AI Knowledge Base for Personal Documents

How to Set Up a Private Local AI Knowledge Base for Personal Documents

Build a private local AI knowledge base with Ollama, AnythingLLM, local embeddings, and RAG. Learn the four-step setup, privacy checks, testing, and backups.

How to Fix “Context Window Exceeded” in Long Document Analysis Without Losing Key Evidence

How to Fix “Context Window Exceeded” in Long Document Analysis Without Losing Key Evidence

Fix context-window errors in long document analysis by trimming input, splitting files, using retrieval, and verifying that the final answer covers the source.