# Keyword Research Scraper: 8 search boxes, a-z, questions, CSV (`tallyrake/keyword-research-scraper`) Actor

One seed list, suggestions from every search box you select, one flat CSV or JSON dataset. Google, YouTube, Amazon, Bing, DuckDuckGo, TikTok, Pinterest, App Store and Play. Each row has the suggestion, rank, expansion and engine.

- **URL**: https://apify.com/tallyrake/keyword-research-scraper.md
- **Developed by:** [Tallyrake Data](https://apify.com/tallyrake) (community)
- **Categories:** SEO tools, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 result rows

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## Keyword Research Scraper: 8 search boxes, a-z, questions, CSV

Search-box suggestions from up to eight engines, one seed list, one flat dataset. Give it keywords. It asks Google, YouTube, Amazon, Bing, DuckDuckGo, TikTok, Pinterest, and the App Store plus Google Play (whichever you select) for the seed, the seed plus a to z, question words, and prepositions. Comparisons and numbers are off unless you turn those expansions on. One row per suggestion per seed per engine, with a stable `id` and an `engine` field so you can pivot the CSV.

Engines run one after another, each with its own pacing. If one engine fails or blocks the run's IP, the run writes `{ "type": "engine_error", "engine": "...", "reason": "..." }` and continues with the rest. The run status message and the `SUMMARY` key-value record both say how many rows each engine produced.

This is the same suggestion client as the single-engine actors, called in one run. It is not search volume, not a results page, and not a place to download products, videos, pins, or app listings.

Same box, one engine at a time: [Google](https://apify.com/tallyrake/google-autocomplete-scraper), [YouTube](https://apify.com/tallyrake/youtube-autocomplete-scraper), [Amazon](https://apify.com/tallyrake/amazon-autocomplete-scraper), [Bing](https://apify.com/tallyrake/bing-autocomplete-scraper), [DuckDuckGo](https://apify.com/tallyrake/duckduckgo-autocomplete-scraper), [TikTok](https://apify.com/tallyrake/tiktok-autocomplete-scraper), [Pinterest](https://apify.com/tallyrake/pinterest-autocomplete-scraper), [App Store and Play](https://apify.com/tallyrake/appstore-autocomplete-scraper).

### What you get

| Field | Example |
|---|---|
| `engine` | `google`, `youtube`, `amazon`, `bing`, `duckduckgo`, `tiktok`, `pinterest`, `appstore` |
| `keyword` | `coffee grinder` (your seed) |
| `query` | `coffee grinder b` (the exact string sent) |
| `suggestion` | `burr coffee grinder` |
| `rank` | `1` (position in that engine's list for that query) |
| `expansion` | `direct`, `alphabet`, `question`, `preposition`, `comparison`, `number`, `depth` |
| `modifier` | `b`, `how`, `for`, `vs` |
| `depth` | `1` or `2` |
| `country` | `us` (the country you set, on every row) |
| `language` | `en` (the language you set, on every row) |
| `fetchedAt` | ISO timestamp |
| `id` | first 16 hex chars of sha1 of engine, keyword, and suggestion, joined with a vertical bar |

Rows are deduplicated per seed per engine (case-insensitive), so a suggestion that appears under several modifiers is kept once, under the first query that produced it. The same text from two engines is two rows. App Store and Play dedupes per store, so Apple and Play can both keep "photo editor". Those two rows share an `id`, because the id does not include the store. Filter on `store` before you unique the id column.

Engine-specific fields from the single-engine actors are still on the row when that engine produced them:

| Engine | Extra fields |
|---|---|
| Google, YouTube, Bing, DuckDuckGo | `relevance`, `suggestType`, `subtypes`. This actor uses Google's firefox client, so `relevance` is null. `subtypes` is still set when Google sends it |
| Amazon | those, plus `marketplace` (`US`, `UK`, ...) |
| Bing | `market` (`en-US`) |
| DuckDuckGo | `region` (the kl code, `us-en`) |
| TikTok | `hotLevel`, `ecomIntent`, `region` |
| Pinterest | `suggestionType`, `suggestionId`, `url`, `pinCount`, `boardCount`, `trending`, `verified`, `subLabel`, `region` |
| App Store and Play | `store` (`apple` or `google-play`) |

An engine that fails or gets blocked adds one dataset row and no suggestion fields: `type` = `engine_error`, `engine`, `reason`.

### Engines

| Input | Search box | Requests per query | How country and language are applied |
|---|---|---|---|
| `google` | Google Autocomplete | 1 | `hl` = language, `gl` = country, firefox client (up to 10 suggestions) |
| `youtube` | YouTube | 1 | same suggestion host as Google, with `ds=yt` |
| `amazon` | Amazon | 1 | marketplace from country: US, UK, DE, FR, ES, IT, CA, JP, IN, MX, BR, AU. `gb` is UK. Any other country uses the US site |
| `bing` | Bing | 1 | market `language-COUNTRY`, e.g. `en-US`. `uk` and `gb` are sent as GB. A language that already looks like `en-GB` is sent as-is |
| `duckduckgo` | DuckDuckGo | 1 | kl `country-language`, e.g. `us-en`. `gb` is sent as `uk-en` |
| `tiktok` | TikTok | 1 | web search params (`aid=1988`, region from country) |
| `pinterest` | Pinterest | 1 | host from country (`us` is www, `gb` and `uk` are uk.pinterest.com) |
| `appstore` | Apple App Store, then Google Play | 2 | both stores, same country and language. Apple's storefront header and Play's batchexecute call are unchanged |

### Example input

```json
{
  "keywords": ["coffee grinder"],
  "engines": ["google", "youtube", "amazon"],
  "expansions": ["alphabet", "questions", "prepositions"],
  "depth": 1,
  "country": "us",
  "language": "en",
  "dedupe": true,
  "proxyConfiguration": { "useApifyProxy": true },
  "maxConcurrency": 3,
  "pacingMs": 200
}
```

Paste seeds one per line. The default engines are Google, YouTube, and Amazon. The default expansions send 52 queries per seed per engine (1 seed + 26 letters + 14 question words + 11 prepositions). Comparisons and numbers are not in that 52. One seed sends many queries; the output is dataset rows, not queries. An `engine_error` row is a dataset row.

Proxy defaults to Apify Proxy with no proxy group (datacenter). Leave the groups empty. Turn proxy off only for a small direct run.

`country` is also read from `region` when `country` is omitted. `country` wins when both are set.

### Recipes

**Three boxes, one sheet.** Seeds: your head terms. Engines: the default three. Expansions: alphabet. Depth 1. Pivot the CSV on `engine`.

**Questions people type.** Engines: `google` and `youtube`. Expansions: questions. Depth 1. The `modifier` column is the question word.

**App names.** Engines: `appstore` only. That is Apple and Google Play. Read `store` before you dedupe on `id`.

### Reliability and cost

Billing terms are on the Pricing tab of the listing. Apify platform usage is a separate charge and is not the listing price. Empty queries produce no suggestion rows. Every request is paced with a jittered delay (`pacingMs`, default 200 ms). On HTTP 429, 403, or 503 the engine rotates its session and backs off. After repeated blocks that engine stops, an `engine_error` row is written, and the next engine starts.

No soaked run of this actor has been measured on Apify yet, so this README does not state a success rate or a platform-usage cost. The single-engine READMEs have their own measured yields. Do not add those up and treat the sum as a promise for this actor.

### Cost limit

If a run reaches the "Maximum total charge per run" you set, it stops in process rather than being aborted by the platform: the run ends with status SUCCEEDED, and the run status message says how many rows were written and how many were found but not written. Engines that had not started are marked not run in `SUMMARY`. Re-running the same input writes the same prefix again, so raise the cap before re-running if you want the rest.

### Bigger jobs

Need this as a one-off file instead of a run? Ask for a quote at https://tallyrake.com/datasets/. We may decline. A quote binds nothing, and a job exists only when we confirm it in writing, before any payment.

### Limits

Default run, one seed, three engines, depth 1: 156 suggestion requests (52 per engine). All eight engines at the default expansions, depth 1: 468 requests per seed (seven engines at 52, App Store and Play at 52 per store). Depth 2 sends first-level suggestions back, up to `maxQueriesPerKeyword` further requests per seed per engine (per store for App Store and Play). The cap applies to each depth's plan, the same way it does on the single-engine actors, not as one combined total across engines.

`maxConcurrency` (1 to 10) is inside one engine. Engines are not queried at the same time.

Suggestions are the list that box returned at the time of the run. They carry no search volume, no CPC, and no difficulty. The `id` here is not the id the single-engine actors write (those hash more fields). Join on `engine` + `keyword` + `suggestion`, not on `id`, if you mix the two.

Export CSV for Excel and Google Sheets. Export JSON for pipelines. `SUMMARY` in the key-value store has per-engine query counts, row counts, block flags, and error strings.

### What it is NOT

This is not eight separate runs you merge by hand, and it is not a scraper of search result pages. It does not return products, prices, reviews, videos, pins, boards, shops, or app listings. It does not log in. It does not return Google Trends, search volume, or keyword difficulty. A blocked engine does not cancel the others, and a successful engine does not fill in rows for an engine that returned nothing.

# Actor input Schema

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

One seed per line. Each seed is expanded (see Expansions) and every expanded query is sent to each selected engine.

## `engines` (type: `array`):

Search boxes to query, in this order. Engines run one after another. A blocked or failed engine is recorded and the run continues. appstore queries Apple and Google Play.

## `expansions` (type: `array`):

How to fan each seed out. alphabet: seed + a..z (26 queries). questions: who/what/why/how/when/where/which/is/are/can/does/do/will/should + seed (14). prepositions: seed + for/with/without/to/near/in/on/from/like/after/before (11). comparisons: seed + vs/or/versus/and (4). numbers: seed + 1..5/10/2026 (7). The plain seed is always queried. The same plan is sent to every selected engine.

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

1 = expand the seeds only. 2 = also send every first-level suggestion back to that engine (capped by Max queries per keyword, same rule as the single-engine actors).

## `maxQueriesPerKeyword` (type: `integer`):

Cap on suggestion requests per seed per engine, per depth. App Store and Play applies the cap once per store.

## `country` (type: `string`):

Two-letter country code, e.g. us, gb, de. Also read from region when country is omitted. Amazon's marketplace, Bing's market, DuckDuckGo's kl, Pinterest's host and TikTok's region are derived from this. gb is sent to Amazon as UK, to Bing as GB, and to DuckDuckGo as uk.

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

Language code, e.g. en, de, es, fr, pt, ja. Bing's market is language-COUNTRY (en-US) unless language already looks like en-US. DuckDuckGo's kl is country-language (us-en).

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

Keep the first occurrence of each suggestion per seed per engine (case-insensitive). App Store and Play dedupes per store, so the same text from Apple and Play is two rows. The same text from two engines is two rows.

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

Apify Proxy, datacenter by default (leave proxy groups empty). Each engine rotates its own session on HTTP 429 or 403. A block stops that engine only. Residential is optional if datacenter is blocked.

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

Parallel suggestion requests inside one engine. Engines themselves run one after another. 3 is the house default; up to 10 with Apify Proxy.

## `pacingMs` (type: `integer`):

Base jittered delay between requests for each engine; 0 disables pacing. Each engine paces itself.

## Actor input object example

```json
{
  "keywords": [
    "coffee grinder"
  ],
  "engines": [
    "google",
    "youtube",
    "amazon"
  ],
  "expansions": [
    "alphabet",
    "questions",
    "prepositions"
  ],
  "depth": 1,
  "maxQueriesPerKeyword": 300,
  "country": "us",
  "language": "en",
  "dedupe": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 3,
  "pacingMs": 200
}
```

# Actor output Schema

## `suggestions` (type: `string`):

All rows (one per suggestion per seed per engine, plus an engine_error row when an engine fails).

## `csv` (type: `string`):

The same rows as CSV.

# 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": [
        "coffee grinder"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tallyrake/keyword-research-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 = { "keywords": ["coffee grinder"] }

# Run the Actor and wait for it to finish
run = client.actor("tallyrake/keyword-research-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 '{
  "keywords": [
    "coffee grinder"
  ]
}' |
apify call tallyrake/keyword-research-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tallyrake/keyword-research-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/00xB11LZunjzQ3lNg/builds/dXUDOsTkooifU78TK/openapi.json
