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
--fieldswith 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.