Skip to content
goodread

Configuration

The config file, the data directory, the store, politeness, environment, and global flags.

goodread needs almost no configuration. The defaults are chosen so the common case needs none, and everything is a flag. See everything it resolved with:

goodread info

That prints the configuration, the paths and the affiliation disclaimer.

Precedence

Flag, then environment, then config file, then default. The flag always wins, because a run that behaves differently on two machines with the same command line is a run nobody can help you debug.

The config file

Optional, and there is no default one to write. It lives at ~/.config/goodread/config.toml, or $XDG_CONFIG_HOME/goodread/config.toml, or wherever GOODREAD_CONFIG points.

# ~/.config/goodread/config.toml
pace       = "3s"
depth      = "full"
cache_ttl  = "72h"
no_cache   = false
store      = "~/books/goodread.db"
data_dir   = "~/books"
user_agent = "yourname/1.0 (+https://example.com)"
format     = "jsonl"
timeout    = "45s"
retries    = 5
Key Overriding flag
pace --delay
depth --depth
cache_ttl --cache-ttl
no_cache --no-cache
store --store
data_dir --data-dir
user_agent --user-agent
format --format
timeout --timeout
retries --retries

Every key has a flag, and a key that cannot be a flag does not belong in the file. The grammar is key = value and # comment, which is deliberately less than TOML: a config file the flags cannot express is one nobody can reproduce from a command line.

A missing file is not an error. A file that is there and does not parse is an error, and exits 2, because the alternative is running with settings you think you changed.

no_robots is not a key. Setting it is an error with a message saying why, rather than a silent ignore, so nobody spends an afternoon wondering what it does. The override's whole justification is that a person decided, this invocation, that it was their call, and a config key would make it ambient.

The data directory

goodread keeps its state under one tree: the on-disk page cache and the SQLite store. It defaults to the XDG data directory, which is ~/.local/share/goodread on Linux. Move it with --data-dir or GOODREAD_DATA_DIR.

The store

The graph and the crawl frontier live in a SQLite file, by default <data-dir>/goodread.db. Point that single file somewhere else with --store, which is how you keep one corpus per project:

goodread crawl --seed gr:genre/science-fiction --store ~/projects/sf/goodread.db

find, query, graph, export, db and mcp all read that file.

Caching

Every fetch goes through a content-addressed gzip cache on disk, so a repeat run does not re-fetch unchanged pages. --cache-ttl sets how long an entry stays fresh, default 24 hours. --no-cache bypasses it for one run, and --refresh forces a re-fetch and rewrites the entry. Manage it with cache info, cache path <url> and cache clear.

Politeness

Flag Default Meaning
--delay 2s Minimum spacing between requests, with a hard floor of 1s
--timeout 30s Per-request timeout
--retries 3 Retry attempts on 429 and 5xx
--user-agent goodread/<version> (+<repo>) The header sent

A --delay below one second is clamped, with a note on stderr saying so. It is neither honoured nor silently dropped.

There is no --workers and no other parallelism flag. There was one in v0.2.0, clamped to a maximum of one, so it did nothing except suggest the tool could be told to open ten connections. A flag that does nothing but imply that is worse than no flag.

--no-robots

--no-robots   read paths that robots.txt disallows

It has no config key and no environment variable, on purpose. It has to be typed, every time, by the person running the command. It warns once on stderr, the pace floor still applies, a crawl with it needs --yes as well, and the MCP server ignores it entirely.

See robots.txt and what it costs.

Environment variables

Variable Used for
GOODREAD_DATA_DIR Root data directory, overriding the XDG default
GOODREAD_CONFIG Path to the config file
XDG_DATA_HOME Where the data directory defaults under
XDG_CONFIG_HOME Where the config file defaults under

Global flags

Flag Default Meaning
-f, --format auto table, json, jsonl, csv, tsv, url, raw
--json off Shorthand for --format json
--fields all Comma-separated columns to include
--no-header off Omit the header row in table, csv and tsv
--template none Go text/template applied per record
--color auto auto, always or never
-n, --limit 0 Maximum rows, 0 is all
-q, --quiet off Suppress progress on stderr
-v, --verbose off Explain what is read, -vv adds every request and the ladder
--depth meta How much to read: quick, meta, full, deep
--delay 2s Minimum spacing between requests
--timeout 30s Per-request timeout
--retries 3 Retry attempts on 429 and 5xx
--cache-ttl 24h On-disk cache freshness window
--no-cache off Bypass the on-disk page cache
--refresh off Force a re-fetch and overwrite the cache
--data-dir XDG Root directory for cache and store
--store <data-dir>/goodread.db SQLite store path
--user-agent goodread/<version> User-Agent header
--no-robots off Read paths robots.txt disallows

Output auto-detection

The default output format adapts to where it is going: an aligned table when stdout is a terminal, JSONL when it is piped. That keeps interactive use readable and scripted use parseable without you setting --format either time. See output formats.

Exit codes

Code Meaning
0 success
1 an error nothing else classified
2 usage, including a config file that will not load
3 network, meaning the site never answered
4 the site answered and the answer was an error or a block
5 extraction failed, or a record did not reconcile
6 not found
7 refused because robots.txt disallows the path
8 robots.txt could not be read, so nothing can be checked against it