# Google People Also Ask Scraper - Question Tree (`s-r/google-people-also-ask`) Actor

Collect the People Also Ask questions Google shows for a keyword, then expand each one to get the questions behind it. One seed returns four; two levels return dozens. Every row keeps the question that produced it, so the tree can be rebuilt.

- **URL**: https://apify.com/s-r/google-people-also-ask.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** SEO tools, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 run starts

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 People Also Ask Scraper - Question Tree

Collect the **People Also Ask** questions Google shows for a keyword, then
expand each of those questions to get the questions behind *them*.

No API key, no browser, no login.

### One seed, four questions. Or seventeen.

A results page carries four or five People Also Ask entries. On its own that is
a thin answer, and it is what every other tool in this category returns.

The value is in the tree. Ask one of those questions and Google shows four
more, most of them new. Measured on `best crm software`, en/us:

| depth | what happens | questions |
|---|---|---|
| 0 | read the seed's own results page | 4 |
| **1** | also ask each of those four | **17** |

Same seed, one input change, four times the material. Depth 2 and 3 go further
and each level costs roughly four times the previous one, which is exactly why
depth is a visible number in the input form and not a hidden default.

Every row keeps **`parent_question`** and **`depth`**, so the flat dataset
rebuilds into the tree it came from:

```
best crm software
├── What is no 1 CRM?
│   ├── What is the #1 CRM in the world?
│   ├── Which CRM is the most popular?
│   └── Who is the biggest CRM company?
├── Is AI going to replace CRM?
│   └── Will CRM be taken over by AI?
├── What is the #1 CRM platform?
└── Is CRM difficult to learn?
```

A branch that cannot be traced back to the question that produced it is just a
keyword list. This one is a map of how Google thinks the topic breaks down.

### What you get

One row per question:

| field | example |
|---|---|
| `question` | `Can web scraping be detected?` |
| `seed` | `web scraping` |
| `parent_question` | `Is web scraping illegal?` |
| `depth` | `1` |
| `language` | `en` |
| `market` | `us` |
| `position` | `9` |

Duplicates are removed **across the whole run**, not per page. A question that
shows up under two different parents is kept once, under the first parent that
surfaced it, so `questionsReturned` is a count of distinct questions rather
than an inflated total.

### Three things this gets right

**The seed is not sold back to you as a question.** Google writes the query
itself into the same place on the page as the questions. Read that naively and
your export says people also ask "cheap flights amsterdam" about "cheap
flights amsterdam". Worse, an expanding run then asks the same thing again and
loops. Both are handled, structurally and by name.

**There is no answer column, because there are no answers.** Google fetches a
People Also Ask answer at the moment somebody clicks the question, so no answer
exists on the page that was served. Tools that promise answers here are either
doing a second round of work they charge you for anyway, or handing you empty
strings. This one says `answersAvailable: false` in the run summary, once, and
gives you the questions.

**A silent change is reported as a number.** If Google keeps the attribute but
changes the tag around it, a naive extractor returns nothing and the run looks
successful on a keyword you assume was just thin. The summary carries
`queriesWithChangedMarkup` for exactly that case, separately from
`queriesWithNoQuestions`, which is the normal and legitimate one.

### Input

| field | default | notes |
|---|---|---|
| `keywords` | — | One per line. Each becomes a search. |
| `depth` | `1` | `0` reads the seed's page only. `1` also expands each question found. Max `3`. |
| `max_questions` | `200` | Stop after this many distinct questions. Your cost ceiling. |
| `language` | `en` | Two letters. The questions come back written in it. |
| `region` | `us` | Two letters. |
| `attempts` | `20` | How often to retry a search before giving up on it. |

Minimal run:

```json
{ "keywords": ["best crm software"] }
```

Flat, cheapest, one request per seed:

```json
{ "keywords": ["best crm software", "crm for small business"], "depth": 0 }
```

Dutch market:

```json
{ "keywords": ["beste crm software"], "language": "nl", "region": "nl" }
```

### Language and country both matter

People Also Ask is regional, and the two inputs do different jobs: `language`
decides what the questions are written in, `region` decides which market's
questions you get. `beste crm software` at `nl`/`nl` returned questions about
ZZP'ers and small Dutch businesses that have no equivalent in the en/us set for
the same topic. Running one keyword across several markets is a legitimate use
of this Actor, and `language` and `market` are on every row so the results stay
separable afterwards.

### What it is for

**Content briefs.** The depth-1 tree for a target keyword is a section outline
that came from Google rather than from a guess. Questions at depth 0 are the
headings, their children are the subheadings.

**FAQ blocks that match real queries.** These are the questions Google has
already decided are adjacent to the topic, phrased the way it phrases them.

**Finding the gap.** Run the tree for your keyword and for a competitor's.
Questions in theirs and not in yours are pages you have not written.

**Topic modelling at low cost.** A depth-1 run on one seed is five searches. A
depth-2 run is around twenty. That is a topical map for a few cents.

### Pricing

Pay per event.

| event | price |
|---|---|
| Run start | $0,002 |
| Question returned | $0,003 |

One seed at depth 0 is four questions: **$0,014**. One seed at depth 1 returned
seventeen: **$0,053**. `max_questions` is a hard ceiling, so a run cannot cost
more than you decided in advance.

### Limits, stated plainly

- **No answers.** Explained above. Questions only.
- **Four or five per page.** That is what Google publishes. A seed that returns
  four questions at depth 0 is not a failed run, and thin or very specific
  keywords sometimes return none at all. Those are counted in the summary as
  `queriesWithNoQuestions`.
- **Depth costs.** Each level multiplies the searches by roughly four. Depth 3
  on several seeds is a big run; set `max_questions` before starting it.
- **Not every search comes back on the first try.** The Actor retries, and the
  summary reports `requestsRetried` so a slow run is explainable rather than
  mysterious. A query that never came back becomes an entry in the `errors`
  record instead of vanishing.
- **Questions change.** Google rewrites People Also Ask constantly. Two runs a
  week apart on the same keyword will not match exactly, and that is the source
  behaving normally.

### Output

Rows go to the default dataset. The run summary and any errors go to the
default key-value store as `summary` and `errors`. The summary carries the
seed count, queries sent, questions per depth level, retries, and the two
counters described above, so a run can be judged without reading the rows.

# Actor input Schema

## `keywords` (type: `array`):

One keyword per line. Each becomes a Google search whose People Also Ask box is read.

## `depth` (type: `integer`):

0 reads only the questions on the seed's own results page, which is four or five. 1 also asks each of those and collects their questions, which in testing turned 4 into 16. 2 and 3 go further and cost roughly four times as many requests per level.

## `max_questions` (type: `integer`):

Stop after this many distinct questions. Also the cost ceiling for the run.

## `language` (type: `string`):

Two-letter interface language, such as en, nl or de. The questions come back written in it.

## `region` (type: `string`):

Two-letter country code. People Also Ask is regional, so the same keyword gives different questions per market.

## `attempts` (type: `integer`):

How often to retry a query before giving up on it. Google serves a results page to roughly two thirds of attempts, so a handful of retries is normal and the default clears it comfortably.

## Actor input object example

```json
{
  "keywords": [
    "best crm software"
  ],
  "depth": 1,
  "max_questions": 200,
  "language": "nl",
  "region": "nl",
  "attempts": 20
}
```

# Actor output Schema

## `questions` (type: `string`):

One row per question.

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

Seeds, queries sent, questions per depth level, and how many queries returned nothing.

## `errors` (type: `string`):

Queries that could not be read.

# 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 = {
    "keywords": [
        "web scraping"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/google-people-also-ask").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 = { "keywords": ["web scraping"] }

# Run the Actor and wait for it to finish
run = client.actor("s-r/google-people-also-ask").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 '{
  "keywords": [
    "web scraping"
  ]
}' |
apify call s-r/google-people-also-ask --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/google-people-also-ask"
        }
    }
}
```

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/zm2IeAwWTWmfmNPct/builds/ylQjPX95ZmKThSeu0/openapi.json
