The fastest way to get a team to actually look at SEO scores is to put them where the team already lives. For most engineering and marketing teams, that's Slack. This post builds a /seo slash command that audits any URL and posts the score, grade, and top priorities back into the channel — no dashboard to open, no login, no context switch.
It's about 60 lines of Node on top of the SEO Score API and the official SDK. You can run it on anything that serves HTTP: a small Express server, a Lambda, a Cloudflare Worker, or a Fly.io machine.
What the bot does
Someone types /seo https://example.com in any channel. Within a couple of seconds, the bot replies:
SEO Score for example.com: 87 (B)
Top fixes:
• Meta description is 64 chars — aim for 140–160
• 3 images missing alt text
• No canonical tag on this URL
That's the whole product. The value is the zero-friction surface: a PM can sanity-check a landing page before a launch, a developer can confirm a staging fix, and an SEO lead can audit a competitor without leaving the thread.
The Slack mechanics you have to get right
Slack slash commands have one hard rule that bites everyone on the first build: you must respond within 3 seconds, or Slack shows the user a timeout error. An SEO audit takes longer than that — we render the page in a real browser. So you can't audit-then-reply inline.
The fix is Slack's two-phase pattern:
- Immediately return a
200with an "on it" acknowledgement. - Do the real work, then POST the result to the
response_urlSlack handed you.
That response_url is valid for 30 minutes and accepts up to 5 delayed responses. This is the single most important thing to understand about Slack bots, so the code below is structured around it.
The code
npm install express seoscoreapi. One file:
const express = require("express");
const { audit } = require("seoscoreapi");
const app = express();
app.use(express.urlencoded({ extended: true }));
const API_KEY = process.env.SEO_KEY;
app.post("/slack/seo", async (req, res) => {
const url = (req.body.text || "").trim();
const responseUrl = req.body.response_url;
if (!url) {
return res.send("Usage: `/seo https://example.com`");
}
// Phase 1: acknowledge within 3 seconds
res.send(`:hourglass_flowing_sand: Auditing ${url}…`);
// Phase 2: do the slow work, then post to response_url
try {
const result = await audit(url, API_KEY);
const fixes = (result.priorities || [])
.slice(0, 3)
.map((p) => `• ${p.issue}`)
.join("\n");
await fetch(responseUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
response_type: "in_channel", // visible to the whole channel
text: `*SEO Score for ${new URL(result.url).hostname}:* ${result.score} (${result.grade})\n*Top fixes:*\n${fixes || "None — clean audit. :tada:"}`,
}),
});
} catch (err) {
await fetch(responseUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
response_type: "ephemeral", // only the requester sees errors
text: `:x: Couldn't audit ${url} — ${err.message}`,
}),
});
}
});
app.listen(3000, () => console.log("SEO bot listening on :3000"));
The audit() call is the entire integration with our API — the SDK handles the X-API-Key header and the /audit endpoint for you. Everything else is Slack plumbing.
Wiring it into Slack
In the Slack app config, create a new app, add a Slash Command called /seo, and point its Request URL at https://your-host/slack/seo. Install the app to your workspace. That's it — no OAuth scopes beyond commands.
One thing worth doing before you ship to a real workspace: verify the Slack signing secret on every request. Without it, anyone who learns your URL can trigger audits and burn your quota. Slack signs each request with an X-Slack-Signature header; verify it against your app's signing secret before processing. It's ~10 lines and it's the difference between a toy and something you'd run for a company.
Why post in_channel instead of privately?
Because the social proof is the point. When one person audits a URL and the whole channel sees the score, SEO stops being one person's job and becomes ambient. We've watched teams go from "nobody checks scores" to "every launch thread has a /seo in it" purely from the result being visible. If you'd rather keep it quiet, switch response_type to ephemeral and only the requester sees the reply.
How much quota does a Slack bot actually use?
Far less than you'd expect. A /seo command is a deliberate human action, not an automated loop — a busy 30-person team might run 50–100 audits a month. That fits comfortably in the Starter plan ($5/mo, 200 audits), and even a heavy multi-team workspace rarely exceeds Basic ($15/mo, 1,000 audits). Compare that to a per-seat SEO platform and the math isn't close.
Adding a richer reply
The minimal version posts three fixes. Two upgrades worth making once the basics work:
Category breakdown. The audit response includes audit.accessibility.score, audit.performance.score, and so on. Post them as a row of Slack Block Kit fields so the team sees where the score is weak, not just the headline number.
A shareable report link. The SDK's reportUrl(domain) returns a public, branded report page. Append it to the reply so anyone can click through to the full breakdown — handy when the three-line summary isn't enough. This is the same report surface covered in White-Label SEO Reports.
Can I audit staging or preview URLs from Slack?
Yes, as long as the URL is reachable from the public internet — the audit runs from our servers, not from your laptop. A Vercel or Netlify preview URL works directly. A localhost URL or a site behind a VPN won't, because our browser can't reach it. For gated preview environments, the same bypass-token approach from Catching SEO Regressions in Vercel Preview Deploys applies here.
Where to take it next
The slash command is the entry point. Once a team is comfortable with /seo, the natural follow-ons are a scheduled digest that posts your key pages' scores to a channel every Monday, and a regression alert that pings the channel when a monitored URL drops. Both reuse the exact same audit() call — see Automated Weekly Client SEO Reports for the scheduling pattern, and bring your AI assistant into the same workflow with the official MCP server.
A slash command that takes a weekend to build changes how a team relates to SEO. That's a good trade.