Tools Reference

discover_keywords

Discover keywords to target, from DataForSEO Labs — the question that comes before pricing a list you already have. Pick a mode: "ideas" (keywords in the…

Cost: 40 credits.

discover_keywords asks DataForSEO Labs to produce keywords you do not have yet — from one seed keyword, a list of seeds, or a domain. It answers the question that comes before research_keywords, which prices a list you already wrote: this one hands you rows you did not type. It is synchronous — everything comes back immediately, with no background job to poll.

What it does

Pick a mode. It is required and has no default, because the four modes ask DataForSEO four different questions — and take four different inputs:

modeWhat comes backWhat you pass
ideasKeywords from the same product/service categories as your seedsseeds — a list of keywords
suggestionsLonger search queries that contain your seedseed — exactly one keyword
relatedThe keywords Google lists under "searches related to" your seedseed, plus optional depth
for_siteKeywords DataForSEO considers relevant to a domain — no seed involvedtarget or project_id

Each row carries DataForSEO's own search_volume, cpc, competition, competition_level, keyword_difficulty, search intent, search-volume trend and the date the vendor last refreshed the row.

Three filters — min_volume, max_volume and max_difficulty — are applied at DataForSEO rather than after the fact. min_volume and max_difficulty are never sent unless you ask for them. max_volume is the one exception: on for_site and ideas it has a default ceiling, for the reason in the next section, and every answer says which ceiling it used.

for_site and ideas leave relevance to DataForSEO — and it measured poorly

Two of the four modes do not start from a keyword you typed. for_site asks DataForSEO which keywords belong to a domain; ideas asks which belong to a category. Both answers are the vendor's judgement alone, and on a live walkthrough both came back off-subject: for_site returned none of its first 15 keywords about the site — national general-purpose queries (weather, translation, government services) of the kind any domain in that country is handed — and ideas returned unrelated products and topics at ordinary search volume.

So those two modes carry a warning above their results, and a default search-volume ceiling. The answer always names the ceiling it applied; pass max_volume to move it, or max_volume: 0 to remove it and see the unfiltered set.

What that ceiling is, and is not. It is a bound SeoGrep chose — not a measured relevance threshold. What the walkthrough measured was that the whole first window, ordered by search volume, was off-subject; the volume of those rows was never captured, so no number here is derived from them. The bound is set where it is on the reasoning that a single site rarely owns a keyword above it, and it drops whatever sits above it regardless of what that keyword is about.

It is therefore a partial remedy and is described as one: a volume bound cannot remove an off-subject keyword of ordinary volume, which is exactly what ideas returned. SeoGrep does not read meaning and will not filter rows by it.

The ceiling also stands down when it would contradict you: give for_site or ideas a min_volume at or above it and the default is dropped rather than sent alongside your floor, because the two together match nothing and you would have paid the flat price for an empty list. The answer says the ceiling withdrew. Two bounds of your own that contradict each other (min_volume above max_volume) are refused outright, before anything is charged — there SeoGrep has no default of its own to give up, and picking one of your bounds to ignore would run a lookup you did not ask for.

suggestions and related are left alone — no warning, no ceiling. They stay anchored to a seed keyword you choose (the first returns queries that contain it, the second what Google itself lists beside it), and both measured clean on the same walkthrough. If a for_site or ideas answer reads as noise, those two are the narrower question.

How long a seed may be

A seed keyword is capped at 200 charactersseed on suggestions and related, and every entry of seeds on ideas. That is SeoGrep's bound, not DataForSEO's: the vendor publishes none we have read, and the longest keyword we have ever seen come back from it is 29 characters.

It exists because the answer quotes your seeds back in its heading, and one enormous seed would crowd out the keywords you paid for: measured, a 60,000-character seed produced a reply too large for any client to show, carrying zero keywords. Two hundred characters is about thirty ordinary words — far more than a real search query — and a longer one is refused before anything is charged.

A long seed list is handled differently, because there the cap would lose information: ideas takes up to 200 seeds and the heading quotes as many as it can, then says how many more you sent. The count is always exact.

A field from another mode is rejected, not ignored

Pass seed with mode: "for_site" and the call is refused — it is not quietly dropped. That is deliberate: for_site looks up a domain, so a silently ignored seed would run a different lookup than the one you asked for, and bill you for it. The error names the field and says what the mode does take, so it is a one-step fix.

The same rule runs in both directions: ideas will not accept a domain, depth belongs to related alone, and include_subdomains to for_site alone.

Whose numbers these are

Every value in the output is a DataForSEO field printed under DataForSEO's own name. The vendor publishes two different competition measurements and they stay apart: competition is a 0–1 float, competition_level is an advertiser band (LOW / MEDIUM / HIGH). SeoGrep does not merge them, derive one from the other, or rename either into something friendlier.

A field the vendor did not report is printed as unreported, never as 0 — "nobody has a figure for this" and "nobody searches this" are different facts. A genuine zero the vendor did send is printed as 0. Secondary intent is the one exception in the other direction: an empty list cannot be told apart from a field the vendor never sent, so an empty one is printed as nothing at all rather than as "no secondary intent".

What it does not tell you

There is no opportunity score here, no "easy win" label and no ordering of ours: the rows come back in the one vendor order the tool asks for — by DataForSEO's own keyword_info.search_volume, highest first — and the output names that field so you know what "first" means.

About these search volumes: they are Google Keyword Planner figures, passed through from DataForSEO unchanged. Each one is a 12-MONTH AVERAGE for the keyword AND ITS CLOSE VARIANTS, and Google ROUNDS it — so a volume here is not a count of exact-match searches in the month just gone, two keywords can share a figure only because both were rounded onto it, and volumes added up across rows or across locations will not come out exact. Because the figures are rounded, many rows land on the SAME volume: sorting by it groups the list into bands rather than ranking it, and the order of rows sharing one figure carries no meaning — do not read a row above another as the stronger keyword when both print the same volume.

keyword_difficulty is DataForSEO's own 0–100 estimate about the search results for a keyword. It is not a forecast of where your site would rank, not a promise that a low number is winnable, and neither DataForSEO nor SeoGrep can tell you what traffic any of these keywords would bring you. Which of them to target is your decision; this tool brings you the vendor's rows and says whose they are.

Example

Ask your MCP client in plain language:

Find longer search queries containing "seo software".

Or start from a domain instead of a keyword:

What keywords does DataForSEO consider relevant to example.com? Skip anything under 500 monthly searches.

Input

FieldTypeRequiredDescription
modestringYesWHICH QUESTION to ask — required, with no default, because the four modes answer four different questions and take four different inputs. "ideas": keywords from the same product/service categories as your seed keywords (takes "seeds"). "suggestions": longer search queries that CONTAIN your seed keyword (takes "seed"). "related": the keywords Google lists under "searches related to" for your seed (takes "seed", and optionally "depth"). "for_site": keywords DataForSEO considers relevant to a DOMAIN, with no seed keyword involved (takes "target" or "project_id"). Passing a field that belongs to another mode is rejected, not ignored.
seedsstring[]NoMODE "ideas" ONLY: the seed keywords to draw ideas from (1-200, the vendor's own documented ceiling), each at most 200 characters — SeoGrep's bound, not DataForSEO's, because the answer quotes your seeds back and one enormous seed would crowd out the keywords you paid for. The keywords that come back are the vendor's, not yours — none of your seeds is guaranteed to appear in the answer.
seedstringNoMODES "suggestions" and "related" ONLY: exactly one seed keyword, at most 200 characters (SeoGrep's bound, not DataForSEO's — see "seeds"). Both endpoints take a single keyword, not a list — pass one, and run the tool again for another.
depthintegerNoMODE "related" ONLY: how many times DataForSEO follows "searches related to" outward from your seed (0-4, the vendor's own range; default 1 when omitted). A deeper search drifts further from the seed; it does not change the price.
targetstringNoThe domain to discover keywords for, e.g. "example.com" or "https://example.com" — any public domain, including a competitor's. Pass this OR project_id, not both.
project_idstring (uuid)NoOne of your projects (from setup_project / list_projects) — the domain is taken from it. Pass this OR target, not both.
include_subdomainsbooleanNoMODE "for_site" ONLY: whether the domain's subdomains count as part of it (default true). Sent to DataForSEO explicitly either way, so the answer never depends on a vendor default that could move.
limitintegerNoHow many keywords to return (1-1000, default 100). It does not change what YOU pay: this call costs 40 credits whether you ask for one keyword or 1000, and asking for fewer rows costs the same. It does move DataForSEO's own bill, unlike SeoGrep's backlink tools where the row count barely shifts it: the Labs tariff is a flat fee per request plus a fee per row, and the per-row half catches the flat half at 100 rows and is ten times it at 1000. That is what fixes the ceiling — the flat credit price was signed against a full-width request — and it is not a reason to ask for less than you need.
offsetintegerNoHow many keywords to skip before this window starts (default 0). Page through a large set by advancing it; the output always states the offset and limit the rows came back under.
min_volumeintegerNoOPTIONAL vendor filter: keep only keywords whose DataForSEO keyword_info.search_volume is at least this. Omitted by default — no filter is sent at all, so nothing is dropped on your behalf. Filtering happens at DataForSEO, so it changes which rows you are billed for.
max_volumeintegerNoOPTIONAL upper bound on DataForSEO keyword_info.search_volume — the one filter here that has a DEFAULT. On modes "for_site" and "ideas" a ceiling of 100000 monthly searches is applied when you pass nothing, because those two modes ask DataForSEO to judge relevance and were measured returning national general-purpose queries that had nothing to do with the subject. The answer always states which ceiling was applied. Pass your own number to move it, or 0 to remove it entirely. Modes "suggestions" and "related" get NO default ceiling — a number here still applies to them if you want one. Filtering happens at DataForSEO, so it changes which rows you are billed for.
max_difficultyintegerNoOPTIONAL vendor filter: keep only keywords whose DataForSEO keyword_properties.keyword_difficulty is at most this (0-100, the vendor's own scale). Omitted by default. The cut-off is YOURS: DataForSEO publishes the score but no threshold, and a low score is not a promise that a keyword is winnable.
language_codestringNoLanguage code (default 'en').
location_codeintegerNoDataForSEO location code (default 2840 = United States).

Returns

A heading naming which mode ran and the DataForSEO Labs function behind it, then — on for_site and ideas only — the relevance warning, then what that mode means in the vendor's own terms, then the locale, the vendor field the rows are ordered by, the filters that were actually sent (printed in DataForSEO's own [field, operator, value] grammar, so what you read is what the vendor received) and a plain sentence naming which search-volume ceiling applied — a default one, your own, or none — and the argument that changes it.

The keyword list is captioned as a window: the rows you got, the offset and limit they were fetched under, and DataForSEO's whole-set count attributed to the vendor by name, followed by the sentence that stops the arithmetic — this window is a slice of that set, not a count of it. When the vendor gave no total, the caption says that instead of back-filling one from the rows in hand.

A lookup that matched nothing says so plainly, naming the window and the filters it asked for, and you are still charged for the delivered lookup — "nothing here matched" is a real answer, not an error. A missing or foreign mode field, a limit or depth outside the allowed range, a for_site call naming neither target nor project_id (or both), and a project_id that is not yours are all rejected before anything is charged; while live data is off you get a "not yet enabled" message instead — also free.

Billing

One call is one flat price, charged once, and behind it is one DataForSEO request. If it fails, the whole call fails and you are not charged.

discover_keywords needs a paid credit balance. It reads live data from a paid third-party provider, so it is not available on trial credits; the refusal arrives before anything is reserved and says outright that you were not charged. Buy any credit pack and it unlocks straight away; your existing credits are untouched and keep working for crawls, audits, reports and Search Console tools.

The limit ceiling is part of the price rather than a display preference: DataForSEO bills per returned row, and that cap is what holds the flat price inside the margin it was signed against. Asking for fewer rows costs the same; asking for more than the ceiling is refused before anything is charged.

A wide reply is bounded, and it says so when it is. A keyword row costs about 300 characters, so a full-width 1,000-row lookup would render close to 300,000 characters — several times the size a calling client refuses outright, and a refused reply means the credits are spent and you see an error instead of an answer. So the reply has a size budget.

The default window is not affected. Ask for no limit at all and every one of the 100 keywords prints — the budget is set above the widest default answer on purpose, because a call you did not tune should return a whole answer. A wider window is where the budget bites: a 1,000-row lookup prints roughly 120–130 keywords. When rows are cut, the reply says how many keywords were shown and how many more were fetched in the same window but not printed, and states plainly that those were charged for either way. Raising limit past what one reply carries buys rows nobody can show you: advance offset to read the next stretch — a separate call at the same flat price — or narrow the set with min_volume, max_volume or max_difficulty so the keywords you want arrive inside the window that prints.

Limitations

Every delivered lookup is recorded: SeoGrep keeps a row saying what was looked up, when, under which settings, and a capped summary of what came back. The Lookups page of your dashboard lists them, so a lookup you paid for an hour ago is still something you can point at.

That record is history, not a live surface. No call here reads a previous run, nothing is refreshed for you, and there is no "new since last time" — so to see the current picture, run it again.

The keywords are the vendor's, not yours: none of your seeds is guaranteed to appear in the answer, and a set of many thousands is normal — read the window caption for how far your slice sits from DataForSEO's whole-set count, and page through it with offset.

for_site is DataForSEO's own judgement about which keywords are relevant to a domain. It is not a list of what that site currently ranks for — that is ranked_keywords — and the two will not agree. related follows "searches related to" outward from your seed, so a deeper depth drifts further from it.