# Google Scholar Search Scraper - Citations & PDFs, No Login (`cliqtomedia/google-scholar-scraper`) Actor

Scrape Google Scholar search results: title, authors, venue, year, snippet, cited-by count, versions and PDF link. Filter by year, skip citations, sort by date. Up to 100 articles per query. No login.

- **URL**: https://apify.com/cliqtomedia/google-scholar-scraper.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (community)
- **Categories:** AI, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 88.9% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 articles

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?

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

Search Google Scholar with your own queries and get one Dataset row per article: title, link, authors, venue, year, snippet, cited-by count and a direct PDF or HTML link when Scholar shows one. Add a year range, skip \[CITATION] entries or sort by date. No Google login, account or API key is needed. The prefilled example returns 10 articles in under 15 seconds and costs $0.010 on the FREE tier.

![Google Scholar Search Scraper: papers, authors, citation counts and PDF links from your queries](https://api.apify.com/v2/key-value-stores/gtaQFAkBZrPMeU4cC/records/README-BANNER-20261017.png)

[What it does](#what-it-does) · [Quick start](#quick-start) · [Use cases](#use-cases) · [Input](#input) · [Output](#output) · [Pricing](#pricing) · [Limits](#limits) · [API and AI agents](#api-and-ai-agents) · [Troubleshooting](#troubleshooting) · [FAQ](#faq) · [Related Actors](#related-actors) · [Support](#support)

### What it does

The Actor opens the public Google Scholar results pages for each query, page by page (10 articles per page), and reads every result. It returns up to 100 articles per query in Scholar's order, with positions that continue across pages. Your year range, citation and sort settings are passed to Scholar as filters. Duplicate articles inside one query are removed by Scholar's own result id (`resultId`).

#### Good fit

- Literature review: the top articles on a topic, with authors, venue, year and citation counts in one spreadsheet.
- Finding open full texts: `pdfUrl` gives the direct \[PDF] or \[HTML] link from the right column of Scholar.
- Comparing topics or periods: several queries in one run, each with the same year range.
- Feeding papers to an AI agent or a RAG pipeline: titles, snippets and links as clean JSON.

#### Not a fit / limits

- More than 100 articles per query. Scholar cuts deeper pages for automated traffic; split a big topic into narrower queries or year ranges.
- Author profile pages, "Cited by" lists, BibTeX/citation export or full abstracts. The Actor returns the snippet Scholar shows, not the abstract.
- Downloading PDFs. The Actor returns links; it does not fetch or store files.
- Pages behind a CAPTCHA. The Actor never solves CAPTCHAs. When Scholar blocks every proxy IP, the query gets an `error` row and is not charged.

### Quick start

1. Open the **Input** tab. The form is prefilled with the query `web scraping` and **Max results per query** = 10.
2. Click **Start**.
3. Open the **Results** view in the Dataset. Check `title`, `authors`, `year`, `citedBy` and `pdfUrl`.
4. Open the `RUN_SUMMARY` record in the Key-value store to see the run status and counts.

```json
{
  "queries": ["web scraping"],
  "maxResults": 10
}
```

Expected result: 10 rows with status `complete`, positions 1–10, from the first Scholar results page for "web scraping".

### Use cases

| Buyer job | Input path | Output entity | Key fields | Next step |
| --- | --- | --- | --- | --- |
| Top papers on a topic | `queries` + `maxResults` | Article | `title`, `authors`, `year`, `citedBy` | Export CSV or Excel |
| Papers from a fixed period | `yearFrom` + `yearTo` | Article | `year`, `venue`, `url` | Screening spreadsheet |
| Open full texts | `includeCitations: false` | Article | `pdfUrl`, `fullTextType`, `source` | Download with your own tool |
| Newest papers | `sortByDate: true` | Article | `addedAgo`, `title`, `url` | Read via API |
| Papers by one author or journal | `queries` with `author:` or `source:` | Article | `authorProfiles`, `venue`, `citedBy` | Join on `resultId` |

### Input

| Field | Type | Required | Default / prefill | Allowed values | Effect |
| --- | --- | --- | --- | --- | --- |
| `queries` | array of strings | yes | prefill `["web scraping"]` | 1–200 queries | One Scholar search for each line. Scholar operators work, e.g. `author:"J Smith"`, `source:Nature`, `"exact phrase"`. Empty lines and duplicates are removed. |
| `maxResults` | integer | no | default 10, prefill 10 | 1–100 | Articles per query. The Actor stops earlier when Scholar has no more results. |
| `yearFrom` | integer | no | none | 1000–2100 | Only articles from this year or later. |
| `yearTo` | integer | no | none | 1000–2100 | Only articles from this year or earlier. Must not be lower than `yearFrom`. |
| `includeCitations` | boolean | no | `true` | `true`, `false` | `false` drops \[CITATION] entries: works Scholar knows only from reference lists, usually without a link. |
| `sortByDate` | boolean | no | `false` | `true`, `false` | `true` sorts newest first, like Scholar's "Sort by date". Scholar then shows only articles added in the last year. |
| `proxyConfiguration` | object | no | Apify Proxy (datacenter) | Apify proxy groups or your own proxy URLs | See below. |

A run with an empty `queries` list, `maxResults` above 100 or `yearFrom` > `yearTo` is rejected before any request to Scholar.

**Proxy.** With the default setting the Actor starts on Apify datacenter IPs. When Scholar refuses or shortens a page there, the Actor retries it through Apify residential proxy (US) and keeps that IP for the next page of the same query. If you choose a proxy group or your own proxy URLs, only your proxy is used. Apify Proxy traffic is included in the price; you pay no extra for it.

#### Input example

```json
{
  "queries": ["CRISPR gene editing", "machine learning interpretability"],
  "yearFrom": 2020,
  "yearTo": 2023,
  "includeCitations": false,
  "maxResults": 25
}
```

Expected behavior: two Scholar searches limited to 2020–2023, without \[CITATION] entries, three pages each. Expected output: up to 50 article rows with `year` between 2020 and 2023 where Scholar shows a year. Cost on FREE: $0.001 + 2 × $0.002 + 50 × $0.0007 = $0.040.

### Output

Each Dataset row is one article (`status: "complete"`), or one status row for a query that found nothing (`not_found`, charged only as a query) or failed (`error`, free). The **Results** view shows the main columns; the JSON, CSV and Excel exports contain every field.

| Field | Meaning | Type | Can be empty? |
| --- | --- | --- | --- |
| `query` | Your query | string | no |
| `position` | Place among the articles returned for this query; page 2 normally starts at 11 (repeats from earlier pages are skipped) | integer | null on status rows |
| `page` | Scholar results page (1 = first) | integer | |
| `resultId` | Scholar's own id of the result; stable across runs | string | null on status rows |
| `type` | `article`, `pdf`, `html`, `book` or `citation`, from Scholar's label | string | null on status rows |
| `title` | Article title | string | null on status rows |
| `url` | Link of the title (publisher page or repository) | string | null for \[CITATION] entries without a link |
| `authors` | Author names as Scholar prints them (initials and surname) | array | can be `[]` |
| `authorsTruncated` | `true` when Scholar cut the author list with "…" | boolean | |
| `authorProfiles` | Authors with a Scholar profile: `name`, `profileId`, `profileUrl` | array | can be `[]` |
| `venue` | Journal, conference or book; Scholar may shorten it with "…" | string | null when Scholar shows none |
| `year` | Publication year | integer | null when Scholar shows no year |
| `source` | Domain or publisher at the end of the result line, e.g. `nature.com` | string | yes |
| `snippet` | Text excerpt under the title | string | yes |
| `addedAgo` | With `sortByDate` only: how long ago Scholar added the article | string | null otherwise |
| `citedBy` | Number from "Cited by N" | integer | see below |
| `citedByUrl` | Scholar page with the citing articles | string | yes |
| `versions` | Number from "All N versions" | integer | see below |
| `versionsUrl`, `relatedUrl` | Scholar "All versions" and "Related articles" pages | string | yes |
| `pdfUrl` | Direct full-text link from the right column | string | null when Scholar shows none |
| `fullTextType` | Label Scholar shows for that link: usually `PDF` or `HTML`, sometimes another type such as `PS` | string | null without `pdfUrl` |
| `searchUrl` | Scholar results page this row came from | string | no |
| `status` | `complete`, `not_found` or `error` | string | no |
| `statusReason`, `errorCode` | Why a row is `not_found` or `error`; code such as `SOURCE_BLOCKED` | string | null on article rows |
| `fetchedAt` | When the page was fetched (ISO 8601, UTC) | string | no |

**null is not 0.** `citedBy: 0` means Scholar shows the link bar of the result but no "Cited by" link. `citedBy: null` means the link bar is missing, so the count is unknown. `year: null` means Scholar shows no year; it is never written as 0.

#### Output example

One row from the prefilled run, shortened (`relatedUrl`, `versionsUrl` and `citedByUrl` removed):

```json
{
  "query": "web scraping",
  "position": 1,
  "page": 1,
  "resultId": "vzUZDE4wNWMJ",
  "type": "pdf",
  "title": "Web scraping or web crawling: State of art, techniques, approaches and application.",
  "url": "http://i-csrs.org/Volumes/ijasca/2021.3.11.pdf",
  "authors": ["MA Khder"],
  "authorsTruncated": false,
  "authorProfiles": [
    { "name": "MA Khder", "profileId": "BUTYh5gAAAAJ", "profileUrl": "https://scholar.google.com/citations?user=BUTYh5gAAAAJ&hl=en" }
  ],
  "venue": "International Journal of Advances in Soft Computing & …",
  "year": 2021,
  "source": "i-csrs.org",
  "snippet": "… are required to implement web scrapping properly such as … into what web scraping is, how it works, web scraping stages, …",
  "addedAgo": null,
  "citedBy": 679,
  "versions": 4,
  "pdfUrl": "http://i-csrs.org/Volumes/ijasca/2021.3.11.pdf",
  "fullTextType": "PDF",
  "searchUrl": "https://scholar.google.com/scholar?q=web+scraping&hl=en&as_sdt=0%2C5",
  "status": "complete",
  "statusReason": null,
  "errorCode": null,
  "fetchedAt": "2026-10-17T12:32:46.110Z"
}
```

Scholar results change over time, so the same query can return a different order or citation count tomorrow.

#### Status rows and run summary

| Situation | What you see | What to do |
| --- | --- | --- |
| Articles found | `complete` rows | Use them |
| Scholar found nothing for the query and filters | One `not_found` row with `statusReason` | Check spelling, operators or the year range |
| A page failed after some articles | The articles, then one `error` row: "Stopped after N result(s)" | Run the query again or narrow it |
| Scholar blocked the query on every IP | One `error` row with `errorCode` `SOURCE_BLOCKED` | Try again later |
| Scholar answered no new article on 16 new IPs in a row | The run stops early; open queries get `error` rows with `SOURCE_BLOCKED` | Try again later; you paid only for articles already saved |

The Key-value store record `RUN_SUMMARY` has the run status (`COMPLETE`, `PARTIAL`, `CAPPED`, `NO_RESULTS`, `ERROR` or `INVALID_INPUT`), the stop reason, counts (including `repeatsSkipped`: articles Scholar repeated from earlier pages, which are not saved again) and one line per query with its result count and Scholar's "About N results" total. A "did not match" answer is checked once more on a new IP before the query is reported as `not_found`.

### Pricing

Pay-per-event. You pay for three things:

| Event | When | FREE | BRONZE | SILVER | GOLD and above |
| --- | --- | ---: | ---: | ---: | ---: |
| Actor start | Once per run | $0.001 | $0.001 | $0.001 | $0.001 |
| Query | Once per query that Scholar answered (articles or `not_found`) | $0.002 | $0.002 | $0.002 | $0.002 |
| Result | Each `complete` article row | $0.0007 | $0.00063 | $0.00056 | $0.00049 |

`error` rows are free, and a query that fails before Scholar answers is not charged. Platform usage (compute, proxy, storage) is included.

| Scenario | Input | Charged events | FREE | GOLD |
| --- | --- | --- | ---: | ---: |
| Prefill | 1 query × 10 | start + 1 query + 10 results | $0.010 | $0.008 |
| One deep query | 1 query × 100 | start + 1 query + 100 results | $0.073 | $0.052 |
| 1,000 articles | 10 queries × 100 (needs a longer timeout) | start + 10 queries + 1,000 results | $0.721 | $0.511 |
| Query with no articles | 1 query, `not_found` | start + 1 query | $0.003 | $0.003 |

Your **maximum cost per run** stops the run early when the next article or query would go over it: the run then ends SUCCEEDED with status `CAPPED` and reason `CHARGE_LIMIT`, keeping the rows that fit. Apify controls actual billing; see the **Pricing** tab for your tier.

### Limits

- Up to 200 queries per run and 100 articles per query. In our tests 200 queries × 1 article took about 9 minutes with a 600-second timeout, so raise the timeout for big lists.
- Speed: a 10-article run usually takes under 10 seconds. In our tests 3 queries × 50 articles took about 30 seconds, and 5 queries × 100 articles took 2–3 minutes at 256 MB.
- Deep pages: Scholar often repeats or empties pages after about 70 articles of one query. In our tests 1–2 of 5 queries × 100 ended with a free `error` row after 72–92 articles; the other queries reached 100.
- The default run timeout is 300 seconds. The Actor stops source work 15 seconds before the timeout and keeps what it has: the run ends SUCCEEDED with status `CAPPED` and reason `DEADLINE`. For many queries, raise the timeout in the run options.
- Default memory is 256 MB; more memory does not add results.
- Results are in English Scholar (`hl=en`) with Scholar's default search scope (articles, no patents).

This Actor is independent of Google. It reads public Google Scholar results pages, the same pages you see in a browser without login. Articles belong to their authors and publishers; check their terms before you reuse texts or PDFs.

### API and AI agents

Run the Actor with the same JSON input through the [Apify API](https://docs.apify.com/api/v2/actors-runs-post). Keep your Apify token private. Read the default Dataset items and the `RUN_SUMMARY` record from the finished run. Apify exports JSON, CSV and Excel and can run the Actor on your own schedule.

#### Use with AI agents (MCP)

- MCP server with only this Actor: `https://mcp.apify.com?tools=cliqtomedia/google-scholar-scraper` (authenticate with Apify).
- Smallest useful input: `{"queries": ["web scraping"], "maxResults": 10}`.
- Key output fields: `title` (paper title), `authors` (names), `year` (publication year or null), `citedBy` (citation count or null), `pdfUrl` (direct full text or null).
- A 10-article call usually finishes in under 15 seconds and costs $0.010 on FREE. Limits: 100 articles per query, 200 queries per run.

### Troubleshooting

#### A query returns a `not_found` row, but Scholar in my browser shows articles

Likely reason: a different spelling, operator or year range, or a filter such as `includeCitations: false`. Check the `searchUrl` of the row in a browser. Action: fix the query or remove a filter.

#### The last row of a query is an `error` row

Likely reason: Scholar refused or cut a deeper page on every proxy IP. The articles before it are kept and paid; the `error` row is free. Action: run the query again, or ask for fewer articles per query.

#### The run ends with `CAPPED` and `DEADLINE`

Likely reason: many queries for the run timeout. Action: raise the timeout in the run options, or split the queries into several runs.

### FAQ

#### Do I need a Google account or a SerpApi key?

No. The Actor reads public Scholar pages through Apify Proxy. You only need an Apify account.

#### Why is `citedBy` or `year` empty for some rows?

Scholar does not show them for every result. `null` means "not shown", not zero. \[CITATION] entries often have no year and no link.

#### Can I get every article on a topic?

No. Scholar shows at most about 1,000 results for one search, and it cuts deep pages for automated traffic; this Actor returns up to 100 per query. Use narrower queries or year ranges to cover more.

#### Are the results the same as in my browser?

Not always. Scholar answers different IPs and sessions differently: when we ran the same query several times, the order and some of the articles changed between runs. `position` is Scholar's order at the time of the run, so compare by `title` or `url`, not by position.

### Related Actors

- [DOI Metadata & Bibliography Enrichment](https://apify.com/cliqtomedia/doi-bibliography-enrichment) — turn the papers you found into full bibliographic records by DOI.
- [Google Patent Details Scraper](https://apify.com/cliqtomedia/google-patents-scraper) — the same research step for patents.

### Support

Use the Actor's **Issues** tab on Apify. Include the run ID, the expected result and a non-sensitive input example. Never share tokens or cookies.

# Changelog

This Actor's version history is a separate document: https://apify.com/cliqtomedia/google-scholar-scraper/changelog.md

# Actor input Schema

## `queries` (type: `array`):

What to search on Google Scholar, one query per line, e.g. "web scraping". Scholar operators such as author:"J Smith", source:Nature or "exact phrase" work. Up to 200 queries; duplicates are removed.

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

How many articles to return for each query, up to 100 (Scholar shows 10 per page). You pay per article returned plus a small fee per answered query. Stops earlier when Scholar has no more results. Need more on one topic? Add narrower queries or split by year range.

## `yearFrom` (type: `integer`):

Only articles published in this year or later (Scholar's "Since" / custom range filter). Leave empty for any year.

## `yearTo` (type: `integer`):

Only articles published in this year or earlier. Leave empty for any year.

## `includeCitations` (type: `boolean`):

Keep \[CITATION] entries (works Scholar knows only from other papers' reference lists, without an online copy). Turn off to get only results with a link.

## `sortByDate` (type: `boolean`):

Newest first, as Scholar's "Sort by date" does: Scholar then shows only articles added in the last year, so year filters and totals differ. Default: sort by relevance.

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

Apify datacenter proxy by default. A page Scholar refuses or shortens on datacenter IPs is retried through Apify residential proxy (US). Choose your own proxy group or URLs to turn this off. The Actor never solves CAPTCHAs.

## Actor input object example

```json
{
  "queries": [
    "web scraping"
  ],
  "maxResults": 10,
  "includeCitations": true,
  "sortByDate": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Articles as a table.

## `runSummary` (type: `string`):

Status, per-query counts, charged results and request statistics.

# 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 = {
    "queries": [
        "web scraping"
    ],
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/google-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 = {
    "queries": ["web scraping"],
    "maxResults": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/google-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 '{
  "queries": [
    "web scraping"
  ],
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call cliqtomedia/google-scholar-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cliqtomedia/google-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/hNziKGPAetx29bkbp/builds/7dNGMImlnxv0ZsVtf/openapi.json
