Research Scan
Type to search documentation.

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.

Terminal window
research-scan doctor # a four-line summary and a verdict
research-scan doctor --verbose # every check, with timings and paths
research-scan doctor --json # machine-readable; this is the CI/agent interface

The 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 store
Ready.

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.13
ok ! config path ~/.config/research-scan
ok ! cache path ~/.cache/research-scan
ok OPENALEX_API_KEY **** from user-config
ok OPENALEX_MAILTO **** from user-config
ok S2_API_KEY **** from user-config
ok NCBI_API_KEY **** from user-config
ok ! 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.

Terminal window
research-scan doctor --json
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 / skipskip 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