seo meta tags

How to Generate SEO Title Tags and Meta Descriptions at Scale Using Google Sheets and AI

On this page

    Managing hundreds of title tags and meta descriptions manually is inefficient and inconsistent. A simple automated system using Google Sheets, Apps Script, and the OpenAI API removes the work entirely. This tutorial walks through every step required to build a repeatable, scalable metadata generator.

    1. Prepare the Google Sheet

    Create a new Google Sheet with three columns:

    • Column A: URL
    • Column B: Title Tag
    • Column C: Meta Description

    Paste your full list of URLs starting from A2.

    Example:

    A1: URL
    B1: Title Tag
    C1: Meta Description
    A2: https://www.example.com/page-1/
    A3: https://www.example.com/page-2/

    This sheet will become the input and output for the automation.

    2. Open Apps Script

    Inside your Google Sheet:

    • Go to Extensions → Apps Script
    • Delete any existing code in Code.gs

    You’ll be writing a script that loops through all URLs, sends them to OpenAI, and writes back optimised metadata.

    3. Add the OpenAI API Script

    Paste this entire script into Code.gs.
    Replace ‘sk-xxxx’ with your real OpenAI API key.

    const OPENAI_API_KEY = ‘sk-xxxx’; // Replace with your new regenerated API key

    function generateMetaForUrl(url) {
    if (!url) return { title: ”, description: ” };

    const prompt = `
    You are an SEO specialist.

    Task:
    Given this URL, generate:
    1) An SEO title tag (~50–60 characters).
    2) A meta description (~130–150 characters).

    Rules:
    – Australian English
    – Be concise
    – Avoid keyword stuffing
    – Return ONLY valid JSON:
    {“title”: “…”, “description”: “…”}

    URL: ${url}
    `;

    const payload = {
    model: “gpt-4.1-mini”,
    messages: [
    {
    role: “system”,
    content:
    “You write SEO titles and meta descriptions that are short, clear and follow character limits.”
    },
    { role: “user”, content: prompt }
    ],
    temperature: 0.3
    };

    const options = {
    method: “post”,
    contentType: “application/json”,
    headers: {
    Authorization: “Bearer ” + OPENAI_API_KEY
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
    };

    const response = UrlFetchApp.fetch(
    “https://api.openai.com/v1/chat/completions”,
    options
    );

    const status = response.getResponseCode();
    const text = response.getContentText();

    if (status !== 200) {
    Logger.log(“HTTP error: ” + status);
    Logger.log(text);
    return { title: “”, description: “” };
    }

    let data;
    try {
    data = JSON.parse(text);
    } catch (e) {
    Logger.log(“Failed to parse JSON response”);
    Logger.log(text);
    return { title: “”, description: “” };
    }

    if (!data.choices || !data.choices.length) {
    Logger.log(“No choices in response”);
    Logger.log(JSON.stringify(data));
    return { title: “”, description: “” };
    }

    const raw = (data.choices[0].message.content || “”).trim();

    try {
    const parsed = JSON.parse(raw);
    return {
    title: parsed.title || “”,
    description: parsed.description || “”
    };
    } catch (e) {
    Logger.log(“JSON parse error: ” + e);
    Logger.log(“Raw content: ” + raw);
    return { title: “”, description: “” };
    }
    }

    function generateAllMeta() {
    const sheet = SpreadsheetApp.getActiveSheet();
    const lastRow = sheet.getLastRow();
    if (lastRow < 2) return;

    const urlRange = sheet.getRange(2, 1, lastRow – 1, 1);
    const urlValues = urlRange.getValues();

    const titleRange = sheet.getRange(2, 2, lastRow – 1, 1);
    const descRange = sheet.getRange(2, 3, lastRow – 1, 1);

    const titles = [];
    const descriptions = [];

    for (let i = 0; i < urlValues.length; i++) {
    const url = urlValues[i][0];

    if (!url) {
    titles.push([“”]);
    descriptions.push([“”]);
    continue;
    }

    const result = generateMetaForUrl(url);
    titles.push([result.title]);
    descriptions.push([result.description]);

    Utilities.sleep(1200); // Protect against rate limits
    }

    titleRange.setValues(titles);
    descRange.setValues(descriptions);
    }

    function onOpen() {
    const ui = SpreadsheetApp.getUi();
    ui.createMenu(“SEO Tools”)
    .addItem(“Generate Meta for All URLs”, “generateAllMeta”)
    .addToUi();
    }

    function testSingle() {
    const sheet = SpreadsheetApp.getActiveSheet();
    const url = sheet.getRange(2, 1).getValue();
    const result = generateMetaForUrl(url);
    sheet.getRange(2, 2).setValue(result.title);
    sheet.getRange(2, 3).setValue(result.description);
    }

    4. Add Permissions

    When you run the script the first time:

    • Click Run → testSingle
    • Google will ask for permissions
    • Approve them

    This ensures Apps Script is allowed to access external APIs.

    5. Test the Script on One URL

    Use the built-in helper:

    • Go to Run → testSingle

    This will generate the title and meta description for the URL in A2.
    Check columns B2 and C2.

    If the metadata appears correctly, the system is working.

    6. Run the Full Automation

    Once tested:

    • In Sheets, click SEO ToolsGenerate Meta for All URLs

    The script will loop through every URL from A2 downward and fill in metadata automatically.

    A large batch will take time because each request includes a delay to avoid rate limits.

    7. Updating or Re-Running

    You only modify one place:
    The prompt text inside generateMetaForUrl.

    Everything else stays the same.

    You can:

    • Change style
    • Add branding
    • Adjust character limits
    • Add or remove categories
    • Add keyword rules
    • Enforce templates per folder structure

    Then re-run generateAllMeta and overwrite the old metadata.

    8. Benefits of This System

    • Works entirely inside Google Sheets
    • Requires no external apps
    • Uses the OpenAI API directly
    • Produces deterministic, consistent metadata
    • Scales to thousands of URLs
    • Easy to reuse for any client or website
    • Fully automated once set up

    This is a permanent workflow you can use for every SEO project.

    Post Category:

    Share this article:

    Ankit Chauhan is an SEO Consultant and Researcher. Having more than 5 years of extensive experience in SEO, Ankit loves to share his SEO expertise with the community through his blog. Ankit Chauhan is a big-time SEO nerd with an obsession for search engines and how they work. Ankit loves to read Google patents about search engines and conduct SEO experiments in his free time.

    Related Posts

    seo meta tags

    How to Generate SEO Title Tags and Meta Descriptions at Scale Using Google Sheets and AI

    optimizing vps for wordpress performance without paying for managed hosting

    VPS Optimization for Maximizing WordPress Performance Without Managed Hosting

    add the apps script

    How to Automatically Pull Meta Titles and Descriptions into Google Sheets

    Let’s Make Something Rank

    Whether you’re a brand in India, an agency overseas, or a founder who’s tired of being buried under your competitors - I’m ready when you are.

    Let’s build something search engines (and your users) can’t ignore.

    Drop a message. I’ll respond like a human, not a sales funnel.