---
title: "Check copy for AI writing patterns | SEO Skill"
description: "Find the lines in a blog post, landing page, title or meta description that read as AI-written, with a fix for each, using seo lint."
canonical: "https://seoskill.dev/docs/lint"
language: "en"
---

# seo lint

Find the lines in your copy that read as AI-written, and get a concrete fix for each one. Works offline, with no sign-in.

Use it on a draft before you publish, or on a page that reads flat and you cannot say why. It looks for the habits that make copy sound generated: a “not X” tacked onto the end of a sentence, “It isn’t X, it’s Y” reframes, verbless fragments such as “Same essay, two titles.”, stock idioms that would fit any company, and closing lines such as “That’s a real win.” It also checks titles and descriptions for length.

It does not guess who wrote the page. Each finding names a pattern you can see in the line, so you decide what to change.

## Check a file

```sh
seo lint drafts/pricing-page.mdx
```

You can pass several files, a folder or a glob such as `"content/**/*.md"`, up to 100 files at once. It reads Markdown, MDX, HTML, Astro and plain text. Code blocks, inline code, imports, tags and URLs are skipped. Captions and titles inside components are checked, because people read them.

To check copy from your agent, ask it to run the check on what it just wrote:

```text
Check the draft in drafts/launch-post.md with seo lint and fix every error.
```

The hosted MCP takes the text itself (up to 200,000 characters, about a long post many times over), so it works from any agent without the CLI.

## Read the result

Each finding has a line and column, the words it matched, why they stand out, and a fix:

```text
125:110  error  figurative-worth  "fight worth picking"
    "fight worth picking" bends an idiom: you pick a fight, but a fight is not worth picking.
    Fix: Say it literally, such as "I would not compete for that search."
```

Errors are patterns a careful writer almost never needs. Warnings are often fine on a second read, such as a list item that opens with a bold label. The command exits with code 1 when it finds an error, so you can run it before a commit or in CI. Add `--fail-on warning` to stop on warnings too.

The “to review” list is different. These are lines a pattern can find but only a reader can judge, such as a superlative with no number nearby or a ”, not X” that might be a real correction. Each comes with a question. Change the line only when it fails the question.

## Pick a preset for the kind of copy

| Preset | Use it for |
| --- | --- |
| `blog` | Posts, guides and long pages. The default for Markdown and text. |
| `landing` | Short marketing copy. Stricter on buzzwords, vague link text and claims with no number. The default for HTML and Astro. |
| `docs` | Documentation, where lists and plain contrasts are normal. |
| `meta` | Titles and meta descriptions, one per line. |

```sh
seo lint --preset meta titles.txt
```

Titles are checked for 30 to 60 characters and descriptions for 70 to 160, when the page has them in frontmatter or in its HTML head. These are rules of thumb for what fits in a search result, not limits Google enforces.

## Keep a line the check flags

Sometimes the flagged line is exactly what you mean (a quote, or a contrast the reader really needs). Put a comment on the line before it to skip one rule there:

```md
<!-- seo-lint-ignore contrast-reframe -->
Bing calls run on your machine, not on the hosted server.
```

In MDX, use `{/* seo-lint-ignore contrast-reframe */}`, or `// seo-lint-ignore dash` on the line above a component prop. Leave out the rule name to skip every rule on that line. To skip a rule everywhere, add `--ignore contrast-reframe`. To allow a phrase that keeps matching, such as a product name, add `--ignore-phrase "Unlock Studio"`.

For the rest of your search work, start with the [SEO report](https://seoskill.dev/docs/report).
