# OpenAlex Scholar Scraper (Papers, Authors, Journals) (`scrapyx/openalex-scholar-scraper`) Actor

Search OpenAlex, the open CC0 catalogue of 250M+ scholarly works plus authors, journals, institutions, publishers, funders and topics. Full-text query, filters (year, type, open access, citations, author, journal), cursor pagination past 10k, reconstructed abstracts. DOI/ORCID/ROR/ISSN lookup.

- **URL**: https://apify.com/scrapyx/openalex-scholar-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** AI, Education, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## OpenAlex Scholar Scraper (Papers, Authors, Journals)

Search **[OpenAlex](https://openalex.org)** — the open, CC0 catalogue of
250M+ scholarly works, plus the authors, journals & repositories,
institutions, publishers, funders and research topics behind them. Built and
maintained by the non-profit OurResearch; no API key.

- **Full-text query** across title + abstract + full text
- **Filters** — publication year range, work type, open-access only, minimum
  citations, by author / journal / institution, plus a raw OpenAlex filter
  passthrough for anything else
- **Cursor pagination** — pull far more than 10,000 results (the page-offset
  ceiling does not apply to cursors)
- **Reconstructed abstracts** — OpenAlex stores abstracts as an inverted
  index; this Actor turns them back into plain text
- **ID lookup** — resolve a DOI, ORCID, ROR, ISSN or OpenAlex id to its
  record; the entity is inferred from the reference

| Record type | One per | Carries |
| --- | --- | --- |
| `SEARCH_SUMMARY` | query | entity, upstream count, results returned, cursor pages, filter string, requests made |
| `RESULT` | work / author / journal / … | normalised fields for that entity + the verbatim OpenAlex record in `raw` |
| `ERROR` | bad input / missing record | `_error` code + `_errorDetail` |

### Entities

`works` (papers, books, datasets, preprints…), `authors`, `sources`
(journals & repositories), `institutions`, `publishers`, `funders`,
`topics`. All seven share the same search/filter/cursor mechanics; the
normalised fields on the `RESULT` row differ by entity.

### Things OpenAlex will (not quite) mislead you about

Each is measured, and each has a scenario in
`tests/smoke/openalex-scholar-scraper_traps.sh` (9/9 passing).

**`search` is an honest filter, not a ranked corpus.** A nonsense query
returns `count: 0` — unlike Crossref and the Art Institute of Chicago, which
return the whole corpus, ranked. Real queries are relevance-ranked by default
(supply `sort` to override — e.g. `cited_by_count:desc`, but note that
overrides relevance entirely).

**An unknown `workType` returns a silent `count: 0`, not an error.**
`filter=type:notarealtype` is HTTP 200 with zero results — a "no such works"
that is really "no such type". The Actor validates `workType` against
OpenAlex's controlled list up front and refuses an unknown one with the valid
list. (A malformed filter *key* or *operator* IS a 400 → `bad_request`.)

**`page`-based paging stops at offset 10,000** (`page=401` at `per-page=25`
→ HTTP 400). This Actor uses `cursor=` pagination throughout, which has no
such limit — `deepPaginationViaCursor: true` on the summary when it kicks in.

**Paratext is excluded by default.** Series/issue front-matter records
aggregate a whole series' citations and swamp a cited-by-count sort with
non-articles. `is_paratext:false` is applied unless you set `includeParatext`.

**Every id and DOI comes back as a URL** (`https://openalex.org/W…`,
`https://doi.org/…`). The normalised fields carry the bare form; `raw` keeps
the URLs.

**Some works genuinely have no abstract** (`abstract: null`) — OpenAlex
doesn't have one, not a parse failure.

### The polite pool

OpenAlex offers a faster request pool to API users who identify themselves.
Set `mailto` to your contact email to use it (added as `mailto=` on every
request). Off by default — the common pool is fine at this Actor's pacing,
which backs off on the 429 OpenAlex returns under sustained load.

### Notes on cost

`search` mode: one request per 200 results (cursor pages). `ids` mode: one
request per reference. `slimOutput` drops the (large, for works) `raw`
record. No proxy needed.

# Actor input Schema

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

`search` runs a query/filters against one entity. `ids` looks up specific records by OpenAlex id / DOI / ORCID / ROR / ISSN (entity inferred).

## `entity` (type: `string`):

`search` mode. Which OpenAlex catalogue to search.

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

`search` mode. Full-text search. An unrecognised term honestly returns 0 results (OpenAlex search is a filter, not a ranked corpus match). Optional if you supply filters instead.

## `recordIds` (type: `array`):

`ids` mode. One per line: `W2741809807`, `A5086198262`, `S137773608`, a DOI (`10.7717/peerj.4375`), an ORCID, a ROR, or an ISSN. Bad refs become `invalid_ref` errors.

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

Works filter. Keep works published on/after 1 January of this year.

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

Works filter. Keep works published on/before 31 December of this year.

## `workType` (type: `string`):

Works filter. An OpenAlex type: `article`, `review`, `book-chapter`, `book`, `dataset`, `preprint`, `dissertation`, `report`, `standard`, `editorial`, `letter`, `erratum`, `paratext`, `peer-review`, `other`. An unknown value returns HTTP 400 (not a silent widen).

## `openAccessOnly` (type: `boolean`):

Works filter. Keep only works OpenAlex marks as open access (`is_oa:true`).

## `includeParatext` (type: `boolean`):

By default `is_paratext:false` is applied — paratext records (series/issue front matter) aggregate a whole series' citations and swamp a cited-by-count sort with non-articles. Turn this on to include them.

## `minCitations` (type: `integer`):

Works filter. Keep only works cited at least this many times.

## `authorId` (type: `string`):

Works filter. Restrict to works by one author — an OpenAlex author id (`A5086198262`) or ORCID.

## `sourceId` (type: `string`):

Works filter. Restrict to works in one source — an OpenAlex source id (`S137773608`) or ISSN.

## `institutionId` (type: `string`):

Works filter. Restrict to works with an author affiliated to one institution — an OpenAlex institution id (`I27837315`) or ROR.

## `countryCode` (type: `string`):

Non-works filter. ISO 3166-1 alpha-2 (`us`, `gb`, `de`). Restricts authors/institutions/sources/… by country.

## `rawFilter` (type: `string`):

Power users. Appended verbatim to the `filter=` parameter (comma-joined with the structured filters above). E.g. `is_retracted:true`, `language:en`, `authorships.institutions.country_code:us`. See docs.openalex.org/how-to-use-the-api/get-lists-of-entities/filter-entity-lists.

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

OpenAlex sort key, e.g. `cited_by_count:desc`, `publication_date:desc`, `works_count:desc`. Default is relevance when a query is given, else OpenAlex's default order.

## `includeAbstract` (type: `boolean`):

OpenAlex stores abstracts as an inverted index (`{word: [positions]}`). When on, they are reconstructed into plain text on the `abstract` field. Some works have no abstract at all.

## `slimOutput` (type: `boolean`):

By default every RESULT row carries the full verbatim OpenAlex record in `raw`. Turn on to emit only the normalised fields (works records in particular are large).

## `mailto` (type: `string`):

Optional. OpenAlex offers a faster 'polite pool' to API users who identify themselves. If given, it is added as the `mailto=` parameter on every request. Off by default (the common pool is used).

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

`search` mode cap. Cursor pagination is used, so this can exceed 10,000 (the page-offset ceiling does not apply).

## `maxConcurrency` (type: `integer`):

Parallel in-flight requests (matters for `ids` mode).

## `minRequestInterval` (type: `number`):

Politeness pacing for a non-profit's infrastructure. OpenAlex 429s under sustained hammering.

## `proxyConfiguration` (type: `object`):

Optional. No anti-bot layer, so a proxy is OFF by default.

## Actor input object example

```json
{
  "mode": "search",
  "entity": "works",
  "query": "CRISPR gene editing",
  "openAccessOnly": false,
  "includeParatext": false,
  "includeAbstract": true,
  "slimOutput": false,
  "maxResults": 500,
  "maxConcurrency": 4,
  "minRequestInterval": 0.12,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 gene editing"
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/openalex-scholar-scraper").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 gene editing" }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/openalex-scholar-scraper").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 gene editing"
}' |
apify call scrapyx/openalex-scholar-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/openalex-scholar-scraper"
        }
    }
}

```

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/EMkWgCzYwd1r2iZMd/builds/eOF039JYbo2lZuDp2/openapi.json
