# Academic Paper Search: OpenAlex, PubMed, arXiv (`bravona/academic-papers-search`) Actor

- **URL**: https://apify.com/bravona/academic-papers-search.md
- **Developed by:** [Bravona](https://apify.com/bravona) (community)
- **Categories:** Education, AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 paper returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Academic Paper Search: OpenAlex, PubMed, arXiv

**Academic Paper Search** searches **OpenAlex, PubMed and arXiv in one run** and returns a single **deduplicated list of papers** with DOI, abstract, citation count and open-access PDF links. Enter a search query, pick a year range, and download the results as JSON, CSV or Excel.

Unofficial tool. Not affiliated with, endorsed by, or sponsored by OpenAlex, the U.S. National Library of Medicine / NCBI (PubMed), or arXiv.

### What does Academic Paper Search do?

- Queries up to three scholarly databases (OpenAlex, PubMed, arXiv) with one input.
- Merges the results and removes duplicates (matched by DOI, then PMID, then arXiv ID, then a careful title match), so the same paper found in two sources appears once.
- Adds a `foundIn` field that lists every source where the paper was found.
- Uses each source's official public API only. No login and no proxy.
- Runs on Apify: call it via API, schedule runs, export to JSON, CSV or Excel, and connect integrations.

### Who is it for?

**Researchers and grad students**: start a literature review from one merged list instead of three separate searches. **Biotech and pharma analysts**: track new papers on a topic across biomedical and preprint sources. **AI teams**: collect paper metadata and abstracts to build RAG corpora, ready for a spreadsheet or vector store.

### What data can you get from OpenAlex, PubMed and arXiv?

One dataset item per paper. Missing values are `null`; empty lists are `[]`.

| Field | Type | Description |
|---|---|---|
| `title` | string (rarely null) | Paper title |
| `doi` | string or null | The paper's DOI, lowercase, without the `https://doi.org/` prefix. arXiv-only papers get their arXiv DOI (`10.48550/arxiv.<id>`); a publisher DOI wins when both exist |
| `pmid` | string or null | The paper's PubMed ID |
| `arxivId` | string or null | The paper's arXiv ID |
| `openalexId` | string or null | The paper's OpenAlex work ID |
| `year` | integer or null | Publication year |
| `publishedDate` | string or null | Publication date as `YYYY-MM-DD`, `YYYY-MM` or `YYYY` |
| `authors` | list of strings, can be `[]` | Author names |
| `venue` | string or null | Journal or venue; `arXiv` for preprints |
| `abstract` | string or null | Paper abstract |
| `citedByCount` | integer or null | Number of citations. Comes from OpenAlex only; null otherwise |
| `isOpenAccess` | boolean or null | Whether the paper is open access. Null for PubMed papers with no PubMed Central copy (unknown) |
| `oaPdfUrl` | string or null | Link to an open-access PDF |
| `concepts` | list of strings, can be `[]` | Topics for the paper. For arXiv these are category codes such as `q-bio.GN` |
| `foundIn` | list of strings, never empty | Which sources returned this paper |
| `sourceUrl` | string, rarely null | Link to the record on its source |
| `scrapedAt` | string (ISO timestamp) | When the record was collected |

**Why can `pmid` be filled when `foundIn` is only `openalex`?** `foundIn` lists the sources whose search returned the paper. OpenAlex records often include the paper's PubMed ID, so `pmid` can be filled even when PubMed's own search did not return that paper.

### Sources and licenses

| Source | Access method | Published rate limit | What the Actor does | Data license / terms |
|---|---|---|---|---|
| OpenAlex | Official OpenAlex API (`api.openalex.org/works`) | 10 requests/s without a key ([OpenAlex](https://developers.openalex.org/how-to-use-the-api/rate-limits-and-authentication)) | About 8 requests/s, with or without a key | Data is CC0 ([OpenAlex about: Open data](https://explore.openalex.org/about), [OpenAlex license](https://github.com/ourresearch/openalex-docs/blob/main/license.md)) |
| PubMed | Official NCBI E-utilities API (esearch + efetch, [docs](https://www.ncbi.nlm.nih.gov/books/NBK25501/)); PubMed web pages are not used | 3 requests/s without a key, 10 with one ([NCBI](https://www.ncbi.nlm.nih.gov/books/NBK25497/)) | About 2.9 requests/s without a key, 9 with one | [NCBI policies](https://www.ncbi.nlm.nih.gov/home/about/policies/): some material may be protected by copyright, and users are expected to follow the copyright holder's terms. PubMed abstracts may be covered by publisher copyright |
| arXiv | Official arXiv API (`export.arxiv.org/api/query`) | 1 request every 3 seconds ([arXiv API Terms of Use](https://info.arxiv.org/help/api/tou.html)) | 1 request every 3.1 seconds | Metadata usable under CC0 1.0 ([arXiv API Terms of Use](https://info.arxiv.org/help/api/tou.html)) |

Without a key, OpenAlex gives each IP address a small daily budget. If it runs out, the run continues with PubMed and arXiv only. **We recommend a free OpenAlex key:** it raises your daily OpenAlex budget 10× (a free key gets $1 a day) and lets the Actor use cursor paging. Without a key it uses page-by-page paging, which also reaches the 10,000-per-source maximum, so no results are lost. It does not make the Actor faster; the Actor runs at about 8 requests/s either way.

You are responsible for how you use the data.

### How to use Academic Paper Search

1. Open the Actor and click **Start**.
2. Enter your **search query**, e.g. a topic like "CRISPR".
3. Choose **sources** (OpenAlex, PubMed, arXiv) and a **year range**.
4. Set **Max results** (per source) to cap the number of papers and the cost.
5. Start the run, then download the dataset as JSON, CSV or Excel, or fetch it via the Apify API.

### Input

| Field | Required | Default | Description |
|---|---|---|---|
| `query` | Yes | none (prefill: "CRISPR") | Search terms, e.g. a topic or keyword. Sent to each source's own search (OpenAlex search, PubMed search terms, arXiv all-fields search), so matching can vary by source |
| `sources` | No | all three (openalex, pubmed, arxiv) | Which databases to search. Select at least one |
| `fromYear` | No | empty (no lower bound) | Earliest publication year. Leave empty to skip this bound |
| `toYear` | No | empty (no upper bound) | Latest publication year. Leave empty to skip this bound. If both years are empty, there is no year filter |
| `maxResults` | No | 100 | Maximum papers **per source** (1 to 10,000); caps cost |
| `dedupe` | No | true | Merge the same paper found in several sources |
| `openAlexApiKey` | No | none | Free OpenAlex API key (secret). Recommended |
| `ncbiApiKey` | No | none | NCBI API key (secret). Raises PubMed from 3 to 10 requests/s |
| `contactEmail` | No | none | Optional email passed to NCBI and OpenAlex as a polite contact. |

Example input:

```json
{"query":"CRISPR","sources":["openalex","pubmed","arxiv"],"maxResults":10}
```

On Apify this returned 30 items (10 per source) in 2.7 seconds.

### Output example

Three real records from the example run above. Abstracts are trimmed.

```json
{"title":"The Heroes of CRISPR","doi":"10.1016/j.cell.2015.12.041","pmid":"26771483","arxivId":null,"openalexId":"W2239190724","year":2016,"publishedDate":"2016-01-01","authors":["Eric S. Lander"],"venue":"Cell","abstract":null,"citedByCount":493,"isOpenAccess":true,"oaPdfUrl":"http://www.cell.com/article/S0092867415017055/pdf","concepts":["Biology","CRISPR","Computational biology","Genetics","Evolutionary biology"],"foundIn":["openalex"],"sourceUrl":"https://openalex.org/W2239190724","scrapedAt":"2026-10-30T19:31:48.805Z"}
```

```json
{"title":"Genome modification by CRISPR/Cas9.","doi":"10.1111/febs.13110","pmid":"25315507","arxivId":null,"openalexId":null,"year":2014,"publishedDate":"2014-11-07","authors":["Yuanwu Ma","Lianfeng Zhang","Xingxu Huang"],"venue":"The FEBS journal","abstract":"Clustered regularly interspaced short palindromic repeats (CRISPR)-CRISPR-associated protein (Cas)9-mediated genome modification enables us to edit the genomes…","citedByCount":null,"isOpenAccess":null,"oaPdfUrl":null,"concepts":["Animals","CRISPR-Associated Proteins","Clustered Regularly Interspaced Short Palindromic Repeats","Genetic Engineering","Humans","RNA Editing"],"foundIn":["pubmed"],"sourceUrl":"https://pubmed.ncbi.nlm.nih.gov/25315507/","scrapedAt":"2026-10-30T19:31:48.805Z"}
```

```json
{"title":"Investigating the genomic background of CRISPR-Cas genomes for CRISPR-based antimicrobials","doi":"10.48550/arxiv.2202.07171","pmid":null,"arxivId":"2202.07171","openalexId":null,"year":2022,"publishedDate":"2022-02-15","authors":["Hyunjin Shim"],"venue":"arXiv","abstract":"CRISPR-Cas systems are an adaptive immunity that protects prokaryotes against foreign genetic elements…","citedByCount":null,"isOpenAccess":true,"oaPdfUrl":"https://arxiv.org/pdf/2202.07171v1","concepts":["q-bio.GN"],"foundIn":["arxiv"],"sourceUrl":"https://arxiv.org/abs/2202.07171","scrapedAt":"2026-10-30T19:31:48.805Z"}
```

The first record has a `pmid` although `foundIn` is only `openalex`: OpenAlex supplied the PubMed ID.

### How much does it cost to search academic papers?

You pay only for papers saved to your dataset.

| Event | When it is charged | Price |
|---|---|---|
| Actor start | Once per run | $0.00005 |
| Paper returned | Each paper saved to the dataset | $0.0015 ($1.50 per 1,000) |

Example: one run that returns 1,000 papers costs $0.00005 + (1,000 × $0.0015) = **$1.50005, about $1.50**. A run with 100 papers costs about $0.15.

Because **Max results applies per source**, the number of papers depends on how many sources you select. Deduplication can lower the count, so these are upper bounds:

- Prefilled run (all 3 sources, Max results 10): up to 30 papers, about **$0.045** plus the $0.00005 start fee.
- Default Max results (all 3 sources, 100 each): up to 300 papers, about **$0.45** plus the $0.00005 start fee.

The Actor respects the maximum cost you set for the run (Apify's standard spending limit): when the limit is reached it stops saving papers, logs a warning and finishes normally.

Platform usage (compute, proxy, storage) is included in these prices. You pay only the event prices listed above, nothing extra on top.

Works on Apify's free plan; you pay a tiny start fee plus only for results returned (each paper saved to your dataset). Charges come out of your Apify account's prepaid usage; on the Free plan, once that monthly usage is used up, runs stop until the next billing cycle.

### Limits

- Runs stay under each source's published rate limit (see Sources and licenses). arXiv is the slowest source, at one request every 3.1 seconds.
- Without your own OpenAlex key, OpenAlex's small per-IP daily budget applies; if it runs out, the run continues with PubMed and arXiv only.
- Metadata and abstracts only. The Actor links to open-access PDFs but does not download full texts.
- Some fields are empty for some sources: `citedByCount` comes only from OpenAlex, and `isOpenAccess` is null for PubMed papers with no PubMed Central copy.
- PubMed abstracts may be covered by publisher copyright. Check reuse rights before republishing them.
- Maximum results per run: 10,000 per source, so up to 30,000 papers before deduplication.

### FAQ

**Is it legal to use this data?** The Actor reads public metadata through each source's official API. OpenAlex data is CC0 and arXiv metadata is usable under CC0 1.0; PubMed abstracts may be covered by publisher copyright, so check reuse rights before republishing them (see [NCBI policies](https://www.ncbi.nlm.nih.gov/home/about/policies/)). You are responsible for how you use the data. This is not legal advice.

**Do I need an API key?** No, all three sources work without keys. We recommend a free OpenAlex key: it raises your daily OpenAlex budget 10× (a free key gets $1 a day) and lets the Actor use cursor paging; without one, page-by-page paging still reaches the full 10,000 results per source. Without a key, the small per-IP daily budget applies; if it runs out, the run continues without OpenAlex. A key does not change speed: the Actor runs at about 8 requests/s either way. An NCBI key raises PubMed from 3 to 10 requests/s.

**How does deduplication work?** With `dedupe` on, records are merged when they share a DOI, then a PMID, then an arXiv ID. As a last step, a careful title match merges records whose normalized titles match (at least 25 characters), whose years are at most one apart, and whose DOIs don't conflict. arXiv-only papers keep their arXiv DOI (10.48550/…); when a publisher DOI also exists, the publisher DOI wins. `foundIn` lists every source that returned the paper. Merges are transitive (if A matches B and B matches C, all three become one record). Each field comes from the first source that has it, in the order OpenAlex, PubMed, arXiv; the first source's abstract is kept unless another source's is more than 20% longer, the higher citation count is kept, and `concepts` are combined. OpenAlex concepts are included only when they score 0.3 or higher.

**Where can I see how each source did?** Each run writes an `OUTPUT` record to its key-value store with each source's status (`ok`, `failed` or `skipped`) and its item count. The run's status message shows the same summary.

**What search syntax can I use?** Your query goes to each source's own search, so syntax differs:

- **OpenAlex** searches title, abstract, full text and keywords, and supports `AND`, `OR`, `NOT`, parentheses and "quoted phrases". A leading minus (`-word`) does not exclude.
- **PubMed** accepts full PubMed syntax, including field tags such as `[tiab]` and `[au]`.
- **arXiv** passes field prefixes (`ti:`, `au:`, `abs:`, `cat:`) through as written. Otherwise each word becomes `all:word`, joined with `AND`. arXiv has no `NOT`.

**Why did I get fewer papers than Max results × sources?** Duplicates were merged, or the sources returned fewer matches for your query and year range.

**Can I use it via API or integrations?** Yes, like any Apify Actor, through the API tab, schedules and integrations.

**Do I pay for Apify compute on top?** No. Platform usage is included. You pay only the listed event prices: $0.00005 per run start and $0.0015 per paper.

**Something broke or a field is missing?** Open an issue in the **Issues** tab.

### Changelog

- **0.1** (Unreleased): Initial release. Searches OpenAlex, PubMed and arXiv in one run with a per-source result limit and an optional year range; merges duplicates by DOI, PMID, arXiv ID and a careful title match, with `foundIn` showing every source; optional OpenAlex and NCBI API keys and a contact email; stays under each source's published rate limit; pay per event at $0.0015 per paper.

### Disclaimer

Academic Paper Search is an unofficial tool built by Bravona. It is not affiliated with, endorsed by, or sponsored by OpenAlex, the U.S. National Library of Medicine / NCBI (PubMed), or arXiv. All product names and trademarks belong to their owners and are used only to describe compatibility. The Actor reads publicly available data through official public APIs without logging in, and follows each source's published rate limits. Data is provided as-is; accuracy and availability depend on the source. You are responsible for using the data in line with the source's terms and applicable law (including privacy law).

# Changelog

This Actor's version history is a separate document: https://apify.com/bravona/academic-papers-search/changelog.md

# Actor input Schema

## `query` (type: `string`):

Search terms, e.g. a topic like CRISPR. Sent to each source's own search (OpenAlex search, PubMed search terms, arXiv all-fields search), so matching can vary by source. Required; no default.

## `sources` (type: `array`):

Databases to search: OpenAlex, PubMed, arXiv. Optional; defaults to all three. Select at least one. Searching fewer sources is faster; arXiv is the slowest because it allows one request every three seconds.

## `fromYear` (type: `integer`):

Earliest publication year to include, e.g. 2023. Leave empty to apply no lower bound. If both years are empty, there is no year filter.

## `toYear` (type: `integer`):

Latest publication year to include, e.g. 2026. Leave empty to apply no upper bound. If both years are empty, there is no year filter.

## `maxResults` (type: `integer`):

Maximum papers to save from each selected source (1 to 10,000; default 100). With 3 sources you can get up to 3 × this number, before duplicates are merged. Each saved paper is billed at $0.0015 ($1.50 per 1,000), so this also caps cost.

## `dedupe` (type: `boolean`):

Merge the same paper found in several sources (matched by DOI, PMID, arXiv ID, then a careful title match) into one record. Default: on. The foundIn field lists every source that returned the paper.

## `openAlexApiKey` (type: `string`):

Optional, recommended. A free OpenAlex key raises your daily OpenAlex budget 10× (a free key gets $1 a day) and allows cursor paging past the first page; it does not change speed (about 8 requests/s either way). Without a key, if the small daily budget runs out, the run continues with PubMed and arXiv only. Stored encrypted.

## `ncbiApiKey` (type: `string`):

Optional. Your NCBI key raises PubMed from 3 to 10 requests/s. Stored encrypted.

## `contactEmail` (type: `string`):

Optional. An email passed to NCBI and OpenAlex as a polite contact. Leave empty to send none.

## Actor input object example

```json
{
  "query": "CRISPR",
  "sources": [
    "openalex",
    "pubmed",
    "arxiv"
  ],
  "maxResults": 10,
  "dedupe": true
}
```

# Actor output Schema

## `papers` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "CRISPR",
    "sources": [
        "openalex",
        "pubmed",
        "arxiv"
    ],
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("bravona/academic-papers-search").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "query": "CRISPR",
    "sources": [
        "openalex",
        "pubmed",
        "arxiv",
    ],
    "maxResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("bravona/academic-papers-search").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "CRISPR",
  "sources": [
    "openalex",
    "pubmed",
    "arxiv"
  ],
  "maxResults": 10
}' |
apify call bravona/academic-papers-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bravona/academic-papers-search"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/2wy8rNK4RRSSNnfPb/builds/Y82fgtHqbdvIrDhDR/openapi.json
