syted

syted blog

SEO Automation: What to Script, What to Do by Hand

Look at what actually ranks for "seo automation" in the United States. Two Reddit threads sit in the top ten, at positions 3 and 8, carrying 136 and 92 answers between them. Around them: a vendor landing page, a 9,000 word tool listicle, and two guides whose intended reader is a marketing operations team coordinating fifty stakeholders.

When Google puts forum arguments that high, it is admitting something. No article answered the question well enough to displace people arguing about it.

And the question those threads ask is not the one the listicles answer. The threads ask which specific tasks survive a script. The listicles answer which products exist. Across roughly sixteen thousand words of ranking content, there is not one line of code a reader could paste.

This article is the missing answer: a verdict on each task, the code for the ones that hold, and the exact failure mode for the ones that do not.

The test that decides whether a task can be scripted

A task is safe to automate when the machine can check its own output. That is the whole criterion, and it decomposes into three questions.

Is correctness checkable without judgment? A URL either returns 200 or it does not. A sitemap either lists a page that exists or it lists a deleted one. Those have right answers. "Is this the right keyword for my business" does not.

Is the input complete? Everything the task needs has to be in the data the script can read. Volume and difficulty live in an API. Whether the people ranking for a query are selling what you sell lives in your head, after you look.

Is being wrong cheap? A wrong sitemap entry costs a crawl request and gets fixed on the next build. A published article targeting a query one of your own pages already owns costs you both pages, and you find out months later.

Task property Sitemap generation Keyword selection
Correctness checkable by machine Yes, the URL resolves or it does not No, it depends on who ranks and what you sell
Input complete in the data Yes, the content source is the truth No, the audience read is not a number
Wrong answer cheap to reverse Yes, next build fixes it No, a published page competes with you for months

Run those three questions over your own work before you buy anything. Most of what a founder calls "SEO work" fails the second question, and that is where automation tools quietly produce confident garbage.

The task inventory, with a verdict on each

Here is the inventory. The verdicts are not opinions about ambition, they come from applying the three tests above.

Task What a script gets right What it gets wrong Verdict
Sitemap and robots generation Every published URL, every time, with a true lastmod Nothing, if it reads the content source and not a hand list Script it
Broken internal link detection Finds every link pointing at a slug that no longer exists Cannot tell you which link should have existed instead Script it
Liveness of published URLs Catches a page that stopped serving your article Needs the article's identity, not just a status code Script it
Impressions with zero clicks Surfaces every page Google shows and nobody opens Cannot write the better title Script it
Duplicate target detection Flags two of your pages aimed at one query Cannot decide which page wins Script it
Keyword expansion and metrics Pulls hundreds of variants with volume and difficulty Ranks them by numbers that ignore who ranks Assist
Content brief Assembles the outline of what already ranks Copies the consensus, including its blind spots Assist
Draft writing Produces coherent prose at volume Cannot tell whether the claim it wrote is true Assist
Keyword selection Nothing The audience read, which is the whole decision Keep human
The decision to publish Nothing Everything that the quality gate exists to catch Keep human
Link outreach Finds and tracks prospects The relationship, which is the only part that works Keep human
Getting a page indexed Nothing, and no API offers this See the section below, this one does not exist Nobody

Three verdicts, three meanings. "Script it" means write it once and never think about it again. "Assist" means the machine does the first pass and you own the output. "Keep human" means a script that touches it will cost you more than it saves.

Four jobs worth scripting this week

These are ordered by how quickly they pay back. Each one is a file you write once.

The sitemap that cannot drift

The common sitemap bug is not a missing sitemap. It is a sitemap that lists what you deleted, because someone maintained it by hand. Generate it from the same source that renders the pages, and drift becomes impossible.

On Next.js 16, that is a single file. This repo runs next@16.2.12, and the shape below is what app/sitemap.ts looks like there:

// app/sitemap.ts
import type { MetadataRoute } from 'next'
import { getAllPosts } from '@/lib/blog'

export default function sitemap(): MetadataRoute.Sitemap {
  const posts = getAllPosts()

  return posts.map((post) => ({
    url: `https://example.com/blog/${post.slug}`,
    // The date the content changed, not the date of the build.
    // A lastmod that moves on every deploy teaches crawlers to ignore it.
    lastModified: post.updatedAt ?? post.date,
    changeFrequency: 'monthly' as const,
    priority: 0.6,
  }))
}

The comment in the middle is the part people get wrong. Setting lastModified to new Date() is easy and it makes every page look freshly edited on every deploy. That signal is worth something exactly once. After that it is noise, and it is noise you generated. More on the Next.js specifics in our notes on Next.js SEO.

The report that finds pages Google shows and nobody opens

This is the highest value script on the list, and almost nobody runs it. Search Console knows which of your pages collect impressions and zero clicks. That set is a list of titles and descriptions to rewrite, handed to you for free.

The Search Analytics endpoint is generous enough that a single site will never hit a limit. Google's API limits page, last updated 2025-08-28, gives Search Analytics 1,200 queries per minute per site and per user, with 40,000 per minute and 30,000,000 per day at the project level.

ACCESS_TOKEN=$(gcloud auth print-access-token)

curl -s -X POST \
  "https://searchconsole.googleapis.com/webmasters/v3/sites/sc-domain%3Aexample.com/searchAnalytics/query" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startDate": "2026-08-29",
    "endDate": "2026-09-26",
    "dimensions": ["page"],
    "rowLimit": 1000
  }' > /tmp/gsc.json

Then the filter, which is four lines of the thirty you will write:

const res = await fetch(endpoint, { method: 'POST', headers, body })

// An error is not a zero. If you swallow the status, the report prints
// "nothing to fix" on the morning your credentials expire, and you believe it.
if (!res.ok) {
  throw new Error(`Search Console ${res.status}: ${await res.text()}`)
}

const { rows = [] } = await res.json()

const stranded = rows
  .filter((row) => row.impressions >= 25 && row.clicks === 0)
  .sort((a, b) => b.impressions - a.impressions)

That comment is the most expensive lesson on this page. A missing rows key means Google returned no data. An HTTP 401 means your token died. Collapsing both into an empty array produces a green report that is lying, and a green report that lies is worse than a red one.

We run this on our own blog, so here is what it currently says about us. /blog/diy-seo-for-small-business collected 229 impressions and zero clicks between 2026-08-29 and 2026-09-26. Google shows the page and nobody opens it, which means the title is the defect, not the article. That is a human editing job, and the script's entire contribution was pointing at it. The same report flags our piece on how much SEO costs at 28 impressions with no clicks.

The liveness check almost nobody writes

Every publishing pipeline eventually serves a 200 that contains the wrong page. A catch-all route, a CMS that moved the slug, a customer page that already occupied the address. The status code says everything is fine.

So do not check the status code. Check that the response contains the article's own identity.

async function isLive(post) {
  const res = await fetch(post.url, { redirect: 'follow' })

  if (!res.ok) return { ok: false, reason: `status ${res.status}` }

  const html = await res.text()

  // The identity is the article's id, not its title: a title can be
  // rewritten, and a catch-all page can contain the words of the title
  // by coincidence. Fall back to the title only if no id is rendered.
  if (html.includes(post.id) || html.includes(post.title)) {
    return { ok: true }
  }

  return { ok: false, reason: 'served by something else' }
}

One more rule that comes from running this in production: a failed liveness check must never change the article's published state. Publishing is a dated fact. Being reachable is a symptom of today. A 404 at nine in the morning that resolves by one in the afternoon is a caching window, not an unpublished article, and a script that flips a status on it will corrupt your own records. If your pages are not showing up at all, that is a different problem, covered in why your website is not showing up on Google.

The duplicate target check, run before you queue a keyword

Two of your own pages aimed at one query is a defect you manufacture. It is also completely detectable, because the comparison is between strings you control.

const STOP = new Set(['a', 'an', 'the', 'and', 'or', 'of', 'in', 'on', 'to', 'for', 'your', 'my'])

function fingerprint(keyword) {
  return keyword
    .normalize('NFD')
    .replace(/[̀-ͯ]/g, '')
    .toLowerCase()
    .replace(/[^a-z0-9\s]/g, ' ')
    .split(/\s+/)
    .filter((word) => word && !STOP.has(word))
    .map((word) => (word.endsWith('s') && word.length > 3 ? word.slice(0, -1) : word))
    .sort()
    .join(' ')
}

function relation(a, b) {
  const fa = fingerprint(a)
  const fb = fingerprint(b)
  if (fa === fb) return 'duplicate'

  const ta = new Set(fa.split(' '))
  const tb = new Set(fb.split(' '))
  const contained = [...ta].every((t) => tb.has(t)) || [...tb].every((t) => ta.has(t))

  // Strict inclusion is reported, never merged: "crm" and "best crm"
  // are two different result pages with two different intents.
  return contained ? 'review' : 'distinct'
}

Two rules are encoded there, and both were learned by getting them wrong.

The stopword list deliberately excludes qualifiers. "Best", "free", "cheap" and "alternative" look like noise and are not: each one changes the set of pages Google returns. Strip them and the script starts merging queries that were never the same, which drains your topic list without telling you.

And inclusion gets flagged for a human, never folded automatically. "CRM" and "best CRM" share every token of the shorter phrase. They are still two result pages. Merging them is how a content plan silently loses half its topics. Our write-up on SEO content audits covers what to do once the check finds a real pair.

The one automation that does not exist: making Google index a page

This is worth its own section because the market sells it anyway, and because both official answers are short and unambiguous.

Google's Indexing API does not accept your blog posts. From the Indexing API quickstart, last updated 2026-07-16, verbatim: "The Indexing API can only be used to crawl pages with either JobPosting or BroadcastEvent embedded in a VideoObject." The same page adds that it is not a substitute for a sitemap, and that Google still recommends submitting one for coverage of your whole site.

IndexNow is a real protocol that real engines honour, and Google is not one of them. The IndexNow FAQ lists the participants as "Amazon, Bing, Naver, Seznam.cz, Yandex, and Yep". It also states plainly that "submitting a URL does not guarantee immediate indexing", and that each engine applies its own crawl quota, scheduling logic and quality signals.

So the honest automation here has two halves. Ping IndexNow for the engines that accept it, and give Google a correct sitemap with a truthful lastmod.

# The key must also be readable at https://example.com/$INDEXNOW_KEY.txt
cat > /tmp/indexnow.json <<JSON
{
  "host": "example.com",
  "key": "$INDEXNOW_KEY",
  "urlList": ["https://example.com/blog/seo-automation"]
}
JSON

curl -s -o /dev/null -w "%{http_code}\n" \
  -X POST "https://api.indexnow.org/indexnow" \
  -H "Content-Type: application/json; charset=utf-8" \
  --data-binary @/tmp/indexnow.json

Two constraints from the protocol docs are worth putting in the script rather than in your memory. A single POST takes up to 10,000 URLs, so batch instead of looping. And do not resubmit the same URL repeatedly without a real content change: wait at least five minutes between updates, and back off when you get an HTTP 429.

While we are on the subject of what vendors promise, Google's own guidance on hiring help is blunt about it. The page on whether you need an SEO, last updated 2026-06-05, says "Be wary of tools that claim to be 'acceptable' or 'approved' by Google Search." There is no approved list. Any product implying membership in one is telling you something about itself.

Three jobs that stay human, and what breaks when you script them

Choosing the keyword

This is the expensive one, and it is expensive precisely because a script can fake it convincingly. Volume and difficulty are two numbers in an API response. Sorting by them produces a ranked list that looks like a decision and is not.

What the numbers cannot see is who currently ranks and whether those pages serve the reader you serve. Two measured examples from our own rejected list make the point better than the principle does.

"SEO for HVAC companies" measures 1,057 searches a month at difficulty 8. Every threshold passes. Then you open the results: the page at position 2 is an agency service page, position 5 is a directory listing, and position 7 is an article titled "8 Best HVAC SEO Agencies to Drive Local Leads". An agency roundup in the top ten proves the click is shopping for a provider, not learning to do the work. A script optimising a composite score writes that article and wonders why it converts nothing.

The second example is worse, because the numbers are better. "Shopify SEO" measures 5,389 a month at difficulty 0, which is the best score we have ever recorded. The results page has Shopify itself occupying four of the ten organic slots on its own brand name, with one help page carrying 1,572 backlinks. Difficulty 0 said "free". The backlink column said "not available". We have written elsewhere about running this check as an AI SEO agent would, and the conclusion is the same: read the results page before you trust the score.

The brief

An automated brief assembles what already ranks, which means it reproduces the consensus of the current top ten, including the thing all ten of them missed. That is how a whole niche ends up with fifty articles that share the same outline and the same gap.

Use the machine to get the inventory, then ask what is absent from all of it. The absence is the article. We picked the angle for this one that way: five pages ranking, zero code blocks. Our notes on content marketing for ecommerce and on ecommerce blog ideas were both chosen on the same reading.

The decision to publish

Keep a gate, and let it hold things back. A draft that fails on a factual claim it cannot source is not a draft to fix in post, it is a draft to stop.

The reason this cannot be automated is not sentiment. It is that the gate's whole purpose is to catch what the generator did not know was wrong, and a generator checking itself is the same blind spot twice.

What Google's rules actually say about automated publishing

The policy text is more specific than the discourse around it, and reading it settles most arguments.

From Google's spam policies, last updated 2026-08-28: "Scaled content abuse is when many pages are generated for the primary purpose of manipulating search rankings and not helping users." The clause that names AI directly describes "using generative AI tools or other similar tools to generate many pages without adding value for users."

Read what the sentence is built on. Purpose and value, not authorship or tooling. Nothing there prohibits a script, a model, or a hundred pages. What it prohibits is generating pages whose reason to exist is the ranking.

The same document defines site reputation abuse as applying "where third-party content is published on a host site mainly because of that host's already-established ranking signals, which it has earned primarily from its first-party content." Worth knowing if you were planning to rent out a subfolder.

One more, because it closes off a popular automation. Google's page on AI features, last updated 2025-12-10, states: "You don't need to create new machine readable files, AI text files, or markup to appear in these features. There's also no special schema.org structured data that you need to add."

We serve an llms.txt anyway, and it is fair to point out the tension. The file costs nothing, some assistants other than Google read it, and Google's sentence is about Google. Anyone selling it as the key to AI Overviews is contradicting the documentation. What actually moves citation rates is covered in answer engine optimization and in how to get cited by ChatGPT.

How to measure whether the automation worked

Measure the automation, not the rankings. Those are two different questions, and conflating them is how people conclude that a working script failed.

Three numbers tell you whether the scripts earned their keep:

  • Defects caught before publishing. Broken links, duplicate targets, dead covers, missing canonical. Count them per month. This number going up is good news, it means the gate is working.
  • Hours returned to you. Be honest and subtract maintenance. A script you debug for an hour a week did not save you an hour a week.
  • Pages with impressions and no clicks. From the report above. This one should shrink, because it is the queue of titles you have not rewritten yet.

What none of that tells you is where you will rank. Google's own documentation on the subject, from the page quoted earlier: "No one can guarantee a #1 ranking on Google." Treat any tool, agency or script that implies otherwise as having answered a question about its honesty rather than your traffic. How long the search side actually takes is a separate discussion, and we go through it in how long SEO takes.

There is also a specific trap in measuring time saved. Time saved producing pages nobody reads is not saved, it is spent faster. If your defect count is falling and your zero-click list is growing, the automation is efficiently manufacturing work for a future you.

A build order for one person

Cheapest first, and each step is useful before the next one exists.

  1. Sitemap and robots from the content source. One file each. An afternoon, and it never needs attention again.
  2. The liveness check, run after every publish. Twenty lines. It catches the failure mode that no status-code monitor catches.
  3. The zero-click report, weekly. Thirty lines plus an OAuth setup you do once. It is the only script on this list that hands you a ranked list of things to fix by hand.
  4. The duplicate target check, run at queue time. Not at publish time. Catching a collision after you wrote the article saves nothing.
  5. Keyword expansion into a reviewed list. The machine pulls the candidates. You open the results page for the finalists and read who ranks. That step is not optional, and it is the one every tool skips.
  6. Drafting, with a gate you actually enforce. Last, and only after the five above are running. A drafting pipeline in front of no gate is a machine for producing the thing Google's policy describes.

Notice that the order is the inverse of how these products are sold. The drafting is the demo. The sitemap and the liveness check are the parts that stop you losing work you already did. If you are deciding whether any of this is worth your evenings at all, is SEO worth it and DIY SEO for a small business both take the question head on, and the second is the article our own report says we titled badly.

FAQ

Can SEO be automated?

Parts of it, and the parts split cleanly. Anything whose correctness a machine can verify against data it already has is safe to script: sitemaps, link integrity, liveness, reporting, duplicate detection. Anything that requires reading a results page and judging whether those pages serve your reader is not, no matter what a product claims. The reliable test is whether the script can tell you it was wrong.

Which SEO tasks should never be automated?

Three, in order of how much damage they do. Keyword selection, because the numbers that a script sorts by are silent about who currently ranks. The decision to publish, because a gate exists to catch what the generator did not know it got wrong. And outreach, because the part that works is the relationship, and the automated version is the reason your emails go to spam.

Is automated SEO content against Google's rules?

Not by itself. The spam policies define scaled content abuse as generating many pages "for the primary purpose of manipulating search rankings and not helping users", and the AI clause targets generating pages "without adding value for users". Both sentences judge purpose and value, not who or what typed the words. A script that publishes one useful page is fine. A script that publishes two hundred thin ones is the thing the policy names.

Can you automate getting your pages indexed?

No, and two official sources say so directly. Google's Indexing API "can only be used to crawl pages with either JobPosting or BroadcastEvent embedded in a VideoObject", so it does not apply to a blog. IndexNow works, and its participants are Amazon, Bing, Naver, Seznam.cz, Yandex and Yep, which does not include Google. What you can automate is a correct sitemap with a truthful lastmod, plus an IndexNow ping for the engines that accept one.

Can ChatGPT do SEO?

It can do the assist half well and the decision half badly. Asked for an outline, a set of keyword variants, or a rewrite of a title that is collecting impressions and no clicks, it is genuinely useful. Asked whether a keyword is right for your business, it will produce a confident answer built on no results page at all, because it did not open one. Treat its output as a first pass that you verify, which is also the right posture for LLM SEO work generally.

Will SEO be replaced by AI?

The mechanics are shifting and the underlying job is not. Assistants and AI Overviews change how often a search ends without a click, which changes which queries are worth writing for. What they have not changed is that something has to decide which query deserves a page and whether the page answers it. Tracking whether assistants cite you at all is now its own measurement problem, which is the subject of LLM brand visibility.

Get cited by ChatGPT. Rank on Google.

You found this article through search. That is the whole product.

  • One researched article a day
  • Published on your own domain
  • Keywords checked against live results
Start writing

Get cited by ChatGPT. Rank on Google.

You found this article through search. That is the whole product.

  • One researched article a day
  • Published on your own domain
  • Keywords checked against live results
Start writing