# Google Scholar Alternative: Academic Papers (OpenAlex) (`groundtruth/academic-papers`) Actor

Search 250 million academic papers by keyword, DOI, citing paper, institution or journal. Citation counts, abstracts, open access PDF links and DOIs from OpenAlex, no scraping. Every record links to the source.

- **URL**: https://apify.com/groundtruth/academic-papers.md
- **Developed by:** [Ground Truth](https://apify.com/groundtruth) (community)
- **Categories:** AI, Education, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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

## Google Scholar Alternative: Academic Papers (OpenAlex)

Search more than 250 million academic papers and get clean JSON back: title, year, journal, citation count, abstract, open access PDF link, topics, institutions and the author byline. It reads the OpenAlex API (an open catalogue of scholarly work, data released under CC0), so there is no scraping, no CAPTCHA and no blocked IP. **Every record carries a `verify_url` that opens the paper on its DOI page**, so any value can be checked at the source in one click.

Five ways to use it:

- **Search** by keywords, with filters for year range, open access, paper type, minimum citations and sort order.
- **Look up by DOI**: paste a list of up to 2,000 DOIs and get one record each.
- **Cited by**: give one paper and get the papers that cite it.
- **Institution**: papers from a university or lab, by name, ROR id or OpenAlex id.
- **Journal**: papers from one journal, by name, ISSN or OpenAlex id.

Why an AI agent would call it: it answers "what has been published on this, who cites it, and where is the free PDF" as structured data, with a link a person can open to confirm each paper exists.

### What it does not do

- It is not Google Scholar and does not read Google Scholar. Coverage and citation counts come from OpenAlex and will differ from Google's numbers.
- It returns the printed author byline only. No author profiles, no emails, no ORCID records.
- It does not download PDFs. It gives you the open access link when one exists.
- Some papers have no abstract in OpenAlex (publishers do not always release them). Those records have `abstract: null`.
- OpenAlex list results stop at 100 authors per paper. A byline of exactly 100 sets `authors_truncated: true`. Open `verify_url` for the full list.
- Keyword search matches stems and related terms. Sorting by citations returns the most cited papers among all matches, which can include loose matches. Use relevance sort or the quoted-phrase form for tight results.

### Sample output

Input (DOI lookup):

```json
{ "mode": "doi_lookup", "dois": ["10.1371/journal.pmed.0020124"] }
```

Output record (abstract shortened here):

```json
{
  "openalex_id": "W2144981148",
  "openalex_url": "https://openalex.org/W2144981148",
  "doi": "10.1371/journal.pmed.0020124",
  "title": "Why Most Published Research Findings Are False",
  "publication_year": 2005,
  "publication_date": "2005-08-02",
  "type": "article",
  "language": "en",
  "source_name": "PLoS Medicine",
  "source_type": "journal",
  "publisher": "Public Library of Science",
  "issn_l": "1549-1277",
  "cited_by_count": 10451,
  "is_open_access": true,
  "oa_status": "gold",
  "oa_url": "https://doi.org/10.1371/journal.pmed.0020124",
  "oa_url_is_pdf": false,
  "abstract": "There is increasing concern that most current published research findings are false. ...",
  "topics": ["Meta-analysis and systematic reviews", "Academic Publishing and Open Access"],
  "primary_topic": "Meta-analysis and systematic reviews",
  "field": "Decision Sciences",
  "author_names": ["John P. A. Ioannidis"],
  "author_count": 1,
  "authors_truncated": false,
  "institutions": ["University of Ioannina"],
  "is_retracted": false,
  "referenced_works_count": 40,
  "matched_entity": null,
  "lookup_status": "ok",
  "note": null,
  "verify_url": "https://doi.org/10.1371/journal.pmed.0020124"
}
```

**Check it yourself:** open the `verify_url`. The title, journal and year on the publisher's page should match. The tests in this Actor were checked the same way, against Crossref (the DOI registration agency) and Europe PMC, not against the Actor's own output. Citation counts change daily and are not part of those checks.

Handled error: a DOI OpenAlex does not know comes back as a record with `lookup_status: "not_found"`, a `note`, and a `verify_url` to try by hand. It is not charged. An input that is not a DOI is skipped with a warning in the log, and if every entry is invalid the run fails with a plain message.

### Configuration

| Input | Type | Default | What it does |
|---|---|---|---|
| `mode` | choice | `doi_lookup` | `doi_lookup`, `search`, `cited_by`, `institution` or `source` |
| `dois` | list | three sample DOIs | DOI lookup. DOIs, resolver URLs or OpenAlex ids (W...). Up to 2,000 |
| `query` | text | none | Keywords. Required for search. Optional narrowing for institution and source. Up to 500 characters. Use double quotes for an exact phrase |
| `search_scope` | choice | `title_and_abstract` | Or `full_text`, which matches the body of papers and returns far more, looser results |
| `cited_work` | text | none | Cited by mode: a DOI or OpenAlex id |
| `institution` | text | none | Institution mode: name, ROR id (https://ror.org/...) or OpenAlex id (I...) |
| `source` | text | none | Journal mode: name, ISSN or OpenAlex id (S...) |
| `from_year`, `to_year` | integer | none | Publication year range, inclusive |
| `open_access_only` | boolean | false | Only papers with a free full text |
| `work_types` | list | all | article, preprint, review, book, book-chapter, dissertation, dataset, report, letter, editorial, standard, other |
| `min_citations` | integer | 0 | Only papers cited at least this often |
| `exclude_retracted` | boolean | false | Skip retracted papers |
| `sort` | choice | relevance with keywords, otherwise citations | `relevance`, `citations`, `newest`, `oldest` |
| `max_results` | integer | 25 | Up to 10,000. Up to 50,000 with your own OpenAlex key. Not used for DOI lookup |
| `include_abstract` | boolean | true | Turn off for smaller output |
| `openalex_api_key` | secret text | none | Optional. See the note below |

Filters apply to search, cited by, institution and journal modes. Tables of contents and cover pages are always left out of those modes. DOI lookup ignores filters.

For institution and journal names, the best OpenAlex match is used and every record says which one in `matched_entity`. Use the id to be certain. Institution mode includes papers by affiliated departments and labs.

#### Output fields

`openalex_id`, `openalex_url`, `doi`, `title`, `publication_year`, `publication_date`, `type`, `language`, `source_name`, `source_type`, `publisher`, `issn_l`, `cited_by_count`, `is_open_access`, `oa_status`, `oa_url`, `oa_url_is_pdf`, `abstract`, `topics`, `primary_topic`, `field`, `author_names`, `author_count`, `authors_truncated`, `institutions`, `is_retracted`, `referenced_works_count`, `matched_entity`, `lookup_status`, `note`, `verify_url`.

`oa_url` is the open access PDF when one is known (`oa_url_is_pdf: true`), otherwise the best open access page, otherwise null. If OpenAlex files a paper under a different DOI than the one you gave (it merges duplicates), `doi` shows its DOI, `verify_url` uses yours, and `note` explains.

#### The free allowance, and your own key

OpenAlex gives anyone without a key a small free daily allowance that is shared by everyone on the same network address. DOI lookups cost almost nothing against it. Keyword searches cost more, roughly 100 pages of up to 100 papers a day for the whole shared address. If it runs out, the Actor stops cleanly, keeps what it already delivered and says so. To avoid sharing, create a free key at openalex.org/settings/api and paste it into `openalex_api_key`: that gives you a separate $1 a day (about 1,000 search pages) and unlocks runs above 10,000 papers. The key is stored as a secret and never written to the log.

### Pricing

Pay per event: **$0.001 per paper returned** ($1 per 1,000), plus Apify's tiny start fee. Records with `lookup_status: "not_found"` are not charged. Failed runs deliver and charge nothing for papers they did not return.

Data: OpenAlex (openalex.org), released under CC0.

# Actor input Schema

## `mode` (type: `string`):

search: keyword search with filters. doi_lookup: fetch papers by DOI. cited_by: papers that cite one work. institution: papers from a university or lab. source: papers from one journal.

## `dois` (type: `array`):

One DOI per line, for example 10.1038/nature14539, or the same with https://doi.org/ in front. OpenAlex ids such as W2919115771 also work. Up to 2000. A DOI OpenAlex does not know is returned with lookup_status not_found and is not charged.

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

Words to look for, for example graph neural networks. Put a phrase in double quotes to match it exactly. Required in search mode. In institution and journal mode it narrows the papers to that topic.

## `search_scope` (type: `string`):

Title and abstract gives precise results. Full text also matches the body of papers, so it returns far more and looser matches.

## `cited_work` (type: `string`):

A DOI or OpenAlex id. You get the papers that cite it.

## `institution` (type: `string`):

A name such as Massachusetts Institute of Technology, a ROR id such as https://ror.org/042nb2s44, or an OpenAlex id such as I63966007. A name uses the best match, and every record says which institution was matched. Papers by affiliated departments and labs are included.

## `source` (type: `string`):

A name such as Nature, an ISSN such as 1476-4687, or an OpenAlex id such as S137773608. A name uses the best match, and every record says which venue was matched.

## `from_year` (type: `integer`):

Only papers published in this year or later.

## `to_year` (type: `integer`):

Only papers published in this year or earlier.

## `open_access_only` (type: `boolean`):

Only papers with a free full text somewhere.

## `work_types` (type: `array`):

Leave empty for every type. Choose from article, preprint, review, book, book-chapter, dissertation, dataset, report, letter, editorial, standard, other.

## `min_citations` (type: `integer`):

Only papers cited at least this many times.

## `exclude_retracted` (type: `boolean`):

Skip papers marked as retracted.

## `sort` (type: `string`):

Relevance only works with keywords. Without keywords the default is most cited. Citation sort returns the most cited papers among all matches, which can include loose keyword matches.

## `max_results` (type: `integer`):

How many papers to return. Up to 10000 works without a key. Up to 50000 needs your own free OpenAlex API key below. You are charged per paper returned. Not used for DOI lookup.

## `include_abstract` (type: `boolean`):

Abstracts are rebuilt from OpenAlex's index. Turn off for smaller output.

## `openalex_api_key` (type: `string`):

OpenAlex gives anyone without a key a small free allowance that is shared by everyone on the same network address. For heavy use, paste your own free key (openalex.org/settings/api) to get a separate allowance of $1 a day. Leave empty to use the shared one.

## Actor input object example

```json
{
  "mode": "doi_lookup",
  "dois": [
    "10.1038/nature14539",
    "10.1126/science.1127647",
    "10.1371/journal.pmed.0020124"
  ],
  "query": "retrieval augmented generation",
  "search_scope": "title_and_abstract",
  "cited_work": "10.1038/nature14539",
  "institution": "https://ror.org/042nb2s44",
  "source": "1476-4687",
  "open_access_only": false,
  "work_types": [
    "article",
    "review"
  ],
  "min_citations": 0,
  "exclude_retracted": false,
  "max_results": 25,
  "include_abstract": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `resultsCsv` (type: `string`):

No description

## `runDetails` (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 = {
    "mode": "doi_lookup",
    "dois": [
        "10.1038/nature14539",
        "10.1126/science.1127647",
        "10.1371/journal.pmed.0020124"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("groundtruth/academic-papers").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 = {
    "mode": "doi_lookup",
    "dois": [
        "10.1038/nature14539",
        "10.1126/science.1127647",
        "10.1371/journal.pmed.0020124",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("groundtruth/academic-papers").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 '{
  "mode": "doi_lookup",
  "dois": [
    "10.1038/nature14539",
    "10.1126/science.1127647",
    "10.1371/journal.pmed.0020124"
  ]
}' |
apify call groundtruth/academic-papers --silent --output-dataset

```

## MCP server setup

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

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/r0nelx9VhAhGULT6s/builds/Eqsza5zfLaaPu1FPD/openapi.json
