Dromad for coding agents
This page is for an agent asked something like "Investigate our visibility for AI search APIs." Dromad gives you evidence — what ChatGPT, Claude and Gemini answered, what they cited, how pages look to a crawler — and counts it. Interpreting it is your job: Dromad does not explain, recommend or score.
Every command accepts --json and prints one JSON document on stdout: the same object the API returns. Use --json whenever you will read the output programmatically.
With --json, that document is the whole output. stdout carries exactly one JSON document — the result, or the error envelope — and stderr carries nothing: no progress, no cost line, no balance. So dromad questions "<topic>" --json > out.json 2>&1 leaves a file that parses. What the progress lines say without --json is in the document (charge) and in dromad balance --json. The one exception is dromad login, which prints the sign-in URL and code on stderr because a person has to act on them. Read the exit code as well as the document: it is listed under Reading the JSON.
Check the CLI version
dromad --version
The server answers dromad 0.2.0 and newer. An older CLI is refused on every command with cli_outdated (exit code 2): nothing was run or charged, and the message gives the installed version, the required one and the upgrade command (uv tool upgrade dromad or pip install -U dromad). This matters because commands have changed meaning: before 0.2.0, dromad fanout generated questions; it now runs prompts on every engine, and dromad questions generates questions. If a command here is missing from dromad --help, the CLI is old: upgrade before doing anything else, and do not guess at an older command's meaning from this page.
Authenticate
Check first:
dromad whoami --json
It fails with not_authenticated (exit code 2) when there is no credential, and with unauthenticated (exit code 1) when the server refuses the one there is, because it was revoked or is unknown. Either way, ask the user either to run dromad login (opens a browser) or to create an API key at https://dromad.dev/account/api-keys and set DROMAD_API_KEY. Never print a key.
Check who, not only whether. When it succeeds, read email: a credential existing does not mean it is the account the user means, and the account is the one whose workspace pays. dromad login prints a code and a URL, and the token belongs to whichever account the browser session that approves the code is signed in to. The CLI cannot know that account beforehand, and a user with several browser profiles can approve from the wrong one without noticing. So:
- If the user has named an account, sign in with
dromad login --email <address>. It runs the same browser flow, and if another account approves it, it stores nothing, revokes the token that was issued and fails withaccount_mismatch(exit code 1), leaving any earlier credential as it was. Then ask the user to open the URL in the browser profile that is signed in to that address, and run it again. - After any sign-in, confirm
dromad whoami --jsonshows theemailyou expect before spending anything.loginalso printsLogged in as <email>. - An API key has no such ambiguity: it is made in one account, for one workspace. On a machine with several browser profiles, prefer
DROMAD_API_KEY.
Check the project
Work is filed under a project, and the directory you are in is normally linked to one:
dromad status --json
It prints the workspace, the project and the project's context (its domain, brands and competitors, which tell you what the work is about). It fails with not_linked (exit code 2) when the directory has no project. Then ask the user which project to use, or whether to start one, and run dromad link --project <pr_…> or dromad init --name "<name>" --workspace "<workspace>"; dromad list --json shows the choices. Both take flags for everything and ask nothing under --json. Do not invent a project for the user's work without asking: the workspace it is in is the one that pays. dromad unlink forgets the link.
Commands, and when to use them
| Goal | Command | Cost |
|---|---|---|
| See what each engine actually searches for a prompt | dromad fanout "<prompt>" --runs 3 --yes --json |
its prompt runs: prompts × engines × runs (--estimate prints it first) |
| Generate questions people might ask about a topic | dromad questions "<topic>" --json |
$0.002 a question generated |
| See what the engines answer and cite for a question | dromad prompt "<question>" --json |
$0.05 ChatGPT, $0.20 Claude, $0.07 Gemini; $0.32 on all three, the default |
| Count citations across many runs; check a domain's coverage | dromad citations <rq_…/dr_…> --domain <d> --brand <b> --json |
free |
| Inspect one page as a crawler sees it | dromad audit <url> --json |
$0.01 |
| Find earlier work | dromad runs list --json, dromad fanouts list --json, dromad analyses list --json |
free |
| Read one object in full | dromad runs show <dr_…> --json, dromad fanouts show <fo_…> --json, dromad analyses show <fa_…/ca_…> --json |
free |
| Give the user a link | dromad share <id> (and dromad unshare <id>), dromad open <id> --print |
free |
| Check the credential, and whose it is | dromad whoami --json, dromad login --email <address>, dromad logout |
free |
| Check or choose the project | dromad status, dromad list, dromad link, dromad init, dromad unlink |
free |
| See what each operation costs | dromad pricing --json |
free |
| See what credit is left before a large batch | dromad balance --json |
free |
How the commands compose
A typical investigation:
- Questions.
dromad questions "AI search API" --jsonreturnsquestions(each withid,text,cluster_id,intent,specificity) andclusters. Choose the questions that matter for the user's goal — for visibility, the ones where an answer would name providers (discovery, comparison, pricing). These are generated, not observed: a list of prompts worth trying. - Fanout (when the question is what engines search).
dromad fanout "<prompt>" "<prompt>" --runs 3 --yes --jsonrecords the search queries each engine issued for each prompt, run by run. Use it to learn the vocabulary engines search with before deciding what a page should answer. - Prompt runs. Run each chosen question:
dromad prompt "<question>" --json. Keep the batch ID from.batch.id. To start several without waiting, use--no-waitand collect the IDs;dromad runs list --batch <rq_…> --jsonshows their progress. - Citations. Pass all the batch IDs to one analysis:
dromad citations rq_… rq_… --domain their-site.com --domain competitor.com --brand Their --brand Competitor --json. Readdomain_coverage(cited runs and prompts per domain, per engine, the pages cited, and the prompts where the domain was absent or seen but not cited),domains,pagesandoverlap. An analysis counts answers already stored, and finished runs never change: running it again over the same runs with the same domains and brands gives the same numbers and only adds a duplicate. Before creating one, checkdromad analyses list --kind citations --jsonand read an existing one withdromad analyses show <ca_…> --json. Create a new analysis only when the question changed (other domains or brands), the runs changed (new prompt runs, or a different subset such as one engine), or the analysis predates a change to how Dromad counts (itsmethod; the current one iscitations/v2). To learn whether anything changed in the engines themselves, run new prompts; no analysis of old runs can show it. - Audits. Audit the user's pages the engines cited, the ones they skipped, and the competitor pages they cited most:
dromad audit <url> --json. - Report. Combine the evidence with your own reasoning and the user's repository. Link the analysis (
dromad share ca_…) so the user can check the numbers.
Reading the JSON
- Prompt:
{"batch": {...}, "runs": [...]}. In each run,result.answeris the text andresult.sources[]hasurl,domain,cited(true/false/null),citation_countandcitation_positions.nullmeans the provider does not expose it: never count it as "not cited". - Questions, citations: the analysis document, with results at the top level (
.questions,.domain_coverage). - Fanout:
.prompts[].engines[].runs[].queries[]. Each query hastextandsearch_call: searches are in the provider's order, and queries with the samesearch_callwere issued together, with no order among them. Nothing is merged across runs, so the sametextunder two runs is two observations andquery_countcounts observations; when you compare runs or engines, say how many runs a query appeared in rather than treating the union as one search. A run with no queries hassearchedtrue(the provider hid the text),false(it did not search) ornull(not recorded): never readnullas "did not search". Eachrun_idis a prompt run with the answer, sources and raw envelope. - Audit:
dromad audit --jsonprints the audit result withrun_idat the top (.findings[],.content,.metadata);dromad runs show <dr_…> --jsonprints the run with the same result under.result. Each finding haskey,status(pass/fail/not_applicable, orerrorwhen the check itself failed — never read that as a pass),severityandevidence_level. Weighsuggestiveandexperimentalfindings accordingly. - Errors:
{"error": {"code", "message", "engine", "retryable"}}. Retry only whenretryableis true. - Exit codes:
0done (a prompt where some engines failed is still0, its batchpartial),1the operation failed,2it could not be attempted (arguments, credentials, no project, a CLI too old, a fanout that needed--yes).
The full schema is at https://dromad.dev/api/v1/openapi.json.
Spend the user's credit with care
- Look before you run.
dromad runs list --jsonanddromad analyses list --jsonshow earlier work. A finished run never changes; reuse it instead of re-asking the same question. - Choose engines. A prompt is priced per engine.
--engines chatgptis $0.05 where all three are $0.32, when one engine answers the question you have. - Choose questions.
dromad questionsgives 30 by default; running all of them on three engines is 30 × $0.32 = $9.60. Pick the ones that bear on the goal, say which you picked, and say what the batch will cost before you run it. - A fanout multiplies. It costs prompts × engines × runs prompt runs, and
--runsis the multiplier: 3 is enough to see whether an engine searches the same way twice, 5 is the most allowed. Rundromad fanout … --estimate --jsonfirst and tell the user the estimated total; it also reportsavailable_usdand whether that issufficient. Without a terminal the command starts nothing unless you pass--yes: it fails withconfirmation_required(exit code 2). The estimate is informational, not a quote: the fanout starts only if the workspace's credit covers all of it when it is created, and otherwise fails whole withinsufficient_credit. One fanout queues at most 30 prompt runs (a queue limit, not a spending one): split more prompts across several fanouts. - Check the balance first.
dromad balance --jsongivesavailable_usd. A request the credit does not cover fails whole withinsufficient_creditand charges nothing; it will not succeed until the user adds credit at the URL in the message, so ask them rather than retrying. - Citation analyses are included. Re-slice existing runs with new
--domainand--brandvalues rather than running new prompts. - A failed run costs nothing, but don't retry blindly. It records its error; check
retryablefirst. - Mind the account's limits.
rate_limitedmeans the account has too many runs unfinished. Wait for runs to finish instead of retrying in a loop.
What not to claim
Dromad's numbers come from specific runs at a specific time; engines change, and the same question can be answered differently tomorrow. Report counts with their denominators ("cited in 9 of 24 runs"), say which engines and questions they come from, and keep "unknown" distinct from "no". Brand counts are naive whole-word matches. Audit findings describe one fetch of one page.