Checking your setup
doctor invokes every source live with the cache bypassed, and reports readiness in three output modes that share one exit code.
doctor does not list what it thinks is configured. It invokes every source live
with the cache bypassed, and reports what actually answered.
research-scan doctor # a four-line summary and a verdictresearch-scan doctor --verbose # every check, with timings and pathsresearch-scan doctor --json # machine-readable; this is the CI/agent interfaceThe checks and the exit code are identical in all three modes. Only the presentation differs.
The summary
Research Scan 0.5.1✓ configuration✓ OpenAlex ✓ Semantic Scholar ✓ Crossref ✓ arXiv ✓ PubMed✓ writable run storeReady.Progress lines go to stderr; only this block is stdout. --quiet silences the
stderr log.
Exit 0 means go. Exit 3 means a mandatory check failed — read the check name and fix that, rather than working around it.
The full check list
--verbose gives every check with its timing, plus the paths in use.
research-scan doctor — v0.5.1, python 3.13.13
ok python 3.13.13ok ! config path ~/.config/research-scanok ! cache path ~/.cache/research-scanok OPENALEX_API_KEY **** from user-configok OPENALEX_MAILTO **** from user-configok S2_API_KEY **** from user-configok NCBI_API_KEY **** from user-configok ! openalex search 4238497 hits, is_retracted readable, cost $0.001 [587 ms]ok openalex psyarxiv-doi 10.31234/osf.io/mky9j indexed as preprint [506 ms]ok ! s2 search 1 hit(s) [1142 ms]ok ! s2 references 1 reference(s) — graph expansion available [536 ms]ok crossref lookup 10.1038/s41586-021-03819-2 resolves [376 ms]ok arxiv query parsed a candidate for 'electron' (arXiv:2608.19185) [58 ms]ok pubmed esearch esearch returned pmid 42622061 [458 ms]
paths: config_env: ~/.config/research-scan/.env local_env: ~/.env cache_db: ~/.cache/research-scan/http.sqlite
ready(! marks a check that can exit 3)Credential rows show the last four characters of the real value and where it came from; the values above are masked. Hit counts, arXiv ids, PubMed ids and timings are whatever the live APIs returned at that moment and will differ on every run.
--sources openalex,s2 narrows what is checked.
Reading it from a script or an agent
doctor --json is the interface, and its keys are stable.
research-scan doctor --json{ "version": "0.5.1", "ready": true, "providers": { "openalex": "ok", "s2": "ok", "crossref": "ok", "arxiv": "ok", "pubmed": "ok" }, "config": "ok", "run_store": "ok", "paths": { "config_env": "~/.config/research-scan/.env", "local_env": "~/.env", "cache_db": "~/.cache/research-scan/http.sqlite" }, "checks": [ { "name": "openalex search", "status": "OK", "detail": "4238497 hits, is_retracted readable, cost $0.001", "mandatory": true, "duration_ms": 930.1 } ]}Abridged: the real output carries every check in checks, plus a keys object
with each credential’s presence, masked value and origin.
ready is the boolean. providers maps each checked source to ok / warn /
fail / skip — skip means the probe never ran, which is never the same as
passing. checks carries every individual result.
When a check fails
A missing OPENALEX_API_KEY is a FAIL, and the OpenAlex probes are then
reported as SKIP rather than as passing, because they were never run.
A missing S2_API_KEY or NCBI_API_KEY is only a WARN. The scan still
runs, throttled.
arxiv HTTP 429 is a WARN, not a failure. arXiv rate-limits aggressively —
the client already holds to one request every three seconds — and returns 429
under load. A cs scan falls back to OpenAlex and Semantic Scholar, which carry
arXiv preprints anyway: you lose same-day listings and one origin signal, not
coverage. Re-run later if recency matters.
Semantic Scholar answers 429 or 503 when its queue is deep. The HTTP client
retries and then returns the last response rather than raising — a bad status is
data, not an exception. Graph expansion falls back to OpenAlex’s bare-id reference
list, which works but cannot be ranked, so expansion recall drops. It is recorded
per call in expansion.log.jsonl.
One side effect worth knowing
doctor is very nearly read-only, but not entirely: it creates
~/.config/research-scan and ~/.cache/research-scan if they do not exist, and
writes then immediately deletes a probe file in each to prove the run store is
writable. That is the writable run store check.
Last updated Aug 21, 2026