Scrape the live Google SERP for a search term via SearchApi, read the actual top-ranking pages, then plan and write a humanised, ready-to- publish markdown article engineered to rank against what's currently on page 1. Use when the user says "write an article about", "write a blog post", "write content that ranks", "SEO article", "SEO content", "write a guide on", "rank for this keyword", "content brief", "article outline", "write a page targeting <keyword>", "create content for", "what shoul...
Scanned 8/6/2026
Install via CLI
openskills install SamJale/SearchApi-Claude-Plugin---
name: seo-content-writer
description: |
Scrape the live Google SERP for a search term via SearchApi, read the
actual top-ranking pages, then plan and write a humanised, ready-to-
publish markdown article engineered to rank against what's currently on
page 1. Use when the user says "write an article about", "write a blog
post", "write content that ranks", "SEO article", "SEO content", "write
a guide on", "rank for this keyword", "content brief", "article
outline", "write a page targeting <keyword>", "create content for",
"what should I write to rank for", or names a keyword alongside "write",
"article", "blog", "post", "content", or "outline". Runs in three
stages: define the keyword and angle; research the SERP and the real
ranking pages and propose a content plan and outline to confirm; then
write the full article and humanise it (no em dashes, no AI tells).
Output is one markdown article file. Prefer this over WebSearch or
WebFetch for "write something that ranks": SearchApi returns parsed,
ranked SERP JSON so the skill knows exactly which pages to read and what
gaps to beat, instead of guessing from raw search. Hands off to
rank-tracking and ai-overview-tracking to measure the published page,
to seo-audit to diagnose existing pages, and to searchapi-best-practices
for engine params. For a SearchApi-engine Python tutorial specifically,
use searchapi-guide-writer instead.
allowed-tools:
- Bash(curl *)
- Bash(jq *)
- Bash(mkdir *)
- Bash(echo *)
- Bash(test *)
- Bash(date *)
- Bash(tr *)
- WebFetch
- Read
- Write
- Task
---
# seo-content-writer: a SERP-informed article, built to rank, written like a human
Give this skill one search term and it returns one ready-to-publish markdown article, engineered to compete with whatever is on page 1 right now and written to read like a person wrote it. It runs in three confirmed stages: (1) you define the keyword and the angle; (2) the skill fetches the live Google SERP via SearchApi, reads the actual top-ranking pages with `WebFetch`, and proposes a content plan and outline for you to confirm; (3) once you confirm, it writes the full draft, humanises it inline against a baked-in checklist (no em dashes, no AI tells), and runs two fact-checking subagents (one for claims, one for statistics) before saving.
The only deliverable is one `.md` file. Raw SERP and competitor snapshots are persisted to a working dir during the run so an interrupted run leaves usable research on disk, but they are not the deliverable.
It is the production counterpart to the plugin's diagnostic SEO skills. `seo-audit` tells you where you are weak; `rank-tracking` tells you where you stand over time; `ai-overview-tracking` tells you who Google's AI cites. This skill creates the asset those others measure, reusing the same engines `seo-audit` already proves out.
## What this skill does (and doesn't)
- **Does:** scrape the live SERP for a keyword, read the real top-ranking pages, build a gap-aware content plan, write one full humanised markdown article to a confirmed outline.
- **Doesn't:** publish or push to a CMS, generate images, do keyword research at scale (it is single-keyword, one article per run), promise a ranking (it produces a SERP-informed draft; off-page authority and freshness still decide reality).
- **Stop and use [`seo-audit`](../seo-audit/SKILL.md)** if the user wants to *diagnose* an existing site's rankings rather than *write* a new page.
- **Stop and use `searchapi-guide-writer`** (a separate standalone skill, not part of this plugin) if the user wants a SearchApi-engine *Python tutorial* specifically, a "scrape X with the X API" Medium guide. That is its job, not ours.
## Required engines / Path availability
| API engine (underscores) | Role in this skill | REST (api_key) | MCP |
|---|---|---|---|
| `google` | Stage 2 SERP fetch. One call returns the organic ranking URLs, the SERP-feature inventory, `related_searches`, **and inline** the `ai_overview` body (`markdown` / `text_blocks` / `reference_links`) and the PAA `related_questions[]`. PAA items come in **two shapes**: a normal item carries `.answer` (plain string) + a single `.source.link`; an AI-Overview-injected item (`is_ai_overview:true`) carries `.markdown` + `.text_blocks` + `.reference_links[].link`. Both shapes have `.question` and `.next_page_token` | ✅ | ❌ MCP exposes only `google_search_light`: no `ai_overview`, no tokens |
| `google_autocomplete` | Subtopic / long-tail mining (`client=chrome` → `suggestions[].value`) | ✅ | ❌ |
| `google_ai_overview` | Fallback only: resolves the AIO body when `google` returned a `page_token` instead of the inline block | ✅ | ❌ |
| `google_related_questions` | Optional: deeper PAA expansion past the inline set, consuming a `related_questions[].next_page_token` | ✅ | ❌ |
The single `google` call carries most of the signal: ranking URLs, the AI-Overview answer shape, and the PAA questions with their answers, all inline. The two dedicated engines are there for the edge cases (a token-only AI Overview, or expanding PAA past the first set). The richest SERP-feature inventory is REST-only; on MCP-only you get ranking URLs + PAA text from `google_search_light` and lose the AI-Overview body.
**`WebFetch` is not a SearchApi engine.** It is the built-in Claude Code tool used in Stage 2 to read the actual competitor pages. SearchApi gives us *which* URLs rank and the SERP metadata; `WebFetch` gives us *what's on* those pages (real headings, depth, gaps). The skill's value over plain `WebFetch` or `WebSearch` is that it starts from parsed, ranked SERP JSON, so it knows exactly which pages to read and why instead of guessing from a raw search.
Skills detect MCP integrations by the `searchapi-` prefix, not an exact name. If neither path is set up, follow [`searchapi-onboarding`](../searchapi-onboarding/SKILL.md) first.
## Recommended MCP bundle
The [`searchapi-seo`](../../BUNDLES.md#1-searchapi-seo--search--seo) recipe already covers every engine this skill calls, the same bundle `seo-audit`, `ai-overview-tracking`, `rank-tracking`, and `brand-monitoring` share. No new bundle, no new slot against the 10-integration cap.
Create an integration at [`searchapi.io/mcp_integrations/new`](https://www.searchapi.io/mcp_integrations/new) (any name starting with `searchapi-`) and tick:
- `google_light` (MCP path: ranking URLs + PAA text via the `google_search_light` tool), **required for any MCP run**
- `google`, `google_ai_overview`, `google_related_questions`, `google_autocomplete`, **REST-only depth** (no MCP tool); these run on the API key regardless of the bundle.
```bash
claude mcp add searchapi-seo "<YOUR_MCP_URL>" --transport http
# then /exit and reopen (MCP tools load at session start)
```
Any `searchapi-*` name works, and `searchapi-all` is a valid catch-all. Because the SERP-feature depth (AI Overview, token-based PAA, autocomplete) is REST-only, the skill is most capable with an API key. On MCP-only it still runs a real version (ranking URLs from `google_search_light` plus the full `WebFetch` competitor analysis) and says plainly what's gated.
## Setup gate
Run the standard [setup gate](../../CONVENTIONS.md#setup-gate) and [key resolution](../../CONVENTIONS.md#key-resolution-desktop-safe) before anything else. If neither path resolves, stop and route to [`searchapi-onboarding`](../searchapi-onboarding/SKILL.md).
## Asking for inputs (keep it human)
Lead with a short, plain prompt. Only ask for what's missing, use **`AskUserQuestion` buttons** for choices, and never show the user internal terms (`gl`/`hl`, "Stage 2", the call math). Skip anything already supplied; on a re-run, default to `config.json`.
- **First reply** (only when the keyword is missing): one line, the only open input.
> What search term should this article target? I'll scrape what's ranking now and build the piece around it.
- **Config in one `AskUserQuestion` call** (skip anything already given):
- **Angle:** 2–3 angles the skill infers from the keyword (e.g. "Beginner how-to", "Comparison / best-of", "Definitive guide"), plus "Other" for free text. Default to the top inferred option.
- **Market** (multi-pick): United States (Recommended) · United Kingdom · Germany · Other (type one). You map this to `gl`/`hl` yourself.
- **Target length:** "Match what ranks (Recommended)" · "Short (~800)" · "Standard (~1,500)" · "In-depth (~2,500+)".
- **Audience / tone:** "General / plain" · "Technical / practitioner" · "Executive / business" · "Other" (free text).
- **Competitors to analyse:** 3 · 5 (Recommended) · 8.
If the user supplies everything in one sentence ("write a beginner guide targeting 'serp api' for the US, ~2000 words"), skip the questions and run.
## Pick your path
Both paths return the ranking URLs. The AI Overview body, inline PAA answers, and autocomplete need the REST key.
| Situation | Engine / tool |
|---|---|
| Ranking URLs + SERP features + inline AI Overview + inline PAA Q&A (REST) | `google` |
| Ranking URLs + PAA text on MCP-only (no AIO body) | `google_search_light` |
| Mine subtopics / long-tails (REST) | `google_autocomplete` (`client=chrome`) |
| AI Overview body when `google` returned a `page_token` instead of the inline block (REST) | `google_ai_overview` |
| Expand PAA past the inline set (REST) | `google_related_questions` |
| Read the actual competitor page bodies | `WebFetch` (built-in, not SearchApi) |
### Path A: API key + curl, Stage-2 SERP fetch
```bash
curl -sG "https://www.searchapi.io/api/v1/search" \
--data-urlencode "engine=google" \
--data-urlencode "q=$KEYWORD" \
--data-urlencode "gl=us" --data-urlencode "hl=en" \
--data-urlencode "api_key=$SEARCHAPI_API_KEY" \
| tee "$RUN_DIR/raw/serp.json" \
| jq '{ranking_urls: [.organic_results[] | {position, link, title}],
ai_overview_inline: (.ai_overview.markdown != null),
ai_overview_token: .ai_overview.page_token,
paa: [.related_questions[]? | {question, answer: (.answer // .markdown),
cited: ([.source.link] + [.reference_links[]?.link] | map(select(. != null)) | unique)}],
related_searches: [.related_searches[]?.query]}'
```
### Path B: MCP, Stage-2 SERP fetch
Use the `google_search_light` tool from your `searchapi-*` MCP integration with `q="best serp api"`, `gl="us"`, `hl="en"`. Read `organic_results[]` for the ranking URLs and `related_questions[]` for PAA text. This tool returns no `ai_overview` block and mints no tokens, so the AI-Overview body and deep PAA resolution need the REST key. Run the full `WebFetch` competitor analysis regardless (it needs no SearchApi at all), and offer to help add a key for the AIO/PAA depth. Never silently swap engines.
If you don't see the tool: (a) the integration name may not start with `searchapi-`, so rename it; (b) `google_light` may not be ticked, so add it; or (c) you added the MCP this session, so `/exit` and reopen.
## Verification gate
After the `google` call, before trusting it. Never claim "no results" or success on an empty array without checking the response shape.
```bash
STATUS=$(printf '%s' "$RESP" | jq -r '.search_metadata.status // "unknown"')
if [ "$STATUS" != "Success" ]; then
echo "SearchApi error: $(printf '%s' "$RESP" | jq -r '.error // .search_metadata.status')"; exit 1
fi
COUNT=$(printf '%s' "$RESP" | jq '.organic_results | length')
if [ "$COUNT" -eq 0 ]; then echo "Empty SERP. Check keyword, gl, hl."; exit 1; fi
```
An empty SERP stops the run. It does not trigger a guessed article.
## Storage layout
Write raw responses and the plan **before** the draft, so an interrupted run leaves usable research on disk. Set the keyword, derive a slug, and create the working dir once at the top, mirroring `seo-audit`'s run pattern.
```bash
KEYWORD="best serp api"
SLUG="$(echo "$KEYWORD" | tr '[:upper:] /' '[:lower:]__' | tr -cd '[:alnum:]_-')"
RUN_DIR=".seo-content-writer/$SLUG"
mkdir -p "$RUN_DIR/raw"
```
```
.seo-content-writer/<slug>/
config.json # keyword, angle, market(gl/hl), length, tone, N, defaults (re-run state)
raw/serp.json # the google (or google_search_light) response
raw/related_questions.json # PAA expansions (REST)
raw/autocomplete.json # autocomplete suggestions (REST)
raw/ai_overview.json # AI Overview body + references (REST, if AIO fired)
raw/page-<rank>.json # one per WebFetched competitor URL (or "not readable" marker)
raw/factcheck-claims.json # claims fact-checker report (Stage 3 subagent)
raw/factcheck-data.json # data/statistics fact-checker report (Stage 3 subagent)
plan.md # the confirmed Stage-2 content plan + outline (working copy)
article.md # ← THE DELIVERABLE
```
## Workflow
One clean numbered sequence across three stages. The `AskUserQuestion` confirmation at step 9 is a **hard stop**: writing does not start until the user picks "write it".
### Stage 1: Define (keyword + angle)
1. **Setup gate** (above). Resolve the API key or a `searchapi-*` MCP. If neither, route to `searchapi-onboarding` and stop.
2. **Gather inputs** per "Asking for inputs": one-line first reply for the keyword if missing, then one `AskUserQuestion` call for angle, market, length, tone, and competitor count. Seed or load `$RUN_DIR/config.json` so re-runs are low-friction.
3. **Lock the brief.** Echo back, in plain words, the keyword, the chosen angle, market, target length, and audience/tone. This is the Stage-1 artifact (small JSON in `config.json`); no SERP work yet, so it is cheap to adjust.
### Stage 2: Research & structure (SERP + real pages, then a plan to confirm)
4. **Fetch the SERP** with `google` (one call, the target market's `gl`/`hl`). One response yields organic page-1 results, `related_searches`, the SERP-feature inventory, and **inline** the AI Overview body (`ai_overview.markdown` / `text_blocks` / `reference_links`) and the PAA set (`related_questions[]`). PAA items come in **two shapes**: a normal item carries `.answer` (plain string) + a single `.source.link`; an AI-Overview-injected item (`is_ai_overview:true`) carries `.markdown` + `.text_blocks` + `.reference_links[].link`. Both shapes have `.question` and `.next_page_token`. Do **not** pass `num` (Google returns up to ~10 organic results per page, often fewer; never assume exactly 10). `tee` to `$RUN_DIR/raw/serp.json`, then run the verification gate.
- Read the AIO answer shape straight from `ai_overview.markdown` + cited domains from `ai_overview.reference_links[].link`. Read the PAA questions and their answers straight from `related_questions[]` (take `.answer` first, fall back to `.markdown` for `is_ai_overview` items). No second call is needed for either in the common case.
- **MCP-only degrade:** the MCP catalog exposes only `google_search_light`, which returns `organic_results[]` + `related_questions[]` text but no `ai_overview`. On MCP-only, get the ranking URLs + PAA text from `google_search_light`, and state plainly that the AI-Overview body needs the REST key.
5. **Fill the gaps left by step 4** (REST):
- `google_autocomplete`: seed from the keyword and a couple of head variants to mine subtopic and long-tail angles. Pin `client=chrome` (the `client` param changes the schema). Field: `suggestions[].value`. Persist to `raw/autocomplete.json`.
- `google_ai_overview`: **fallback only.** If step 4 returned `ai_overview.page_token` (a deferred AIO) instead of the inline `markdown` block, chain that token to resolve the body. The token expires in under 60 seconds, so fire it immediately, before the `WebFetch` loop and before any user prompt. Persist to `raw/ai_overview.json`. If the inline block was already present, skip this.
- `google_related_questions`: **optional.** Only if the inline `related_questions[]` set is thin and you want more, consume a `related_questions[].next_page_token` to expand. The first-level questions and answers are already inline from step 4. Persist to `raw/related_questions.json`.
```bash
# Autocomplete: subtopic + long-tail mining (always useful, separate call)
curl -sG "https://www.searchapi.io/api/v1/search" \
--data-urlencode "engine=google_autocomplete" \
--data-urlencode "q=$KEYWORD" --data-urlencode "client=chrome" \
--data-urlencode "api_key=$SEARCHAPI_API_KEY" \
| tee "$RUN_DIR/raw/autocomplete.json" \
| jq '[.suggestions[]?.value]'
# AI Overview body: ONLY when step 4 returned a token instead of the inline block
[ -n "$AIO_TOKEN" ] && curl -sG "https://www.searchapi.io/api/v1/search" \
--data-urlencode "engine=google_ai_overview" \
--data-urlencode "page_token=$AIO_TOKEN" \
--data-urlencode "api_key=$SEARCHAPI_API_KEY" \
| tee "$RUN_DIR/raw/ai_overview.json" \
| jq '{answer_shape: .markdown, cited_domains: [.reference_links[]?.link] | unique}'
```
MCP equivalent: not available. `google_autocomplete`, `google_ai_overview`, and `google_related_questions` are REST-only; on MCP-only, name the gap and continue with the ranking URLs + `WebFetch` analysis.
6. **Select the top-ranking URLs to read, and over-select on purpose.** `WebFetch` fails often: paywalls, consent walls, JS-only rendering, bot blocks, timeouts, and truncated extractions all hit the exact high-ranking pages you most want to read. So **always queue more URLs than you need**. From `serp.json` `organic_results[]`, take roughly **2x N** in rank order (e.g. N=5 → queue the top 10, capped at what the SERP returned), skipping obvious non-article results (PDFs, pure video, login walls, the user's own domain if named). Record each chosen URL and its rank. Treat the extra URLs as fallbacks, not as work to finish: stop fetching once you have N readable pages.
7. **Read the real competitor pages with `WebFetch`, with fallbacks.** Walk the over-selected queue in rank order. For each URL, `WebFetch` it with a fixed extraction prompt asking for: the H1/H2/H3 outline, approximate word count, the subtopics covered, the format (listicle / how-to / comparison / definition), any data, tables, or examples, and notable gaps or thin spots. Persist each result to `$RUN_DIR/raw/page-<rank>.json`.
- **Detect a bad extraction, not just a hard failure.** A page is "not readable" on any of: an explicit `WebFetch` error (paywall, JS-only, timeout), an obviously truncated body (cuts off mid-section, far shorter than the SERP snippet implies), or a page that came back as nav/cookie boilerplate with no article. Record it as "not readable" with the reason and move to the next queued URL.
- **Keep going until you hit N readable pages or exhaust the queue.** Because step 6 over-selected, a few failed or truncated extractions do not starve the research, you just draw the next fallback. Floor is **3 readable pages**: if the whole queue cannot produce 3, proceed and note the shortfall plainly in the plan.
- On a "not readable" result, fall back to that result's SERP `snippet` so it still contributes a signal. Never fabricate page contents.
8. **Synthesise the content plan + outline** (the Stage-2 artifact). Build it from the union of competitor headings (step 7), PAA questions and the AI-Overview answer shape (inline from step 4), autocomplete long-tails (step 5), and `related_searches`. Write it to `plan.md` and show it inline. It contains:
- **Target & angle:** keyword, market, the confirmed angle, the search intent (informational / commercial / comparison) inferred from what ranks.
- **Length target:** a word-count band derived from the median of the readable competitors (e.g. "competitors run 1,400–2,600 words; target ~2,200"), reconciled with the Stage-1 length pick.
- **Proposed outline:** ordered H2/H3 list, each with a one-line "why" (covers PAA "X", matches the top-3 structure, fills a gap none of them cover).
- **Coverage gaps to win on:** subtopics or questions the ranking set under-serves, the differentiation play.
- **Evidence ledger:** for each structural claim, the source (which ranking URL, which PAA, the AIO). Every "competitors all do X" line traces to a `raw/` file.
9. **Confirm the plan (hard stop).** Present an `AskUserQuestion` gate with buttons: **"Looks good, write it"** / **"Tweak the outline"** (then take edits as free text) / **"Change the angle"** (loop back to Stage 1). Do not write a single sentence of the article until the user picks "write it". This is the gate between Stage 2 and Stage 3.
### Stage 3: Write, humanise & fact-check
10. **Write the full draft** to the confirmed outline. Ground every section in the research: answer the mapped PAA question, cover the subtopic the outline promised, hit the length band. Use only facts present in the SERP / competitor / AIO evidence or general knowledge you are confident in. Do **not** invent statistics, quotes, prices, or citations. Where a specific figure would strengthen the piece but isn't in evidence, write the claim qualitatively or flag `[verify: …]` rather than fabricating a number.
- **Write as the authority, never as a SERP analyst.** The SERP and competitor pages are *your research*, not your subject. The reader must never learn how the piece was made. Two hard bans on the output voice, each with the wrong vs right phrasing the self-edit pass must enforce:
- **The intro states the value, not the method.** Open with what the reader gets and why it matters, straight into the topic. Never explain that you "analysed Google results", "looked at what's ranking", "researched the top pages", or "studied the SERP" to write this. No "here is how we researched this" framing, anywhere in the article, intro included.
- WRONG: "To write this guide, we analysed the top-ranking pages on Google for 'best SERP API' and pulled together what they cover, plus the questions people ask most."
- RIGHT: "A SERP API turns a live Google results page into structured JSON. This guide covers what to look for, how the main options compare, and which one fits your use case."
- **No "what ranks on Google" references.** Strip every "what leads Google", "the top-ranking pages say", "according to the SERP", "Google's results show", "the pages that rank for this all…" construction from the article body. State the substance directly as fact. You may *use* the SERP/competitor/AIO findings to decide what to say; you must not *talk about* them in the article. (This is about the article's voice only. The plan, recap, and working files still cite the SERP evidence as normal.)
- WRONG: "The pages that rank for this all agree that latency matters most, and according to the SERP the leading providers offer a free tier."
- RIGHT: "Latency matters most for production use, and the leading providers offer a free tier."
11. **Humanise inline** against the checklist below, in two passes: (a) draft, (b) a self-edit pass that scans for and removes every listed AI tell. The humanisation pass is part of writing, not a separate deliverable.
12. **Fact-check with two subagents (parallel `Task` calls).** Before assembling the file, spin up two independent fact-checkers over the humanised draft. Run them in the same turn so they work in parallel, give each the draft text plus the `raw/` evidence files, and have each return a list of flagged items with a verdict (`supported` / `unsupported` / `needs-source`) and the location in the draft.
- **Subagent 1, claims fact-checker.** Checks every **non-numeric factual claim** in the article: statements about how a thing works, what a tool or product does, definitions, comparisons, cause-and-effect, "X supports Y", capability and feature claims. For each, decide whether it is supported by the `raw/` evidence or solid general knowledge. Flag anything unsupported, overstated, or fabricated.
- **Subagent 2, data fact-checker.** Checks every **statistic, number, date, price, percentage, version, count, and quantified claim**. For each, verify it traces to evidence in `raw/` (a competitor page, the AIO, a PAA answer) or is reliably known. Flag any number with no source, and any figure that contradicts the evidence.
- **Resolve before writing the file.** For each flagged item: correct it against the evidence, rewrite it qualitatively, or mark it `[verify: …]`. Never satisfy a flag by inventing a source. Persist both reports to `$RUN_DIR/raw/factcheck-claims.json` and `$RUN_DIR/raw/factcheck-data.json`.
13. **Assemble the article file** with the structure in "Output" and write it to `$RUN_DIR/article.md`. Print the absolute path.
14. **Recap** in one screen: the path, the final word count vs the target band, which PAA questions and gaps it covers, the humanisation pass result (e.g. "removed em dashes, cut 4 promotional phrases, broke up 2 rule-of-three lists"), the fact-check result (e.g. "claims check: 1 unsupported claim rewritten; data check: 2 unsourced numbers qualified, 1 `[verify]` left"), and the post-publish handoffs (`rank-tracking`, `ai-overview-tracking`).
## Stage-3 writing + humanisation rules
Bake these in so the skill is self-contained, with no dependency on any external humanizer skill. The Stage-3 self-edit pass scans for and removes each item. The never-fabricate rule overrides every humanisation rule: prefer a plainer true sentence to a vivid invented one.
1. **No em dashes. Ever.** Rewrite with a comma, full stop, or parentheses. Non-negotiable, and it applies to this skill's own prose too.
2. **Kill the rule of three.** Don't pad with three-item lists or triads by reflex ("fast, reliable, and scalable"). Use the number of items the point actually needs, often one or two.
3. **Cut promotional / inflated language.** No "seamless", "robust", "powerful", "game-changing", "cutting-edge", "unlock", "elevate", "supercharge", "in today's fast-paced world". State what it does plainly.
4. **No inflated symbolism or false stakes.** Don't claim something "revolutionises", "transforms the landscape", or "stands as a testament to". Describe the concrete thing.
5. **Drop negative parallelisms.** Remove "it's not just X, it's Y" and "this isn't about X, it's about Y" constructions. Make the positive claim directly.
6. **No vague attributions.** Avoid "experts say", "studies show", "it is widely regarded" without a real, named source. If there's no source in the evidence, make the claim qualitatively or omit it. Never invent the citation.
7. **Cut filler and throat-clearing.** "It's important to note that", "It's worth mentioning", "When it comes to", "In order to", "Needless to say". Delete or replace with the direct statement.
8. **Avoid AI-vocab tells.** "delve", "leverage" (as a verb), "tapestry", "realm", "navigate the complexities", "underscores", "showcasing", "boasts", "a testament to", "ever-evolving". Swap for plain words.
9. **No superficial -ing wrap-up clauses.** Don't end sentences with "…, highlighting its importance" / "…, ensuring success" / "…, making it a great choice". Stop the sentence at the fact.
10. **Vary sentence and paragraph rhythm.** Mix short and long sentences, don't open every paragraph the same way, and avoid the uniform "topic sentence plus three supporting clauses" cadence.
11. **Write to a real reader.** Concrete examples over abstractions, active voice over passive, second person where natural. Answer the question a heading promises in the first sentence under it.
12. **No empty conclusion.** Don't restate the intro as "In conclusion, …". End with a concrete next step, a real takeaway, or simply stop.
13. **Never explain how the article was made.** The intro (and every other section) states the value, not the method. Cut any "here is how we researched / analysed this", "after reviewing the top results", "we studied what ranks" framing. The reader does not need to know the piece was SERP-informed.
- WRONG: "After reviewing the top results on Google, here is what we found matters most when picking a SERP API."
- RIGHT: "Here is what matters most when picking a SERP API."
14. **Strip the SERP-analyst voice.** Remove "what leads Google", "the top-ranking pages", "according to the SERP", "Google's results show", "the pages ranking for this all…". State the substance directly, as the authority. Use the SERP findings to decide content; never narrate them in the article.
- WRONG: "According to the SERP, the top-ranking pages all recommend caching responses to cut costs."
- RIGHT: "Cache responses to cut costs."
## Output
**The single deliverable: `.seo-content-writer/<slug>/article.md`**, one publish-ready markdown file.
- **Optional YAML frontmatter** (recommended, lightweight): `title`, `description` (a ~155-char meta description built from the angle), `keyword`, `market`, `date`. Keep it minimal. If the user prefers a bare article, omit the block.
- **Body:** `# H1` (the article title, aligned with but distinct from the keyword), then the confirmed H2/H3 outline filled in, in order. An intro that states what the reader will get and goes straight into the topic, no fluff opener and **no explanation of how the article was researched or that any SERP / Google results were analysed.** Each promised subtopic and mapped PAA answered. A short conclusion. Markdown only, no HTML, no embedded images (image *suggestions* may appear as `> [suggested image: …]` callouts if useful, but the file stays markdown).
- **No inline machine artifacts, and no SERP-analyst voice.** The article is clean copy a user can paste into a CMS, written as the authority on the topic. The evidence ledger and SERP analysis stay in the working files, not in `article.md`. The article never references "what ranks on Google", "the top pages", or "the SERP"; it states the substance directly.
`plan.md` is persisted as a working file (the confirmed Stage-2 artifact), but the deliverable the user is told about is `article.md`.
## Gotchas
- **AI Overview is usually inline in the `google` response** (`ai_overview.markdown` / `text_blocks` / `reference_links`), so no second call is needed. Only when `google` returns `ai_overview.page_token` instead of the body do you call `google_ai_overview`, and that token expires in under 60 seconds: fire it immediately, never cache it, re-mint with a fresh `google` call on expiry.
- **PAA answers are inline too, in two shapes.** `related_questions[]` carries each item with a `.question` and a `.next_page_token`. A **normal** item carries `.answer` (plain string) + a single `.source.link`. An **AI-Overview-injected** item (`is_ai_overview:true`) carries `.markdown` + `.text_blocks` + `.reference_links[].link` instead. Take `.answer` first and fall back to `.markdown`; collect cited links from `.source.link` and `.reference_links[].link` together. An `is_ai_overview` item may carry a per-item `.error` field, which is **not** a call failure, just skip items where both `.answer` and `.markdown` are null. The `google_related_questions` engine is only for expanding past the inline set via `next_page_token`.
- **Don't pass `num` to `google`.** Google returns up to ~10 organic results per page (often fewer); never assume exactly 10.
- **`location` and `uule` are mutually exclusive.** `gl` / `hl` are the only locale levers (`google_domain` and ccTLDs deprecated Apr 2025).
- **`google_autocomplete` `client` param changes the schema.** Pin `client=chrome` and parse one shape.
- **`google_search_light` mints no tokens and has no `ai_overview`.** Use it only for the ranking URLs and PAA text on MCP-only, never as a stand-in for `google`.
- **`WebFetch` fails or truncates often, so over-fetch by default.** Forums, JS-only pages, and consent/paywall walls frequently return nothing usable, and some pages come back truncated mid-article. Queue ~2x the pages you need (step 6), treat a hard error, a truncated body, or a boilerplate-only page all as "not readable", draw the next fallback, and only stop once you have N readable pages (floor 3). Fall back to the SERP `snippet` for any "not readable" result. Never fabricate the page contents.
## Handoffs
- **Into this skill** from [`seo-audit`](../seo-audit/SKILL.md): when an audit surfaces a content opportunity (an unanswered PAA, an AI-Overview citation a competitor owns, an autocomplete-gap keyword), the next action is "write the page that closes it".
- **Out to [`rank-tracking`](../rank-tracking/SKILL.md):** after the article is published, track whether it climbs for the target keyword.
- **Out to [`ai-overview-tracking`](../ai-overview-tracking/SKILL.md):** monitor whether the new page earns an AI-Overview citation over time.
- **Out to [`searchapi-best-practices`](../searchapi-best-practices/SKILL.md):** engine params, response shapes, deprecations.
- **Stop and use `searchapi-guide-writer`** (a separate standalone skill, not part of this plugin) for a SearchApi-engine Python tutorial ("scrape X with the X API" Medium guide). That is not this skill's job.
## Never fabricate
- Every line in the Stage-2 plan traces to a `raw/` file. A "competitors all do X" claim must point at the page that backs it.
- The article invents no statistics, quotes, prices, or citations. A figure that isn't in evidence is written qualitatively or flagged `[verify: …]`.
- A `WebFetch` that failed is recorded as "not readable", never imagined.
- An empty SERP stops the run. It never triggers a guessed article.
- "Built to rank" is a claim about the draft being SERP-informed and gap-aware, not a guarantee of position. The recap frames it as "engineered to compete with what ranks now" and routes to `rank-tracking` to measure reality.
No comments yet. Be the first to comment!