<NME> README.md
<BEF> # arxiv.py [![Python 3.6](https://img.shields.io/badge/python-3.7-blue.svg)](https://www.python.org/downloads/release/python-370/) [![PyPI](https://img.shields.io/pypi/v/arxiv)](https://pypi.org/project/arxiv/) [![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/lukasschwab/arxiv.py/python-lint-test/master)](https://github.com/lukasschwab/arxiv.py/actions?query=branch%3Amaster)

Python wrapper for [the arXiv API](http://arxiv.org/help/api/index).

## Quick links

+ [Full package documentation](http://lukasschwab.me/arxiv.py/index.html)
+ [Example: fetching results](#example-fetching-results): the most common usage.
+ [Example: downloading papers](#example-downloading-papers)
+ [Example: fetching results with a custom client](#example-fetching-results-with-a-custom-client)

## About arXiv

[arXiv](http://arxiv.org/) is a project by the Cornell University Library that provides open access to 1,000,000+ articles in Physics, Mathematics, Computer Science, Quantitative Biology, Quantitative Finance, and Statistics.

## Usage

### Installation

```bash
$ pip install arxiv
```

In your Python script, include the line

```python
import arxiv
```

### Search

A `Search` specifies a search of arXiv's database.

```python
arxiv.Search(
  query: str = "",
  id_list: List[str] = [],
| `sort_order`   | string          | `"descending"` | No            |

+ `search_query` is a query string; details of its usage are documented [here](https://arxiv.org/help/api/user-manual#Quickstart).
+ `id_list` contains arXiv record IDs (typically of the format `"0710.5765v1"`)
+ `start` is the result offset for paging through a long query result. If set to 0, the API response will begin with the first result; if set to 10, the API response will begin with the 11th.
+ `max_results` is the maximum number of results per query.

All of these arguments are documented more comprehensively in the [arXiv API documentation](https://arxiv.org/help/api/user-manual#Quickstart).



```python
import arxiv
# Keyword search
arxiv.query(search_query="quantum")
# Get single record by ID
arxiv.query(id_list=["1707.08567"])
# Get multiple records by ID
  sort_by = arxiv.SortCriterion.SubmittedDate
)

for result in search.results():
  print(result.title)
```

Fetch and print the title of the paper with ID "1605.08386v1:"

```python
import arxiv

search = arxiv.Search(id_list=["1605.08386v1"])
paper = next(search.results())
print(paper.title)
```
| `slugify`    | boolean  | False       | No            |

+ `obj` is a result object, one of a list returned by query(). This function looks up keys `pdf_url` and `title` in `obj` to make the download request.
+ `dirname` is the relative directory path to which the downloaded PDF will be saved. It defaults to the present working directory.
+ When `prepend_id` is True, the arXiv record ID will be prepended to the download filename.
+ When `slugify` is True, the paper title will be stripped of non-alphanumeric characters before being used as a filename.

**Download PDF examples:**
+ `result.published`: When the result was originally published.
+ `result.title`: The title of the result.
+ `result.authors`: The result's authors, as `arxiv.Author`s.
+ `result.summary`: The result abstract.
+ `result.comment`: The authors' comment if present.
+ `result.journal_ref`: A journal reference if present.
+ `result.doi`: A URL for the resolved DOI to an external resource if present.
+ `result.primary_category`: The result's primary arXiv category. See [arXiv: Category Taxonomy](https://arxiv.org/category_taxonomy).
+ `result.categories`: All of the result's categories. See [arXiv: Category Taxonomy](https://arxiv.org/category_taxonomy).
+ `result.links`: Up to three URLs associated with this result, as `arxiv.Link`s.
+ `result.pdf_url`: A URL for the result's PDF if present. Note: this URL also appears among `result.links`.

They also expose helper methods for downloading papers: `(Result).download_pdf()` and `(Result).download_source()`.

#### Example: downloading papers

To download a PDF of the paper with ID "1605.08386v1," run a `Search` and then use `(Result).download_pdf()`:

```python
import arxiv

paper = next(arxiv.Search(id_list=["1605.08386v1"]).results())
# Download the PDF to the PWD with a default filename.
paper.download_pdf()
# Download the PDF to the PWD with a custom filename.
paper.download_pdf(filename="downloaded-paper.pdf")
# Download the PDF to a specified directory with a custom filename.
paper.download_pdf(dirpath="./mydir", filename="downloaded-paper.pdf")
```

The same interface is available for downloading .tar.gz files of the paper source:

```python
import arxiv

paper = next(arxiv.Search(id_list=["1605.08386v1"]).results())
# Download the archive to the PWD with a default filename.
paper.download_source()
# Download the archive to the PWD with a custom filename.
paper.download_source(filename="downloaded-paper.tar.gz")
# Download the archive to a specified directory with a custom filename.
paper.download_source(dirpath="./mydir", filename="downloaded-paper.tar.gz")
```

### Client

A `Client` specifies a strategy for fetching results from arXiv's API; it obscures pagination and retry logic.

For most use cases the default client should suffice. You can construct it explicitly with `arxiv.Client()`, or use it via the `(Search).results()` method.

```python
arxiv.Client(
  page_size: int = 100,
  delay_seconds: int = 3,
  num_retries: int = 3
)
```

+ `page_size`: the number of papers to fetch from arXiv per page of results. Smaller pages can be retrieved faster, but may require more round-trips. The API's limit is 2000 results.
+ `delay_seconds`: the number of seconds to wait between requests for pages. [arXiv's Terms of Use](https://arxiv.org/help/api/tou) ask that you "make no more than one request every three seconds."
+ `num_retries`: The number of times the client will retry a request that fails, either with a non-200 HTTP status code or with an unexpected number of results given the search parameters.

#### Example: fetching results with a custom client

`(Search).results()` uses the default client settings. If you want to use a client you've defined instead of the defaults, use `(Client).results(...)`:

```python
import arxiv

big_slow_client = arxiv.Client(
  page_size = 1000,
  delay_seconds = 10,
  num_retries = 5
)

# Prints 1000 titles before needing to make another request.
for result in big_slow_client.results(arxiv.Search(query="quantum")):
  print(result.title)
```

#### Example: logging

To inspect this package's network behavior and API logic, configure an `INFO`-level logger.

```pycon
>>> import logging, arxiv
>>> logging.basicConfig(level=logging.INFO)
>>> paper = next(arxiv.Search(id_list=["1605.08386v1"]).results())
INFO:arxiv.arxiv:Requesting 100 results at offset 0
INFO:arxiv.arxiv:Requesting page of results
INFO:arxiv.arxiv:Got first page; 1 of inf results available
```

<MSG> Document: multi-field queries must be space delimited

<DFF> @@ -38,9 +38,13 @@ arxiv.query(search_query, id_list, prune, start, max_results, sort_by, sort_orde
 | `sort_order`   | string          | `"descending"` | No            |
 
 + `search_query` is a query string; details of its usage are documented [here](https://arxiv.org/help/api/user-manual#Quickstart).
+  + **Note:** multi-field queries must be space-delimited. `au:balents_leon AND cat:cond-mat.str-el` is valid; `au:balents_leon+AND+cat:cond-mat.str-el` is *not* valid.
+
 + `id_list` contains arXiv record IDs (typically of the format `"0710.5765v1"`)
+
 + `start` is the result offset for paging through a long query result. If set to 0, the API response will begin with the first result; if set to 10, the API response will begin with the 11th.
-+ `max_results` is the maximum number of results per query.
+
++ `max_results` is the maximum number of results per response.
 
 All of these arguments are documented more comprehensively in the [arXiv API documentation](https://arxiv.org/help/api/user-manual#Quickstart).
 
@@ -48,8 +52,10 @@ All of these arguments are documented more comprehensively in the [arXiv API doc
 
 ```python
 import arxiv
-# Keyword search
+# Keyword queries
 arxiv.query(search_query="quantum")
+# Multi-field queries
+arxiv.query(search_query="au:balents_leon AND cat:cond-mat.str-el")
 # Get single record by ID
 arxiv.query(id_list=["1707.08567"])
 # Get multiple records by ID
@@ -72,8 +78,11 @@ arxiv.download(obj, dirname, prepend_id, slugify)
 | `slugify`    | boolean  | False       | No            |
 
 + `obj` is a result object, one of a list returned by query(). This function looks up keys `pdf_url` and `title` in `obj` to make the download request.
+
 + `dirname` is the relative directory path to which the downloaded PDF will be saved. It defaults to the present working directory.
+
 + When `prepend_id` is True, the arXiv record ID will be prepended to the download filename.
+
 + When `slugify` is True, the paper title will be stripped of non-alphanumeric characters before being used as a filename.
 
 **Download PDF examples:**

Document: multi-field queries must be space delimited