Skip to content
2 min read

Bulk lookups

Pipe files of keywords or domains into SEOCTL and stream normalized JSONL results.

Atomic lookups answer one question. Bulk lookups answer thousands — and they stay just as predictable.

When you pass --bulk, SEOCTL emits JSONL: one JSON object per line, one result per input row, in input order. That maps cleanly onto a pipeline, a spreadsheet, or an agent loop.

Bulk keyword volume

seoctl volume --bulk accepts a plain text file with one keyword per line:

agent seo tools
keyword research
seo api
seoctl volume --bulk keywords.txt --country us
seoctl volume --bulk keywords.txt --global --fields keyword,volume,scope,top_countries

It also reads the first column of a CSV, skipping a keyword header if present:

keyword
agent seo tools
keyword research
seoctl volume --bulk keywords.csv --country us --fields keyword,volume,source

Use --country for a specific market and --global for worldwide demand. Global bulk results include a compact country distribution for each keyword.

Bulk rank checks

seoctl rank --bulk expects a CSV of domain,keyword rows:

domain,keyword
kdnuggets.com,agent seo tools
stripe.com,payment processing software
seoctl rank --bulk rankings.csv --country us --depth 100

Rank credit estimate: depth 1-50 costs 2 credits per row, 51-100 costs 4, 101-150 costs 6, and 151-200 costs 8. The default --depth 100 checks the top 100 organic positions and costs 4 credits per row.

Bulk URL status

seoctl url status --bulk checks local HTTP status, redirects, canonical, robots meta, X-Robots-Tag, and indexability basics for many URLs. It does not use SEOCTL API credits.

https://example.com/
https://example.com/missing
seoctl url status --bulk urls.txt --fields url,status_code,final_url,indexable,indexability_reasons

It also reads the first column of a CSV, skipping a url header if present:

url,note
https://example.com/,home
https://example.com/missing,missing page
seoctl url status --bulk urls.csv --limit 100 --concurrency 8 --fields url,status_code,final_url,indexable

Failed rows still emit one JSONL object with ok:false, the input URL in query.url, and a compact error string.

Read from standard input

Use - as the bulk path to read from stdin. This is the cleanest way for an agent or a script to pass data without a temp file:

printf '%s\n' "seo api" "rank tracking" | seoctl volume --bulk - --country us
printf '%s\n' https://example.com https://example.com/missing | seoctl url status --bulk - --fields url,status_code,final_url

Missing rows

For country-scoped bulk volume, some keywords may come back without data on the first pass. In direct-provider diagnostics, --repair-missing can recheck those rows as exact single-keyword live lookups so a bulk run and an atomic run agree. Disable it with --repair-missing=false if you want the raw first pass.

Tips

  • Pair --fields with bulk commands to keep each line short.
  • JSONL streams as it completes — you do not wait for the whole batch.
  • A failed row still produces one line: a compact JSON error you can branch on.

Next

See the full flag surface in the Command reference, or wire bulk lookups into a coding agent with Agent setup.