Connect a site. Get vetted SEO work items every week.
SEO Ops reads a site's search data, the live pages and your brand brief together, then files five to nine work items a week on a board — each one deduped against everything filed before, vetted by a second pass, and carrying the evidence it was built from. Scout™ does the thinking. You do the work — or your agent does.
Start on a site you know well — you can judge every recommendation yourself, and you do not need a client's permission to try it. Free trial, no card, and you will know within one run whether the work is any good.
Reading search data
1,284 queries · 90d
Organizing signals
4 segments
Scanning your site
rendered · captured
Checking Brand Profile
7 / 11 signals
Scout is thinking
the analysis
Building work items
3 new · 2 merged
Verifying
median 82 · 1 held
Delivering
board · webhook · digest
This is one card. You get five to nine a week.
Shaped exactly like a card from a real run. Type before words, numbers before prose: what kind of work, which page, how big, how sure — in a glance. Open it for the argument.
⌗Schema/guides/trail-blazes/
91primary
FAQPage schema + answer the question in the first 40 words
Ranks, no clicks
5.1position
1.7kimpressions
0clicks
0%ctr
seen ×2
- Why it is on the list
- Top-6 for two high-volume trail-marking queries with zero clicks — the clearest SERP-feature-suppression shape in the data. A featured snippet is taking the click above position 5. Add FAQPage schema and open the page by answering “what do trail blazes mean”.
- Done looks like
- CTR ≥ 3%within 30 days of shippingday 7 · 30check-inspos < 8by day 30 → revert, check cannibalization
- Evidence
query:trail blaze meaningspos 5.1 · 1,707 impr · 0 clicksquery:trail marker colorspos 6.4 · 1,136 impr · 0 clicksbrand:focus“trail navigation guides” is a stated focus phrase- History
- Filed twice — a later run reached the same conclusion and bumped this card instead of duplicating it.
Priced per workspace, not per seat.
SEO Ops is $49 a month per workspace — one price whether you are one consultant or a twelve-person agency. While we are onboarding pilot partners it is $10, and we will tell you before that changes; you will not find out from an invoice. Core plans elsewhere in this category start at $103–$129 a month.
Trial
- Full product while your trial is active
- Start on your own site — no client access needed
- Self-serve signup, no call required
- Fine for one person and one site
Workspace
- One property included
- $8/mo per additional property
- 2 runs per property per week included
- Need more? Buy extra runs any time
- API keys, signed webhooks, export
- Client digests from your own brand
Scale
- Volume property pricing
- Higher run allowance
- White-label digests and reports
- Direct line for onboarding
Runs reset Monday, and unused ones do not roll over. Nothing is metered per seat or per user, and there is no charge for a run that fails. Included allowances are deliberately conservative while we measure real costs — buy extra runs whenever a property needs them.
Scout reads three things at once. You only author one of them.
Search data alone produces generic advice. So does a crawl. Read together, with your brand's own positioning folded in, they produce work a senior SEO would recognise — in the time it takes to make coffee.
What the market is actually asking for
Ninety days of query data, carved into four buckets — page-two positions, low click-through, protected rankings, and impressions earning nothing — before the model reads a single row.
What you actually shipped
The rendered page, not what the CMS claims. Scout escalates until something renders and reports which method worked, then keeps a capture of the homepage as it saw it.
What you are trying to become
Goals, focus phrases, markets, positioning, competitors. The only input a human writes — which makes it the one dial you can turn to sharpen every future run.
Which is how one person covers what used to need a team.
Runs in the console. Everything on it is also the API.
Every tab is a real result next to the exact thing that produced it. Copy the block on the right, get the thing on the left.
Move a card — or tab to one and press Space to pick it up, ← → to change column, Space or Enter to drop.
Backlog
2In progress
1Move a card to see the request it makes.
| Authorization | Bearer $SEO_OPS_PARTNER_KEY |
| Content-Type | application/json |
{"status": "in_progress"}
{
"id": "wi_3f9c1a2b",
"status": "in_progress",
"seen_count": 3,
"last_seen_at": "2026-08-21T07:40:03Z"
}
curl -X PATCH \
-H "Authorization: Bearer $SEO_OPS_PARTNER_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "in_progress"}' \
"/v1/items/wi_3f9c1a2b"
/v1/properties/prop_61aa/items?status=backlog2007 items/v1/items/wi_3f9c1a2b200in_progress/v1/items/wi_9e02c7d1200in_progress/v1/items/wi_4b77f0aa200in_progress/packs/ultralight-frames/ (4.2k impressions, no clicks), the FAQ schema on /guides/trail-blazes/, and a verify-first on the knots post. Starting with the first:# 1 · mint a partner key: console → Settings → API keys
# 2 · drop it where your agent reads secrets
SEO_OPS_PARTNER_KEY=seoops_live_…
# 3 · leave a recipe in the project so the agent knows the rules
# .seo-ops/recipe.md
# - backlog: GET /v1/properties/{id}/items?status=backlog
# - start work: PATCH /v1/items/{id} {"status":"in_progress"}
# - never archive; ask first
# 4 · talk to it
"run SEO Ops on brindlewhaite.example
and open the top three work items"
Any agent that can read a file and call HTTP — Claude Code, Cursor, your own runtime. Same hashed key the console uses; nothing it can do that you cannot.
| Authorization | Bearer $KEY |
| Content-Type | application/json |
{"days": 90}
{
"id": "run_8c2e4d7a",
"status": "running",
"stage": "reading_search_data",
"started_at": "2026-08-21T07:31:44Z"
}
curl -X POST \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"days": 90}' \
"https://api.seo-ops.com/v1/properties/$PROPERTY_ID/runs"
| Authorization | Bearer $KEY |
[
{
"id": "wi_3f9c1a2b",
"title": "Title + meta lead with 'ultralight pack frame' — 4.2k impr at 0% CTR",
"status": "backlog",
"rank": 1,
"impact": "High",
"effort": "Medium",
"seen_count": 5,
"last_seen_at": "2026-08-21T07:40:03Z"
},
… 6 more
]
curl -X GET \ -H "Authorization: Bearer $KEY" \ "https://api.seo-ops.com/v1/properties/$PROPERTY_ID/items?status=backlog"
| Content-Type | application/json |
| X-Agency-Event | items.upserted |
| X-Agency-Signature | sha256=3b1f…9e |
{
"event": "items.upserted",
"api": "v1",
"occurred_at": "2026-08-21T07:40:05Z",
"property_id": "prop_61aa…",
"run_id": "run_8c2e4d7a",
"data": { "item_count": 7, "items": [ … ] }
}
— then push each item into ClickUp, Jira, Linear, or a sheet. report.ready fires first, with the run summary.
# sample payload your endpoint receives
curl -X POST \
-H "Content-Type: application/json" \
-H "X-Agency-Event: items.upserted" \
-H "X-Agency-Signature: sha256=3b1f…9e" \
-d '{
"event": "items.upserted",
"api": "v1",
"occurred_at": "2026-08-21T07:40:05Z",
"property_id": "prop_61aa…",
"run_id": "run_8c2e4d7a",
"data": { "item_count": 7, "items": [ … ] }
}' \
"https://your.app/hooks/seo-ops"
import hmac, hashlib
def verify(raw: bytes, header: str, secret: str) -> bool:
digest = hmac.new(secret.encode(), raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(header, f"sha256={digest}")
@app.post("/hooks/seo-ops")
def hook(req):
if not verify(req.body, req.headers["X-Agency-Signature"], SECRET):
return 401
for item in req.json["data"]["items"]:
clickup.create_task(list_id, name=item["title"],
description=item["detail"],
priority=item["impact"])
return 200
Or skip the endpoint: GET …/export?format=json pulls the open backlog whenever you want it.
Hi Dana — this week’s pass over summitgear.example found seven things worth doing. Two are already in progress on our side.
- /packs/ultralight-frames/ — page 2 for ‘ultralight pack frame’, 4.2k impressions, no clicks. Title and meta rewrite. In progress.
- /guides/trail-blazes/ — ranks 5, no clicks; a featured snippet is taking them. FAQ schema + opening paragraph. In progress.
- /blog/19-knots-every-hiker-should-know/ — 89 queries land here; we’ll read it before touching it.
Four more on your board · Homepage as we saw it on Aug 21 attached
| Authorization | Bearer $KEY |
| Content-Type | application/json |
{
"digest": {
"recipients": ["owner@summitgear.example"],
"cadence": "weekly",
"from_name": "Brindlewhaite Outfitters"
}
}
{ "digest": { "recipients": 1, "cadence": "weekly", "next_send": "2026-08-25T14:00:00Z" } }
curl -X PATCH \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{
"digest": {
"recipients": ["owner@summitgear.example"],
"cadence": "weekly",
"from_name": "Brindlewhaite Outfitters"
}
}' \
"/v1/properties/$PROPERTY_ID"
Your name on it, your sending domain (SPF/DKIM, verified once). The homepage capture is the one Scout rendered during the run — proof of work without writing the email.
A site still finishing its basics. The recipe is a sequence, not a mix: snippets first, then hygiene, batched into one campaign so four look-alike rewrites read as one job.
- Snippet ×4 — one campaign, four pages
- Hygiene ×3 — verify indexation before rewriting
- 3 held ·
ship_range_full(7/7)— the band for this shape is 5–7. Nothing padded, nothing dropped.
A dense site with plenty to choose from. The recipe composes: at most two of any shape, every strong lens represented, one slot always reserved for a risk.
- Snippet ×2 · Schema ×2 · Content ×2
- Check first ×1 — the guard slot, never crowded out
- 13 held ·
shape_slots_full(on_page_rewrite, 2/2)×9 ·shape_slots_full(structured_add, 2/2)×2 ·shape_slots_full(verify_first, 2/2)×2 — no week is seven rewrites.
Every candidate is kept. The ones that did not ship are in the run’s record with the rule that held them — you can disagree with a rule; you cannot be padded.
{
"recipe": "FOUNDATION",
"ship_range": [5, 7],
"stats": {"n": 10, "basics_share": 0.70, "invisible_layer": false},
"campaigns": {"c3": "ctr_fix_on_page_rewrite_pass", "c4": "…", "c6": "…", "c8": "…"},
"shipped_n": 7,
"held_n": 3,
"held": [
{"candidate": {"shape": "structured_add", "lens": "structure_schema", …},
"reason": "recipe:ship_range_full(7/7)"},
…
]
}
{
"recipe": "COMPOSE",
"ship_range": [7, 9],
"stats": {"n": 20, "basics_share": 0.45, "invisible_layer": false},
"guard_used": true,
"shipped_n": 7,
"held_n": 13,
"held": [
{"candidate": {"shape": "on_page_rewrite", "lens": "ctr_fix", …},
"reason": "recipe:shape_slots_full(on_page_rewrite, 2/2)"},
…
]
}
Where this is going: MCP — the same key, spoken natively by your agent — is next. Today, agents work against the API; the guide inside the console walks through it.
Point it at your own site and see.
Sign up, run Scout on your agency's own site, and read the backlog it files. You will know within one run whether the recommendations are any good — which is the point of starting somewhere you can check the work.
Already have an account? Sign in.