# Autocomplete & Keyword Suggest Scraper - 7 Engines at Once (`neverempty/keyword-suggest-scraper`) Actor

For SEO keyword research, PPC and product research: autocomplete suggestions for your seeds from Google, YouTube, Bing, DuckDuckGo, Amazon, eBay and Wikipedia in one run, one row each with the engine, the exact query and its rank. Seven engines show which surfaces a term and which does not.

- **URL**: https://apify.com/neverempty/keyword-suggest-scraper.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** SEO tools, Social media, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.73 / 1,000 suggestion returneds

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

## Autocomplete & Keyword Suggest Scraper - seven engines in one run

**For SEO keyword research, content planning, PPC negative-keyword lists and product-demand research**: autocomplete suggestions from **seven** services in a single run — **Google, YouTube, Bing, DuckDuckGo, Amazon, eBay and Wikipedia** — one row per suggestion, with the engine that produced it, the exact query that was sent, and the rank it held in that engine's list.

**Autocomplete is the only free, first-party record of what people actually type.** Asking one engine tells you what searchers type; asking seven tells you something you cannot get any other way: **which engine surfaces a term and which does not**. A phrase Google suggests but Amazon does not is informational; one Amazon suggests and Google does not is commercial; one YouTube suggests is a video someone should make.

**It expands a seed the way a keyword tool does, and it tells you the cost before it runs away with your bill.** A–Z (**26** queries), 0–9 (**10**), question words (**12**: how, what, why, where, when, which, who, can, is, are, does, should) and prepositions (**8**: for, with, without, near, like, to, from, vs). All four on turns one seed into **57** queries per engine — three engines is **171** requests and three seeds is **513** — so there is a request limit as well as a row limit. Measured on 2026-09-08, responses came back in **206–370 ms** each with **no proxy needed**.

**Every seed gets its fair share of the row limit.** Measured on 2026-09-08 before this was fixed, a plain cut left the third of three seeds with **6 rows out of 307** and said nothing about it.

No API key. Export as JSON, CSV or Excel.

### What one run gives you

| Column | What it is |
|---|---|
| `engine`, `engineTitle` | which service produced the suggestion |
| `seed` | the term you asked for |
| `query` | the exact string that was sent - with expansion on, this is `seed a`, `how seed` and so on |
| `expansion` | which expansion produced that query: `seed`, `alphabet`, `digits`, `questions`, `prepositions` |
| `suggestion` | the suggested phrase |
| `rank` | its position in that engine's list (1 = first) |
| `isSeedItself` | engines usually repeat the seed as the first suggestion; this marks it |
| `wordCount`, `characterCount` | for sorting long-tail phrases |
| `requestedCountry`, `requestedLanguage` | what was asked for - see the honest note below |
| `engines`, `engineCount`, `bestRank` | with deduplication on: every engine that produced the phrase, and its best rank |

### Expansion: one seed into many queries

Autocomplete only answers what you type, so the standard way to go deep is to type more:

| Switch | Queries added per seed, per engine |
|---|---|
| A-Z expansion | 26 (`seed a` … `seed z`) |
| 0-9 expansion | 10 (`seed 0` … `seed 9`) |
| Question expansion | 12 (`how seed`, `what seed`, `why`, `where`, `when`, `which`, `who`, `can`, `is`, `are`, `does`, `should`) |
| Preposition expansion | 8 (`seed for`, `with`, `without`, `near`, `like`, `to`, `from`, `vs`) |

All four on turns one seed into **57** queries per engine. Three engines means 171 requests, and
three seeds means 513. That is why there is a **request limit** as well as a row limit: the row limit
cannot stop requests, because filtering happens after the answer arrives. When the request limit is
reached the run stops asking and says so in a free row. Within one seed the same query is never sent
twice; two seeds that expand onto the same query will each send it.

### What is measured, and what is not

- **The seven engines above are the ones that answered** when this was measured from Apify on
  2026-09-08 (206-370 ms each, no proxy needed). The old Play Store suggest endpoint answered **404**
  and is not offered rather than being offered and failing.
- 🔴 **Country targeting is honest here.** Google and YouTube decide the region mostly from the IP the
  request leaves from, not from the parameter. Measured on the same day: `de` returned German
  suggestions, `jp` returned the same US English list. So the column is called `requestedCountry` -
  what was asked for - and this README does not claim it always changes the answer. Bing, DuckDuckGo,
  Amazon, eBay and Wikipedia ignore the parameter entirely.
- **An empty list is an answer, not a failure.** All seven services return an empty list for a term
  with no suggestions. Those queries produce no charged rows, and a free row at the end says how many
  of the run's requests came back empty - so you can tell "nothing is suggested here" apart from
  "this was never asked".
- **The row limit never drops a whole seed.** When more suggestions were collected than you asked
  for, the rows are taken evenly from every seed and a free row lists how many each seed ended up
  with. Measured on 2026-09-08 before this was fixed, a plain cut left the third of three seeds with
  6 rows out of 307 and said nothing.
- **The same suggestion from the same engine is charged once.** Expansion makes an engine repeat
  itself across queries; those repeats are dropped by default, and `keepRepeatsPerEngine` brings them
  back if you want to see every query that produced a phrase.
- **An answer in a shape this Actor has not measured is reported, not read as "no suggestions".**
  That is the difference between "Amazon has nothing for this" and "Amazon changed its format".
- An engine name that is not one of the seven is **rejected before the run starts** (Apify validates the
  input against the list), so a typo costs you nothing instead of producing a half-empty result.

### Rows that are never charged

| `status` | What happened |
|---|---|
| `no-results` | every request came back with an empty suggestion list - that is the engines' answer |
| `blocked` | a service answered with a bot check instead of suggestions |
| `unreadable` | a service could not be read, or answered in a shape this Actor has not measured |
| `no-match` | suggestions were read and none matched your filters |
| `budget-reached` | the run collected more than your row limit, or hit the charge limit you set; the row says how many were left out |

### Filters

`filterKeywords` / `excludeKeywords` (searched in the suggestion, the query and the seed),
`minWords`, `maxWords`, `excludeSeedItself`, and `deduplicate` to collapse the same phrase across
engines into one row that lists them all. Filters run before the cap, so
`maxSuggestions` counts the rows you actually receive.

### Input

| Field | What it does |
|---|---|
| `keywords` | the seed terms |
| `engines` | which of the seven services to ask |
| `expandAlphabet`, `expandDigits`, `expandQuestions`, `expandPrepositions` | how far to expand each seed |
| `country`, `language` | sent to Google and YouTube; `language` also picks the Wikipedia edition |
| `amazonMarketplaceId` | which Amazon storefront to ask (default is Amazon US) |
| `deduplicate`, `excludeSeedItself`, `minWords`, `maxWords`, `filterKeywords`, `excludeKeywords` | shaping the result |
| `maxSuggestions` | hard cap on returned rows, taken evenly from every seed |
| `maxRequests` | hard cap on requests sent to the engines - the limit that protects them |
| `keepRepeatsPerEngine` | keep every occurrence when one engine repeats a phrase across queries |
| `useProxy` | retry through a proxy only if a service answers with a bot check |

# Actor input Schema

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

The terms to get autocomplete suggestions for, one per line - for example 'coffee grinder'. Every seed is sent to each engine you selected, together with whichever expansions you turn on below.

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

Which autocomplete services to ask. google = Google web search, youtube = YouTube, bing = Bing, duckduckgo = DuckDuckGo, amazon = Amazon product search, ebay = eBay, wikipedia = Wikipedia article titles. Each engine is a separate request per query, so three engines cost three times as many requests as one. Only these seven names are accepted: anything else is rejected by Apify before the run starts, so a typo costs you nothing instead of producing a half-empty result.

## `expandAlphabet` (type: `boolean`):

Also ask for 'seed a', 'seed b' ... 'seed z'. This is the standard way to surface long-tail terms: it turns one seed into 27 queries per engine, so switch it on when you want depth and watch the request count.

## `expandDigits` (type: `boolean`):

Also ask for 'seed 0' ... 'seed 9'. Useful for versioned or list-shaped terms ('iphone 1', 'top 5'). Adds 10 queries per seed per engine.

## `expandQuestions` (type: `boolean`):

Also ask for 'how seed', 'what seed', 'why seed', 'where', 'when', 'which', 'who', 'can', 'is', 'are', 'does', 'should'. This is where the questions people actually type come from. Adds 12 queries per seed per engine.

## `expandPrepositions` (type: `boolean`):

Also ask for 'seed for', 'seed with', 'seed without', 'seed near', 'seed like', 'seed to', 'seed from', 'seed vs'. Adds 8 queries per seed per engine.

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

Two-letter country code sent to Google and YouTube, for example de or gb. Measured on 2026-09-08: this changes the answer for some countries (de returned German suggestions) and is ignored for others (jp returned the same US English list), because these services decide the region mainly from the IP the request comes out of. Every row carries requestedCountry so you can see what was asked for rather than assume what was returned. Bing, DuckDuckGo, Amazon, eBay and Wikipedia ignore it.

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

Language code sent to Google and YouTube (hl), and used to pick the Wikipedia edition - en goes to en.wikipedia.org, de to de.wikipedia.org. Defaults to en.

## `amazonMarketplaceId` (type: `string`):

Which Amazon storefront to ask. The default ATVPDKIKX0DER is Amazon US - the value measured to work on 2026-09-08. Other storefronts have their own ids; if you pass one this Actor has not measured and Amazon answers in an unexpected shape, you get a free row saying so rather than an empty result.

## `maxSuggestions` (type: `integer`):

Hard cap on the rows returned in total, applied after the filters and deduplication. When more were collected than this, the rows are taken evenly from every seed so no seed disappears, and a free row tells you how many were left out and how many each seed got. You are charged for the rows you actually receive.

## `maxRequests` (type: `integer`):

Hard cap on how many requests this run sends to the search engines. This is the limit that protects the services you are asking: expansions multiply queries fast (four expansions, three seeds and seven engines is 1,197 requests), and the row limit above cannot stop them because filtering happens after the answer arrives. When the cap is reached the run stops asking and says so in a free row.

## `deduplicate` (type: `boolean`):

When the same suggestion comes back from several engines or several seeds, return it once with the list of engines and seeds that produced it and its best rank. The per-row columns that no longer apply to a merged row - engine, query, expansion, rank and seed - are left empty rather than showing whichever row happened to be found first. Off = one row per engine, which is what you want when comparing engines.

## `keepRepeatsPerEngine` (type: `boolean`):

By default, when the same engine returns the same suggestion for two different expanded queries, only the first row is kept and charged - it is the same fact twice. Turn this on to keep every occurrence, with the query that produced each one. Measured on 2026-09-08, repeats were 2% of rows on a light expansion and up to 30% on a heavy one.

## `excludeSeedItself` (type: `boolean`):

Engines usually return the seed term as its own first suggestion. Turn this on to drop that row.

## `minWords` (type: `integer`):

Keep only suggestions with at least this many words - a simple way to keep long-tail phrases and drop one-word terms. 0 = no minimum.

## `maxWords` (type: `integer`):

Keep only suggestions with at most this many words. 0 = no maximum.

## `filterKeywords` (type: `array`):

Keep only suggestions whose text, query or seed contains at least one of these words. Case is ignored. Leave empty for no filter.

## `excludeKeywords` (type: `array`):

Drop a suggestion when these words appear in its text, in the query that was sent, or in the seed - the same three fields the 'must contain' filter searches. That means excluding a word that is part of your seed removes every row for that seed. Applied after the 'must contain' filter.

## `useProxy` (type: `boolean`):

Off by default because all seven services answered directly from Apify when this was measured on 2026-09-08 (206-370 ms). Turn it on to retry through a residential proxy if a service ever answers with a bot check; a proxy makes each run slower and more expensive, so it is not used unless it is needed.

## Actor input object example

```json
{
  "keywords": [
    "coffee grinder"
  ],
  "engines": [
    "google",
    "youtube",
    "bing"
  ],
  "expandAlphabet": false,
  "expandDigits": false,
  "expandQuestions": false,
  "expandPrepositions": false,
  "country": "",
  "language": "en",
  "amazonMarketplaceId": "ATVPDKIKX0DER",
  "maxSuggestions": 1000,
  "maxRequests": 500,
  "deduplicate": false,
  "keepRepeatsPerEngine": false,
  "excludeSeedItself": false,
  "minWords": 0,
  "maxWords": 0,
  "filterKeywords": [],
  "excludeKeywords": [],
  "useProxy": false
}
```

# Actor output Schema

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

One row per autocomplete suggestion: which engine produced it, the seed you asked for, the exact query that was sent, which expansion produced that query (seed, alphabet, digits, questions or prepositions), the suggestion text, its rank in that engine's list, whether it is just the seed repeated, its word and character counts, and the country and language that were requested. With deduplication on, a suggestion seen in several engines comes back once with the list of engines that produced it and its best rank. Engines that answered with a bot check, an unexpected shape or an unsupported name come back as their own rows and are not charged; so does a run where every engine answered with an empty list.

# 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"
    ],
    "engines": [
        "google",
        "youtube",
        "bing"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/keyword-suggest-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"],
    "engines": [
        "google",
        "youtube",
        "bing",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/keyword-suggest-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"
  ],
  "engines": [
    "google",
    "youtube",
    "bing"
  ]
}' |
apify call neverempty/keyword-suggest-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/keyword-suggest-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/W8ydyS014Kt7GuFcZ/builds/TUn5JlYVpRDwNFbgu/openapi.json
