Output
Formats, column selection and templates, all the same on every command.
Every command renders through one formatter, so the same flags work everywhere.
The default is auto: a table when stdout is a terminal, NDJSON when it is a pipe.
arxiv surfaces # a table, because this is a terminal
arxiv surfaces | jq . # NDJSON, because this is a pipe
You reach for -o only when you want something other than that.
Formats
| Format | What it is | Good for |
|---|---|---|
table |
aligned columns with a box | reading on a terminal |
markdown |
a pipe table | pasting into an issue or a README |
list |
one block per record, key and value | a record with long fields |
jsonl |
one JSON object per line | piping, streaming, jq |
json |
a single JSON array | loading a whole result at once |
csv |
comma separated with a header | spreadsheets |
tsv |
tab separated | cut and awk |
url |
the URL column alone | feeding other commands |
raw |
the bytes as served | response bodies and file contents |
jsonl and json carry every field.
The table formats carry the columns the command chose to print, which is fewer, because a terminal is 100 columns wide and a paper record is not.
So if a field is missing from a table it is worth looking at the same command in -o json before concluding it was not read.
Choosing columns
--fields takes the column names as the table prints them.
arxiv surfaces --fields id,surface
arxiv surfaces -o markdown # to see what the column names are
The names are the header row, not the JSON keys, and the two are not always the same.
arxiv surfaces prints surface where the JSON says name.
A name that does not match a column gives an empty column rather than an error, so check the spelling against the header if a column comes back blank.
--no-header drops the header row, which is what a downstream tool that expects bare rows wants.
Templates
--template takes a Go text template applied per record, and the keys are the JSON keys, lower case.
arxiv planes --template '{{.name}} paces at {{.pace}}'
arxiv search "attention" --cat cs.CL -n 5 --template '{{.id}} {{.title}}'
Lower case matters.
{{.Name}} renders as <no value> rather than failing, because that is what text/template does with a key it cannot find.
Piping
NDJSON in a pipe is the whole point of the default.
arxiv search "transformer" --cat cs.LG -n 50 | jq -r '.id + " " + .title'
arxiv list cs.CL 2026-01 --all | jq -r 'select(.categories | index("cs.LG")) | .id'
arxiv search --cat cs.CL --from 2026-01 --all > cs-cl.jsonl
Records stream as they are read rather than buffering to the end, so a long walk prints its first result in seconds and you can kill it once you have what you wanted.
That is also why a walk interrupted with ctrl-c leaves usable output.
Text that is text
arxiv bibtex and arxiv cite write text and not records, because a .bib file is text and a citation is text.
They emit no records at all, so -o, --fields and --template have nothing to act on.
When you want a citation as data, ask for it as data: arxiv cite -s csl-json is one JSON array however many papers you gave it.