seomatic
v0.3.0
Published
SEO + agent-readiness audits from your terminal, and the full SEOmatic API - Search Console data, insights, and every agent tool - for scripts and coding agents.
Maintainers
Readme
seomatic
SEO and agent-readiness audits from your terminal, plus the full SEOmatic API - Search Console data, insights, and every agent tool - for scripts and coding agents.
No install, zero dependencies, Node >= 18.
npx seomatic audit https://example.comTwo commands (audit, logs) need no account and no key. For everything
else, run seomatic login once: it opens your browser, you approve, and the
key comes back to the terminal.
Contents
- Quick start - Commands - Authentication
- Global flags - Environment variables
- Exit codes - JSON output
- Scripting recipes - Troubleshooting
Quick start
# 1. Audit any URL. No account.
npx seomatic audit https://example.com
# 2. Find crawl-budget waste in a server log. No account.
npx seomatic logs /var/log/nginx/access.log
# 3. Sign in (creates a free account if you have none), then read your
# Search Console data.
npx seomatic login
npx seomatic gsc top-queries --days 28Commands
Notation: <required>, [optional], a|b alternatives.
seomatic audit <url>
Auth: none. Cost: free.
Fetches one URL and runs 10 checks (11 when the site has a robots.txt) covering both classic SEO and whether an AI answer engine can read the page.
npx seomatic audit https://example.com
npx seomatic audit https://example.com --jsonScored checks: reachable, title, meta_description, canonical, h1,
json_ld, robots_txt, ai_bots_allowed (only when robots.txt exists),
sitemap.
ai_bots_allowed fails only when an AI search crawler is blocked
(OAI-SearchBot, Claude-SearchBot, PerplexityBot). A blocked training crawler
(GPTBot, ClaudeBot, Google-Extended) is noted in detail but does not fail:
it keeps content out of model training, not out of AI search.
Shown but not scored ("scored": false): llms_txt (optional; no measured
effect on AI citations) and security_txt.
{
"url": "https://example.com",
"score": 30,
"passed": 3,
"total": 10,
"checks": [{ "id": "reachable", "ok": true, "detail": "HTTP 200 in 133ms" }]
}score is passed / total over the scored checks, as a percentage. Exit is 0 even when checks fail -
a failing check is a finding, not a CLI error. Branch on score or passed, not
on the exit code. See Scripting recipes for a CI gate.
seomatic logs <file>|-
Auth: none. Cost: free.
Parses a combined-format access log and reports what crawlers actually fetched,
which is the only direct evidence of how your crawl budget is spent. Pass - to
read stdin.
npx seomatic logs access.log
zcat access.log.*.gz | npx seomatic logs -
npx seomatic logs access.log --jsonBots are identified by user agent, so the log must include the UA field (the last quoted field in combined format). A log without user agents parses cleanly and reports zero bots.
seomatic logs (3 lines parsed, 0 skipped)
bots: 100% of traffic (3/3) AI bots: 33% (1)
Googlebot 2 hits, 2 paths, 50% wasted (4xx/5xx)
1 /a
1 /b
OAI-SearchBot [AI] 1 hits, 1 paths, 0% wasted (4xx/5xx)--json returns { lines, totals, bots[] }, where each bot carries
isAiBot, hits, uniquePaths, statuses, wastedCrawlPct and topPaths.
wastedCrawlPct is the share of that bot's requests answered 4xx or 5xx: crawl
budget spent on nothing.
seomatic login
Opens your browser on SEOmatic's approval screen. Sign in (or create a free account), pick the workspace, approve, and the key lands in your terminal. No copying from the dashboard.
- Picking a workspace needs the admin role there, the same right as creating a key in Settings. The screen only lists workspaces you administer.
- The key is kept in the OS credential store: the macOS Keychain, the
Linux keyring (GNOME Keyring / KWallet, when a desktop session is
running), or encrypted with Windows DPAPI to your Windows account.
Where none is available (a bare server) it goes to
~/.config/seomatic/credentials.json($XDG_CONFIG_HOMEand%APPDATA%are respected), readable only by you.seomatic mesays which (--json:cli.key_storage). If the OS store is locked (a Mac keychain over SSH), commands say so (KEYRING_LOCKED) instead of claiming you are signed out.SEOMATIC_KEYRING=filealways uses the file. - It is valid for one year and shows in Settings -> AI Agents -> API Keys
as
OAuth: SEOmatic CLI (<computer name>). Each computer gets its own key; logging in again on the same one replaces it. - On a server, over SSH, or in a container,
seomatic login --deviceprints a short code (BCDF-GHJK) and a link. Open the link on any computer or phone, sign in, type the code, and approve; the terminal picks the key up within seconds. It is chosen automatically over SSH and on Linux with no display (--browserforces the browser flow). Codes last 10 minutes and work once. Only type a code that your own terminal showed: SEOmatic never sends anyone a sign-in code. --no-browserprints the browser link instead of opening it. That link must be opened on the same computer, because the approval hands the key back to a listener on127.0.0.1; elsewhere, use--device.loginrefuses to run in CI (CIset): useSEOMATIC_API_KEYthere.
seomatic logout
Revokes the saved key on SEOmatic and removes it from this computer. If SEOmatic cannot be reached, the local copy is still removed and the command tells you to revoke the key in Settings.
seomatic me
Auth: key. Introspects the key: workspace, scopes, what the plan allows
(acting over the API, webhooks, Zapier), GSC connection, and where the key came
from. Run it first when something is not working. whoami is an alias.
npx seomatic me --jsonseomatic gsc top-queries|top-pages
Auth: key, scope read:gsc. Reads the workspace's connected Search Console
property. Data is Google-finalized with a 2-3 day lag.
npx seomatic gsc top-queries --days 28 --limit 25
npx seomatic gsc top-pages --days 7 --json--days defaults to 28, --limit to 25.
seomatic tools [list]
Auth: key. Lists every tool this key can call, which depends on its scopes,
plan and connections. --group read|acts filters read-only from acting tools.
npx seomatic tools
npx seomatic tools --group read --jsonseomatic tools describe <name>
Auth: key. Shows one tool's description and parameters (? marks optional
ones). --json prints the full JSON Schema.
npx seomatic tools describe get_search_queriesseomatic tools call <name> [--data '<json>' | @file.json | -]
Auth: key. Invokes any tool by name, proxying POST /v1/tools/{name}. This
is the whole product surface: every keyword, backlink, SERP, analytics, task and
campaign tool, identical to what REST and MCP expose.
npx seomatic tools call get_search_queries --data '{"days":28}'
npx seomatic tools call create_seo_tasks --data @tasks.json --json
echo '{"days":7}' | npx seomatic tools call get_search_queries --data ---data takes inline JSON, @path to read a file (no shell quoting, and the
argument stays out of the process list), or - for stdin. It must be a JSON
object. Without --json the tool's result is printed as formatted JSON; with
--json the raw envelope { "tool", "result" } is printed, where result is
the tool's answer as a string.
Run seomatic tools for the list and seomatic tools describe <name> for a
tool's arguments. Acting tools need the agents:act scope and the
Infrastructure plan.
Tool calls are idempotent. Every tools call carries an
Idempotency-Key, so an acting tool (one that stages changes) runs at
most once however often the call is repeated. That makes the dangerous cases
safe:
- A dropped connection or a proxy 502/503/504 is retried with the same key: if an acting tool already ran, the retry returns its answer instead of running it again. A fast read tool is simply run again, which is harmless (SEOmatic does not keep read answers, only acting ones and slow ones).
- A tool slower than the API's 60s response limit keeps running on the
server (it is not stopped). The CLI waits for it, up to
--waitseconds (default 300), then prints its answer. - If it is still running after
--wait, the CLI exits 2 withTOOL_STILL_RUNNINGand the key. Collect the answer later (kept 24 hours for acting tools, 1 hour for reads) by running the same command with--idempotency-key <key>:
npx seomatic tools call create_seo_tasks --data @tasks.json --idempotency-key 3f0c...Pass your own --idempotency-key to make a script's retries safe across
runs. Reusing a key for different arguments is refused
(IDEMPOTENCY_KEY_REUSED), never silently answered with the old result. An
answer over 64 KB is returned the first time but not kept: a repeat gets
IDEMPOTENCY_RESULT_NOT_KEPT (the call ran; it is not run again).
seomatic completion bash|zsh|fish|powershell
Prints a tab-completion script: commands, flags, --group values, your
profile names, and tool names for tools call / tools describe (from the
last seomatic tools you ran, so completing never waits on the network).
seomatic completion bash >> ~/.bashrc
seomatic completion zsh > "${fpath[1]}/_seomatic" # then restart zsh
seomatic completion fish > ~/.config/fish/completions/seomatic.fish
seomatic completion powershell >> $PROFILECompletion needs seomatic on your PATH (npm i -g seomatic), not npx.
Authentication
On your own computer, run seomatic login (see above). For CI and scripts,
mint a key in the dashboard under Settings -> AI Agents -> API Keys (needs
the admin role on that workspace - if the section is not there, check
which workspace is selected and whether you are an admin on it rather than a
member), then pass it by environment variable (preferred) or --key:
export SEOMATIC_API_KEY=smk_live_...
npx seomatic mePrecedence: --key, then SEOMATIC_API_KEY, then the key saved by
seomatic login. Prefer the variable in CI so the key never lands in shell
history or a process list.
Several workspaces (agencies): give each one a profile.
npx seomatic login --profile acme # approve the Acme workspace
npx seomatic login --profile globex # approve the Globex workspace
npx seomatic gsc top-queries --profile acme
SEOMATIC_PROFILE=globex npx seomatic me
npx seomatic logout --profile acmeEach profile keeps its own key. Signing in again to the same profile revokes the key it replaces, so no old key is left alive on the server.
The key is only ever sent over https (plain http is allowed to localhost
for development); any other --api is refused before the key is read.
Scopes are the boundary:
| Scope | Unlocks | Cost |
| ------------ | ---------------------------------------- | ------------------- |
| read:gsc | Search Console reads, me, most tools | free |
| chat:ask | Ask-style tools | free |
| agents:act | Tools that stage changes to a site | Infrastructure plan |
Global flags
| Flag | Meaning |
| -------------------- | ------------------------------------------------------- |
| --json | JSON to stdout. Works on every command. |
| --key <key> | API key. Overrides SEOMATIC_API_KEY and your login. |
| --profile <name> | Use a separate saved login. Or env SEOMATIC_PROFILE. |
| --device | login with a code approved from any browser. |
| --browser | login opens a browser even over SSH. |
| --no-browser | login prints the link instead of opening a browser. |
| --wait <seconds> | How long tools call waits for a slow tool (300). |
| --idempotency-key | Collect or safely repeat an earlier tools call. |
| --api <base> | API base URL. Default https://app.seomatic.ai/api/v1. |
| --days <n> | Lookback window for gsc. Default 28. |
| --limit <n> | Max rows for gsc. Default 25. |
| --data <json> | Body for tools call: JSON, @file.json, or -. |
| --group read\|acts | Filter tools by read-only or acting. |
| --version, -v | Print the version and exit 0. |
| --help, -h | Usage and exit 0. |
Flags take --flag value or --flag=value. An unknown flag (including short
ones like -d; only -h and -v exist), a flag missing its value, or an
empty value (--key "", which would otherwise fall back to another saved
login) is a usage error (exit 1) rather than being ignored.
Environment variables
| Variable | Effect |
| -------------------------- | --------------------------------------------------------------- |
| SEOMATIC_API_KEY | API key (after --key, before your login). |
| SEOMATIC_PROFILE | Profile to use, like --profile. |
| SEOMATIC_KEYRING=file | Keep the saved key in the file, not the OS store. |
| SEOMATIC_CONFIG_DIR | Where credentials and caches live. |
| SEOMATIC_NO_UPDATE_CHECK | No update notice (so does NO_UPDATE_NOTIFIER). |
| NODE_USE_ENV_PROXY=1 | Node 22.21+/24: use HTTPS_PROXY (Node ignores it by default). |
Update notice. At most once a day, a background process asks npm for the
latest version; the next command prints one line on stderr if there is a
newer one. It never delays a command and never runs in CI, with --json, or
when output is not a terminal.
Exit codes
| Code | Meaning | Examples |
| ---- | ------------------ | ------------------------------------------------------------------------------------------------------------------ |
| 0 | Success | Command ran. Includes an audit where checks failed. |
| 1 | Usage or API error | Unknown command or flag, missing argument, no key, 401, 403, 402 plan gate. Also bare seomatic. |
| 2 | Runtime failure | Network unreachable, DNS failure, HTTP 5xx, 429 still hit after waiting, tool still running (TOOL_STILL_RUNNING) |
The distinction that matters in CI: 1 means you asked for something wrong, 2 means we could not complete it. Retry on 2, fix the call on 1.
The CLI already retries what is safe to retry before it exits:
- 429 on any command: waits for
Retry-After(up to 30s) and tries again, up to 3 attempts. The limit is checked before anything runs, so this never repeats work. A longer wait exits 2 withretry_afterin the JSON error. - Network errors and proxy 502/503/504: 3 attempts with backoff, on reads
and on
tools call(its Idempotency-Key makes the retry safe). - A slow tool is waited for, up to
--wait(seetools call). - A tool that failed (
TOOL_FAILED) is not retried: rerun the command, which uses a fresh key.
JSON output
With --json, stdout carries exactly one JSON value: the result, or on
failure an error object. Progress (such as login's instructions) goes to
stderr. Without --json, errors go to stderr as error: ....
npx seomatic audit https://example.com --json | jq .scoreError objects have one shape. Branch on code and status; error is a
human sentence and may change wording:
{
"error": "Free monthly limit reached (5 questions). Upgrade ...",
"status": 402,
"code": "FREE_QUOTA_EXCEEDED",
"upgrade_url": "https://app.seomatic.ai/..."
}code is the API's error code when it sent one (INVALID_API_KEY,
MISSING_SCOPE, SCOPE_REVOKED_BY_PLAN, BILLING_INACTIVE,
FREE_QUOTA_EXCEEDED, REST_ACT_REQUIRES_INFRA,
UNKNOWN_TOOL, INVALID_ARGUMENTS, TOOL_FAILED), or one of the CLI's
own: USAGE, NOT_SIGNED_IN, RATE_LIMITED, INSECURE_API_BASE,
INTERACTIVE_ONLY, KEYRING_LOCKED, LOGIN_FAILED, NETWORK_ERROR (nothing
was sent), TIMEOUT, TOOL_STILL_RUNNING and TOOL_STATE_UNKNOWN (both with
idempotency_key: repeat with it to collect or safely re-run), and the API's
IDEMPOTENCY_UNAVAILABLE (the tool was not run; retry). upgrade_url, details and retry_after appear when
they apply.
Scripting recipes
Fail a build when a page regresses:
score=$(npx seomatic audit "$URL" --json | jq -r .score)
[ "$score" -ge 80 ] || { echo "SEO score $score < 80"; exit 1; }List every check that failed:
npx seomatic audit "$URL" --json | jq -r '.checks[] | select(.ok|not) | .id'Find AI crawlers wasting budget on errors:
npx seomatic logs access.log --json \
| jq -r '.bots[] | select(.isAiBot and .wastedCrawlPct > 20)
| "\(.bot) \(.wastedCrawlPct)% wasted"'Retry only on runtime failures:
for i in 1 2 3; do
npx seomatic me --json && break
[ $? -eq 2 ] || break # 1 is our fault; do not retry
sleep $((i * 2))
doneTroubleshooting
| Symptom | Cause | Fix |
| ----------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------- |
| No API Keys section in Settings | Wrong workspace, or you are not an admin on it | It lives under Settings -> AI Agents -> API Keys and needs the admin role |
| not signed in | No login, no key | seomatic login, or export SEOMATIC_API_KEY=smk_live_... |
| saved sign-in is no longer valid | Key revoked in Settings, or a year old | seomatic login |
| Signing in on a server or over SSH | No browser on that machine | seomatic login --device (automatic over SSH) |
| TOOL_STILL_RUNNING (exit 2) | The tool ran past --wait; it was not stopped | Same command with --idempotency-key <key from the error> |
| Missing or invalid API key | Wrong or revoked --key / env key | Check the key in Settings, then seomatic me |
| A tool is missing from tools | Scope, plan or a missing connection | seomatic me shows scopes, plan and GSC state |
| gsc returns nothing | No connected property, or the 2-3 day lag | Connect Search Console; widen --days |
| Acting tool returns a plan gate | agents:act needs the Infrastructure plan | Upgrade, or use read-only tools |
| logs reports 0 bots | Log has no user-agent field | Use combined format, which ends with the quoted UA |
| could not reach ... | Network, DNS, or a wrong --api | Check connectivity; omit --api unless self-hosting |
| Fails only behind a company proxy | Node ignores HTTPS_PROXY by default | Node 22.21+ or 24: export NODE_USE_ENV_PROXY=1 |
| refusing to send your API key | --api is plain http to another host | Use the https URL |
Still stuck? seomatic me --json is the fastest first report to include.
See also
- Web docs: https://seomatic.ai/developers/cli
- REST API: https://seomatic.ai/developers/rest-api
- MCP server: https://seomatic.ai/developers/mcp
License
MIT
