Keyword Search Volume Scraper — Google Keyword Planner, CPC avatar

Keyword Search Volume Scraper — Google Keyword Planner, CPC

Pricing

from $2.00 / 1,000 keyword results

Go to Apify Store
Keyword Search Volume Scraper — Google Keyword Planner, CPC

Keyword Search Volume Scraper — Google Keyword Planner, CPC

Google Ads monthly search volume, CPC and competition for your keyword list — real Keyword Planner figures, nothing modelled; a keyword with no data is never charged. From $2.00 per 1,000 keyword results; one $0.19 fresh-lookup fee per run that buys new data (cached runs pay none).

Pricing

from $2.00 / 1,000 keyword results

Rating

0.0

(0)

Developer

Steadyfetch Team

Steadyfetch Team

Maintained by Community

Actor stats

0

Bookmarked

56

Total users

50

Monthly active users

2 hours ago

Last modified

Share

Never pay for a keyword with no data — or for one you already have. Paste a keyword list and get Google Ads average monthly searches, competition, the top-of-page bid range, 12-month trend direction — and CPC wherever Google publishes one — or expand a keyword into new keyword ideas with the same metrics.

Using an AI agent? Pin this actor in Apify's MCP server with one link: https://mcp.apify.com?tools=steadyfetch/keyword-search-volume-scraper

  • Actor id: steadyfetch/keyword-search-volume-scraper
  • Input: { "keywords": ["project management software"] } — the one field you have to set.
  • Cap the bill: set maxTotalChargeUsd on the run (a run option, not Actor input), e.g. 0.50 — the run stops when it reaches it.

Real Google Ads Keyword Planner data through a licensed data provider. No "AI search volume", no modelled numbers, no keyword-difficulty score we invented: every field is Google's own figure, and isEstimated is false on every row for that reason.

Just want to see it work? Click Start with nothing set and the run looks up two everyday keywords ("project management software", "keyword research tool") for the United States, charged like any run — two keyword rows, plus the fresh-lookup fee only if those two keywords are not already in our 30-day cache (they usually are) — so the first thing you see is real Keyword Planner rows. Pick a country, a network or a limit but paste no keywords and that same sample runs under your settings, with one uncharged note row naming them; a limit larger than the sample's own is capped at it. Paste your own list for your own run.

Keyword Search Volume Scraper input form in the Apify console: the keyword list and paste box with the what-to-return, market and limits sections

Unofficial. This actor is not affiliated with, endorsed by, or sponsored by Google. "Google", "Google Ads" and "Keyword Planner" are trademarks of Google LLC, used here only to describe where the figures come from.

From $2.00/1,000 keyword results — all-inclusive pay per event, charged only on delivery. A run that buys fresh search-volume data also pays one fresh-lookup fee of $0.19 — once per run, whatever the size of the list. Runs answered entirely from our 30-day cache, and runs whose fresh lookup came back with no data, pay no fee. Issues answered in a couple of hours.

Every row carries charged and missReason, so you can reconcile the invoice from the dataset itself without opening the console. Only rows with charged: true were billed.


Output

This is the dataset table of a real run — three keywords looked up for the United States — exactly as it appears in the Apify console:

Dataset table of a real Keyword Search Volume Scraper run: three keywords with average monthly searches, CPC, competition, top-of-page bid range and trend direction

fieldnotes
keywordyour own spelling, echoed back; providerKeyword is the lowercased form Google matched
seedthe keyword an idea came from. null on your own keywords — and on keyword ideas, because your keywords are expanded together in one lookup that does not report which of them produced which idea
seededFromon a keyword idea, the list of your keywords it was expanded from; null on your own keywords
avgMonthlySearchesGoogle's average monthly searches. 0 means "fewer than Google reports", null means no data (and no charge)
cpcUsdaverage cost per click — null on the keywords Google publishes no average for, and then cpcMissReason says which case it is
cpcMissReasonwhy cpcUsd is empty on this row, or null when the CPC arrived
emptyFieldsevery promised field this row came back without, each with its reason. {} on a complete row — see below
competition · competitionIndexLOW / MEDIUM / HIGH, and the 0–100 index behind it
lowTopOfPageBidUsd · highTopOfPageBidUsdthe top-of-page bid range
monthlySearchVolumes[]12 months of {year, month, searches}, oldest month first
trendDirectionrising / flat / falling, derived from that series
dataAsOfthe last month Google itself reported, e.g. 2026-07
country · language · networkthe market this number is for
source · isEstimatedalways google_ads and false
servedFromCachewhether this row came from our 30-day cache (see below)
repeat · firstSeenAt · firstSeenRunIdtrue when your account already had this keyword: the same figures handed back from the run named here, and nothing charged for them
charged · missReason · statusReasonthe reconciliation trio

Plus one summary row with delivered, stoppedBy, resumeCursor and chargedEvents.


And one delivered row in full, from a real run on 2026-09-12. Each keyword with data is one keyword-result charge; a keyword Google has no data for is never charged. monthlySearchVolumes is the full 12-month history, oldest month first.

{
"schemaVersion": 1,
"seed": null,
"seededFrom": null,
"keyword": "quartz countertop sealer",
"providerKeyword": "quartz countertop sealer",
"avgMonthlySearches": 1600,
"cpcUsd": 0.88,
"competition": "HIGH",
"competitionIndex": 95,
"lowTopOfPageBidUsd": 0.23,
"highTopOfPageBidUsd": 1.58,
"monthlySearchVolumes": [
{"year":2025,"month":9,"searches":2900},
{"year":2025,"month":10,"searches":1600},
{"year":2025,"month":11,"searches":1600},
{"year":2025,"month":12,"searches":1600},
{"year":2026,"month":1,"searches":1600},
{"year":2026,"month":2,"searches":1600},
{"year":2026,"month":3,"searches":1600},
{"year":2026,"month":4,"searches":1300},
{"year":2026,"month":5,"searches":1300},
{"year":2026,"month":6,"searches":1000},
{"year":2026,"month":7,"searches":1300},
{"year":2026,"month":8,"searches":1300}
],
"trendDirection": "falling",
"dataAsOf": "2026-08",
"cpcMissReason": null,
"emptyFields": {},
"country": "US",
"language": "en",
"network": "google",
"source": "google_ads",
"isEstimated": false,
"servedFromCache": false,
"charged": true,
"repeat": false,
"firstSeenAt": null,
"firstSeenRunId": null,
"repeatCheck": null,
"missReason": null,
"retryable": false,
"statusReason": "Google Ads search-volume data.",
"sourceIndex": 0,
"fetchedAt": "2026-09-12T12:21:42.697Z",
"_demo": false,
"#k": "kw:quartz countertop sealer",
"#ce": {"keyword-result":1}
}

When a promised field comes back empty

Google does not publish every figure for every keyword. About one keyword in eleven comes back with no average CPC even though its bid range is right there, and a handful arrive with no bid range at all. We never invent the number, and we never quietly leave the column blank either: every field on this list that arrives empty says why, on that row.

emptyFields maps the field name to the reason; cpcMissReason repeats the CPC one as its own column because CPC is the headline. statusReason says the same thing in a sentence.

reasonwhat it means
no-historical-avg-cpcGoogle published a top-of-page bid range for this keyword but no average CPC. Use the bid range as the cost signal
no-advertiser-cost-dataNo average CPC and no bid range — too little advertiser activity for Google to publish either
no-bid-range-reportedThe CPC arrived, the top-of-page bid range did not
no-competition-dataGoogle reports no competition level or index for this keyword
fewer-than-six-months-of-dataToo short a series to call a trend direction — the months themselves still ship
no-monthly-seriesGoogle returned no month-by-month series, so there is no dataAsOf either

A row like this is still a real answer and is still charged once, like any other row — volume, competition, bid range and the 12-month series are all there. Rows with no search volume are the ones you are never charged for, and they carry missReason instead.

Run {} and you get the default two-keyword sample, charged like any run — real rows in exactly this shape, empty fields named where Google withholds them. Set a market or a limit and paste no keywords and the same sample runs under what you set.


Agent / API paste-block

Actor: steadyfetch/keyword-search-volume-scraper
Required: keywords (array of keywords; leave it empty for a two-keyword sample, charged like
any run — settings you do change scope that sample)
Optional: keywordsText (string — paste a list, newline or comma separated)
mode (metrics-only | ideas | both, default metrics-only)
country (ISO-2, ISO-3, English name, Google Ads country location code, a
locale tag, or the first country in a comma list — US, USA, GB,
2840, en-GB, "Dubai, United Arab Emirates"; default US)
language (code, BCP-47 tag or English name — en, eng, en-US, English; default en)
network (google | google-and-partners, default google)
maxIdeasPerSeed (integer, default 200, up to 2,000 — ideas modes only; the run's
idea ceiling is this number times the keywords expanded)
maxIdeaSeeds (integer, default 5, up to 20 — ideas modes only)
maxItems (integer, default 1000 — up to 100,000 for the whole run)
maxRunSeconds (integer, default 90030 s to 1 h; clean stop, never a timeout kill)
Charges: keyword-result once per keyword row delivered with data
keyword-lookup $0.19 once per run that buys fresh data and delivers at least one row
(cache-only and no-data runs pay none)
Run option: Maximum cost per run — at least $0.25 so a fresh lookup fits
Build spec: https://apify.com/steadyfetch/keyword-search-volume-scraper/api
Token: https://console.apify.com/settings/integrations
curl -X POST "https://api.apify.com/v2/acts/steadyfetch~keyword-search-volume-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"keywords":["project management software","keyword research tool"],"country":"US"}'

Agents and MCP callers: omit an optional field, or send it as null — both mean "use the default". Only keywords is required, and it needs a real value.


How the trend direction is worked out

The last three months of the 12-month series against the first three of the same series. 20% or more above → rising. 20% or more below → falling. Anything between → flat. Fewer than six months of data → null, never a guess. The raw series ships with every row, so you can apply your own rule instead.


Three things about Google's numbers that most listings do not tell you

  1. Google quantises search volume. Volumes come back on a coarse ladder — 5,400 / 40,500 / 165,000 — not as exact integers. They are Google's own published figures, not ours, and they are not precise to the search.
  2. Google groups similar keywords. Close variants can share one combined figure. If two of your keywords come back identical, that is usually why. Splitting them across separate runs sometimes separates them.
  3. Some phrases have no published volume at all — and it is per phrase, not per topic. Google reports nothing for a long tail of real, well-searched phrases, and which ones is inconsistent: measured on one US/English lookup, dentist near me and gym near me come back at 1,830,000 and physical therapy san diego at 1,600, while
    physical therapy near me
    , physical therapy carlsbad and physical therapy encinitas come back empty. The phrase is searched; Google simply does not publish a number for it. All we can see is that the figure came back blank — it could be folded into a broader term, sit under Google's reporting floor, or be something Google has nothing for, and the row does not claim to know which of the three it is. What works is the same either way: the head term (drop the city or the qualifier — physical therapy instead of
    physical therapy carlsbad
    ) or a larger nearby city. Every empty row names the head term to try, and none of them is charged.

None of the three is a defect in this actor. All three are how Keyword Planner works, and you are getting exactly what an advertiser sees in the Google Ads UI.


The 30-day cache

Google refreshes these volumes monthly, so a keyword looked up twice in the same month returns the same numbers. We cache every lookup for 30 days and serve repeats instantly — same data, no waiting. Rows served that way are marked servedFromCache: true. That cache is shared across everyone who uses this actor, so it saves the lookup, not the row: a keyword someone else looked up is still your first one. A run answered entirely from the cache also pays no fresh-lookup fee: the fee exists to cover a fresh lookup, and a cached run makes none.

You are never charged twice for the same keyword

Ask for a keyword you already got from us inside those 30 days and the same Google Ads figures are handed straight back, with nothing charged for them. No lookup is bought and no keyword-result event is charged — the check happens before either. So re-running a list, adding ten keywords to a list of a hundred, or resuming a stopped run costs you only what is genuinely new.

Those rows carry repeat: true, charged: false, firstSeenAt (when you first got them) and firstSeenRunId (which run), and statusReason says the same in words.

Past 30 days Google has refreshed its figures, so the same keyword is new data and a new charge — that is the same window the cache above uses, and the same reason.

This memory is yours: it lives in your own account, in a key-value store called kw-volume-account on your Storage tab. Delete it to start over and be charged again. If it cannot be read on a given run, the run still delivers — it charges as it always did and the status line says the check was unavailable, so you know that run could have billed a repeat. Because the memory is a key-value store in your own Apify account, a run started with a scoped API token in restricted-access mode needs key-value store Read, Write and Create permission (or Actor runs set to Full access) under Settings → API & Integrations (Write is what lets the run remember what it delivered; without it every run pays again); without it the run says so on its status line and in one uncharged row, and it cannot skip what you already paid for.

Run it on a schedule — monthly is the cadence that pays

Google refreshes these volumes monthly, so a monthly schedule is the one that brings back numbers you do not already have. Run the same list weekly and nothing is charged for the keywords — inside the 30 days every row comes back repeat: true with the same figures handed straight back — but that run also tells you nothing new. Leave a little over 30 days between runs and each one fetches a fresh month; a run that lands inside the window simply hands back what you already had, uncharged, and says so on every row.

To set it up: save your input as a Task, put the Task on an Apify Schedule, and add an integration or a webhook on run succeeded — a Google Sheet, Slack, n8n, Make, or your own endpoint. Growing a tracked list stays cheap: only the keywords your account has not had in the last 30 days are looked up and charged. Chart by dataAsOf, the last month Google itself reported — that is the field that tells you whether a run brought a new month of data or handed back the month you already had.


What you are never charged for

situationmissReasonrun status
Google publishes no volume for that exact phraseNO_DATASUCCEEDED — avgMonthlySearches: null, and the row names the head term to try
A keyword Google Ads will not accept (over 80 characters, over 10 words, emoji)INVALID_KEYWORDSUCCEEDED — the row names the rule
The source rate-limited usTHROTTLEDSUCCEEDED — re-runnable, and the row says so
The source was unavailable this run, or answered and returned nothing for that keywordPROVIDER_UNAVAILABLESUCCEEDED — re-runnable, and the row says which of the two happened
We could not reach the source at allPROVIDER_ACCOUNTSUCCEEDED — this one is on us, never on you
A language we could not placeUSER_INPUTSUCCEEDED — one row per keyword, uncharged, naming the field
A country we could not placeSUCCEEDED — the run uses the default market (United States) and an uncharged first row names both the value you sent and the market used
A country value longer than any country name, or a whole-region ask (worldwide, EU)USER_INPUTSUCCEEDED — one row per keyword, uncharged, naming the value and the values that work
A country with no data at the source (Russia, Iran, Cuba…)USER_INPUTSUCCEEDED — uncharged, names the country and the nearest markets that do have data
A network or mode we did not recogniseUSER_INPUTSUCCEEDED — the default runs, an uncharged row names the field
Other input we could not read (wrong type)USER_INPUTSUCCEEDED — the row names the fix
Your own maxItems / maxRunSeconds / cost capSTOPPED_AT_LIMITSUCCEEDED — summary row + resumeCursor
The note naming the settings a filters-only Start ran the sample underSUCCEEDED — the sample rows themselves are charged
A keyword your account already had in the last 30 daysSUCCEEDED — repeat: true, the same figures handed back, nothing charged
A run answered entirely from the 30-day cacheno fresh-lookup fee
A fresh lookup that returned no data for any keywordNO_DATA on every rowno fresh-lookup fee, no keyword charges
A cost cap too low to buy one fresh lookup plus one keywordSTOPPED_AT_LIMITSUCCEEDED — cached keywords still delivered, summary row names the fix
A keyword needing fresh data past the Apify free plan's daily lookup allowanceFREE_PLAN_FRESH_CAPSUCCEEDED — cached keywords still delivered and charged, the rest named on uncharged rows and in resumeCursor

We never guess. A rate limit is reported as a rate limit, not as "this keyword has no volume" — a temporary problem is never dressed up as a permanent verdict.

This actor may fail when the platform changes things — failed items are never charged.


Limits are hard limits

maxItems, maxIdeasPerSeed, maxIdeaSeeds and maxRunSeconds stop the run exactly where you set them. The run still finishes successfully, the summary row names which limit bound, and resumeCursor lists the keywords that were not reached — so you continue from there instead of paying to re-look-up what you already have.

Each also has a range of its own: up to 2,000 ideas per keyword, 20 keywords expanded in one lookup, 100,000 rows for the whole run, and a run window between 30 seconds and one hour. Ask outside one of them and the run still starts: it uses the nearest end of the range and writes one uncharged row naming what you asked for and what bound it.

maxIdeasPerSeed is the run's idea ceiling divided by the keywords you expand: three keywords at 200 is up to 600 ideas for the run. Because the expansion is one lookup over all of them, the ceiling is filled from the top of a single list sorted by search volume — so a keyword with few suggestions no longer strands its share, and you get at least as many ideas as before, never fewer.

A five-keyword run costs five keyword rows plus one $0.19 fresh-lookup fee if those keywords were not already in the cache; a run where nothing has data costs nothing. The run's Maximum cost per run (a run option) must cover one fresh lookup plus one keyword row — $0.25 is the minimum. Below it, keywords already in the cache are still delivered, no fresh lookup is bought, and the summary row says exactly what to raise.

And the cap belongs to the run, not to the attempt. If a run is moved to another server mid-way — or you resurrect it from the console — it continues from where it stopped: no row you already have is delivered again, no keyword is looked up twice, and no keyword is charged twice. maxItems still means what it said.

Fresh lookups can be paused on our side for maintenance. Then a run still delivers the keywords already in the 30-day cache, charged as usual, and ships one uncharged row for the rest — nothing is charged for a keyword that was not looked up, and resumeCursor lists them.

On the Apify free plan this actor buys at most 2 fresh lookups per account per day: keywords already in the 30-day cache are still delivered and charged as usual, and keywords that need fresh data come back the next day, or on any paid Apify plan.


FAQ

How much does 1,000 keywords cost? From $2.00 on the volume tier — $0.008 per keyword on the Apify free plan, falling with your Apify plan. That is 1,000 rows of the keyword-result event plus one $0.19 keyword-lookup fee if the run bought fresh data — no per-keyword add-ons, no metrics add-on. Keywords with no data are not rows you pay for.

Scheduling it? Monthly is the cadence that brings new numbers — see Run it on a schedule above.

What is the fresh-lookup fee, and when is it waived? Our licensed source bills every fresh lookup as one batch, however many keywords are in it, so a three-keyword list used to cost us more than it earned. A run pays $0.19 once when it buys fresh data and delivers at least one keyword row. It is waived when the whole run is answered from our 30-day cache, and when the fresh lookup returns no data at all. It is never charged per keyword, per task or per re-run of a resumed run.

Is this real Google data or an estimate? Real. It is the Keyword Planner figure an advertiser sees in Google Ads, delivered through a licensed data provider on our own credentials — you never supply a Google Ads login. isEstimated is false on every row and there is no "AI search volume" field here.

Why do two of my keywords have identical numbers? Google groups similar keywords under one combined figure. See the section above.

Do duplicates cost twice? No. SEO Tools, seo tools and seo tools are one keyword, charged once, echoed back with the first spelling you used. The same rule holds across the whole run: in ideas or both mode, a suggestion that repeats a keyword already delivered — including your own seed echoed back as one of its ideas — is skipped and never charged a second time.

Can I get keyword ideas instead of metrics? Set mode to ideas (suggestions only) or both. Your keywords are expanded together in one lookup, which is why the ideas cost the same whether you send one keyword or twenty. Google returns the suggestions as a single list and does not say which of your keywords produced which idea, so seed is null on an idea row and seededFrom names the keywords it was expanded from. Nothing is guessed.

Which countries and languages work? All 244 markets the search-volume source carries — every country, plus the territories and SARs it reports separately, including Hong Kong, Taiwan, Macao, Puerto Rico, Kosovo, Palestine, Gibraltar and Greenland. Countries take a two-letter code, a three-letter code, the English name or the Google Ads location code, with the common variants: US, USA, America, GB, UK, United Kingdom, AE, UAE, 2840. Languages take a code, a BCP-47 tag or the English name — en, eng, en-US, English. Case, spaces, hyphens, underscores and accents do not matter. A locale tag in the Country box is read for its market — en-GB is the United Kingdom, pt-BR is Brazil — and a comma list runs on the first country it names, so Dubai, United Arab Emirates and germany, france both work, with an uncharged first row saying which market the run used.

Six countries have no search-volume data at the source at any price — Russia, Belarus, Iran, Cuba, North Korea and the Åland Islands. Ask for one and the run says exactly that, names the country and points at the nearest markets that do have data, so you are not left guessing at spellings. Ask for something region-wide like worldwide or EU and it says search volume is reported one country at a time.

Any other value we cannot place runs on the default market, United States, rather than returning you nothing — and never silently: an uncharged first row names both the value you sent and the market the run used, the run page says the same, and every delivered row carries that market in its own country column. A bare number that is not a country code is a Google Ads target inside a country (a city, a region, a postal code); the row says so and tells you to send the country instead. The two values still refused outright are a Country box longer than any country name — a pasted block, not a market — and a whole-region ask; there every keyword you sent comes back as its own uncharged row naming the value and the spellings that work. The run still succeeds either way. The network and mode fields work the other way round — they only widen or narrow an answer about the keywords and market you did name, so an unrecognised value there runs on the documented default and says so on an uncharged row, and your keywords are still looked up.

Does {} cost anything? Two keyword rows at your plan's price — under two cents — plus the fresh-lookup fee only if the two sample keywords are not already in our cache (they usually are). With no keywords it runs the default sample — two everyday keywords for the United States — charged like any run, so you see the exact output shape on real Keyword Planner data. The status line says it was the sample and how to run your own list.

I picked a country but pasted no keywords. What happens? The same sample runs, in the country you picked: what you set is kept, what you did not set takes the sample's own value, and a limit larger than the sample's own is capped at it so a sample can never grow into a bill you did not ask for. One uncharged note row names the settings it used. A field name this actor does not recognise, and a country or language nobody can place, still get a single uncharged row asking for what to fix instead.


One actor per surface, one job each, the same contract everywhere: all-inclusive pay per event, charged only on delivery — so a workflow built for one of them ports to the next. (This actor is the one with a fresh-lookup fee because its data comes from a licensed source that bills per lookup; the others have no per-run fee.)

What you wantActor
Interest over time, related queries, regions, compareGoogle Trends Scraper — Google Trends Data, Interest Over Time
What is trending right now, by countryGoogle Trends Trending Now Scraper — Live Trending Searches
Rising and Breakout queries, with the real growth numberTrending Keywords Scraper — Google Trends Rising Queries
Autocomplete suggestions across 5 enginesGoogle Autocomplete Scraper — Google Suggest, 5 Keyword Engines
Monthly search volume and CPC for a keyword listthis actor
What is trending on social platformsTikTok Trends Scraper — X, Pinterest, YouTube & Google Trends

The rest of the steadyfetch shelf — same contract everywhere: all-inclusive pay per event, charged only on delivery, and no per-run fee on any of them.

FamilyActors
Ad creative intelligenceFacebook · Google Ads video · TikTok · LinkedIn · Google Ads text & OCR
Trends & keywordsGoogle Trends · Trends Now · Breakout keywords · Autocomplete keywords · Keyword volume & CPC · Social trends
YouTube transcriptsYouTube videos · YouTube channels
InstagramReel transcripts · Profile posts
JobsIndeed · Career sites by domain · Glassdoor · Multi-board · Google Jobs
AmazonProducts · Search · Bestsellers · Sellers
Any media fileSpeech to Text · any link or file

Free templates

Free n8n workflow templates for this actor are listed on our profile website (link on our Apify profile).

Feedback & support

Found an issue? Open it on the Issues tab — we usually reply within a couple of hours, always within a day.

If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.