SEO Data Bench/Guides/serp-keyword-clustering-by-route
Does SERP keyword clustering depend on the SERP API? 50 keywords, six runs, measured
The rule people give Claude Code with a SERP API: group keywords whose top 10 results share 5 or more URLs. Applied to the same 50 keywords through nine SERP routes across six runs between 15 September and 15 October 2026, how many keyword pairs each route groups, how far the groups move between runs half an hour apart, and what 1,000 keywords cost per route.
Written for people asking: How do I cluster keywords by SERP overlap with the DataForSEO API in Claude Code?Do SERP-based keyword clusters change depending on which SERP API you use?How much does SERP-based keyword clustering cost per 1,000 keywords?
Since late September 2026 it depends heavily on the route and on the run. The same rule (two keywords are one intent when 5 or more of their top-10 URLs match) was applied to the same 50 keywords through Google SERP routes on six runs between 15 September and 15 October 2026. On 15 September Cloro, DataForSEO, SerpApi and Treg's routed endpoint each linked 10 or 11 keyword pairs, and 10 pairs were linked by all four. On every run since 28 September no pair has been linked by every live route. DataForSEO's live endpoint linked between 1 and 4 pairs per run, and no two of those runs had more than a quarter of their links in common; on 28 September it grouped 'backlinks api' with 'backlink api' (7 shared URLs) and half an hour later did not (0). Treg's routed endpoint, served by ScrapeCreators on this account since at least 24 September, linked 11 to 14 pairs on every run, and any two of those runs had 67% to 93% of their links in common. A stable grouping is not the same as a correct one: nothing here shows which list a person searching would see. Before trusting a clustering run, check that the route groups an obvious plural pair, and run it twice.
The rule comes from a post on X that gives Claude Code the DataForSEO MCP and a Search Console query export: “pull the top 10 for every query (SERP API, standard queue), group keywords wherever 5+ of the 10 URLs match, that’s ONE intent” (@ecomtryggvi, 13 October 2026). The rule is sound and widely used. What it assumes is that the top 10 for a keyword is one list. On 28 September this site measured that it no longer was: the same query came back as two or three different result lists depending on the route and the call. This page measures what that does to the clusters.
What was run
Cohort v1 is 50 US-English buyer queries for data APIs, frozen on 15 September 2026 and listed on the dataset page. Their top-10 organic results were fetched on six runs: 15 September, twice on 28 September (about 30 minutes apart, the second time through four more routes), 5 October, 12 October and 15 October 2026. That is 1,700 stored responses. Fifteen October was a fresh run for this page: 250 calls through the five original routes, all of which returned data, for $1.14 off the Treg balance.
The scorer applies the rule above with no provider call. It normalises each URL, links two keywords when their top 10 share at least 5 URLs, and treats each connected set as one group. It then compares the links between routes on the same run and between runs on the same route. The thresholds of 3 and 4 shared URLs are in the tables as well; they move the counts up but do not change the pattern.
How many pairs each route grouped
Keyword pairs linked at 5 or more shared URLs out of 10, per route and run. Serpstat answers from a stored index and holds only 20 of the 50 keywords.
| Route | 15 Sep | 28 Sep, first | 28 Sep, +30 min | 5 Oct | 12 Oct | 15 Oct |
|---|---|---|---|---|---|---|
| Cloro | 11 | 2 | 0 | 0 | 1 | 10 |
| DataForSEO (live) | 11 | 4 | 2 | 4 | 1 | 1 |
| SerpApi | 10 | 0 | 0 | 0 | 1 | 7 |
| Treg (routed) | 11 | 14 | 13 | 13 | 14 | 11 |
| Serpstat (stored, 20 keywords) | 4 | 4 | 4 | 4 | 4 | 4 |
| Linked by every live route that run | 10 | 0 | 0 | 0 | 0 | 0 |
The first run on 28 September also went through four more routes: ScrapeCreators linked 15 pairs, LimaData 14, Serper 14 and AnyAPI 1. Across all eight live routes that run, 23 pairs were linked by at least one route, 7 by only one, and none by all eight.
Treg’s routed endpoint was served by DataForSEO on 15 September and by ScrapeCreators on every run since. Its column after 15 September is ScrapeCreators’ result list: on 28 September its links matched direct ScrapeCreators calls at 0.93, the share of links in common out of all links either found.
The same route, half an hour later
Four keyword pairs that any clusterer should group, with the number of top-10 URLs each pair shared. Five or more means grouped.
| Pair | Route | 15 Sep | 28 Sep | +30 min | 5 Oct | 12 Oct | 15 Oct |
|---|---|---|---|---|---|---|---|
| backlinks api / backlink api | Cloro | 7 | 7 | 0 | 1 | 1 | 7 |
| DataForSEO | 7 | 7 | 0 | 1 | 2 | 0 | |
| Treg (routed) | 7 | 7 | 5 | 4 | 4 | 7 | |
| keyword research api / keyword search volume api | Cloro | 6 | 0 | 1 | 0 | 0 | 6 |
| DataForSEO | 6 | 0 | 0 | 6 | 1 | 0 | |
| Treg (routed) | 6 | 7 | 7 | 7 | 7 | 7 |
The other two pairs, “clay alternatives” with “clay alternatives for developers” and “meta ad library api” with “ad library api”, follow the same pattern; all four pairs for every route and run are in example-pairs.csv.
A singular and its plural sharing no result is not a change in intent. The likelier reading, given the 28 September depth check (one DataForSEO query returned two disjoint lists minutes apart), is that the two keywords were served from different lists. A clusterer cannot tell that apart from two intents, so it splits the group.
Measured as links in common with the same route’s previous run, at 5 shared URLs:
- DataForSEO: between 0 and 0.25 on every pair of runs since 28 September, including 0.20 between the two runs 30 minutes apart.
- Cloro and SerpApi: no link in common between any two runs from 28 September to 12 October. On 15 October both moved back towards 15 September: Cloro’s links matched its 15 September links at 0.75, SerpApi’s at 0.31. Their result lists still differed from each other’s (URL agreement 0.65) and from DataForSEO’s (0.31 and 0.24), so this is not the split ending.
- Treg’s routed endpoint (ScrapeCreators): between 0.67 and 0.93 on every pair of runs since 28 September.
- Serpstat: 1.00 on every pair, because a stored index does not re-fetch. It also holds fewer than half the keywords.
What 1,000 keywords cost per route
One top-10 fetch per keyword, at the rates measured on 28 September 2026 in the nine-route run:
| Route | Per 1,000 keywords | Basis |
|---|---|---|
| Serper | $1.00 | settled charge on the account ledger |
| ScrapeCreators | $1.88 | catalog list price for one credit |
| DataForSEO (live) | $2.00 | cost field in each response |
| Cloro | $2.00 | settled charge on the account ledger |
| SerpApi | $15.00 | catalog list price |
| Treg (routed) | $0 charged | served on a ScrapeCreators key registered to the account, one credit a call |
| Serpstat (stored index) | about $9.60 per 1,000 answered | billed per result row; answered 20 of these 50 keywords |
The post names DataForSEO’s standard queue rather than the live endpoint used here. Google organic task-post is not listed in the Treg catalog, so its price and its lists were not measured; whether queued results split the same way is open. Checking a clustering run by fetching twice doubles every figure above.
Who this fits
- You cluster keywords from a SERP API in Claude Code or a script. Put one obvious plural pair from your own list (or “backlinks api” / “backlink api”) into the batch. If the run does not group it, the route served split lists that day, and the groups are not yet usable.
- You need groups you can repeat. Fetch the whole list twice, cluster both, and keep only the groups that appear in both runs. On DataForSEO in October that kept almost nothing; on the ScrapeCreators-served route it kept most groups.
- You use a routed endpoint. Record
_treg.served_bywith every fetch. The stable column above belongs to ScrapeCreators, not to routing; on another account, or another week, the routed call may be served by another provider. Pin one provider for a clustering job so every keyword in it comes from the same source. - You want groups that never move. A stored index gives you that, for the keywords it holds: 20 of these 50, on data that does not refresh at request time.
What this run cannot tell you
Which grouping is right. The route with stable groups returns one of the lists Google is serving, and this run has no evidence on which list a person searching from the United States sees. It cannot say why the lists split or why Cloro and SerpApi moved back on 15 October. It is 50 keywords in one niche, in US English on desktop, with six runs, most of them a week apart. The 5-of-10 rule is the one from the post; tools that use hard clustering (every pair in a group must match) or a different threshold will produce different counts. The runs of 15 September to 12 October were made for other benchmarks on this site and reused here; only the 15 October run was made for this page.
Reproduce it
treg auth login
scripts/run-seo-cohort.py evidence/fixtures/seo-cohort-v1.json evidence/serp-runs/$(date +%F) --tasks serp
scripts/score-serp-clusters.py evidence/fixtures/seo-cohort-v1.json out/ 2026-09-15=public/evidence/seo-runs/2026-09-15 today=evidence/serp-runs/$(date +%F)
- Tables as CSV: pairs per route and run, groups at 5 shared URLs, route and run comparisons, example pairs, and the full summary. The tables hold keywords and counts, not result URLs.
- Raw responses for 15 and 28 September are on the evidence page; the scorer reads any directory of them.
- Scripts: cohort runner, clustering scorer, which imports the URL parsing of the SERP-check scorer.
Next step
- Run the scorer on two fetches of your own keyword list before acting on the groups. To pin a provider on Treg’s routed endpoint, the route headers are in the docs: treg.to/docs.
- Why the lists split, and what each route returned on 28 September: SERP APIs after Google’s scraper crackdown. Feeding a Search Console export to Claude Code in the first place: Search Console and keywords in Claude Code.
Questions this page answers
How do I cluster keywords by SERP overlap with the DataForSEO API in Claude Code?
Fetch the top 10 organic results for every keyword, link two keywords when 5 or more URLs match, and treat each connected group as one intent; the scorer used here does exactly that on stored responses. On 50 keywords in October 2026 DataForSEO's live endpoint linked only 1 to 4 pairs per run, against 10 or 11 on 15 September, because the same keyword now comes back as different result lists from call to call. Run the clustering twice and keep only groups that appear both times.
Do SERP-based keyword clusters change depending on which SERP API you use?
Yes, since late September 2026. On the same 50 keywords on 28 September, eight live routes linked between 0 and 15 pairs at 5 of 10 shared URLs and no pair was linked by all eight; on 15 September four routes had agreed on 10 pairs. Routes that return the same list as each other (Treg's routed endpoint, ScrapeCreators and LimaData) also grouped alike.
How much does SERP-based keyword clustering cost per 1,000 keywords?
One live top-10 fetch per keyword: about $1 at Serper, $1.88 in ScrapeCreators credits, $2 at DataForSEO's live endpoint or Cloro, and $15 at SerpApi, at the rates measured on 28 September 2026. Treg's routed endpoint charged $0 on this account because it was served on a ScrapeCreators key registered to the account, one credit a call. Running the clustering twice to check it doubles each figure.
Relationship: the people who run this site also work on Treg, one of the routes compared on this page. Every Treg figure here is a call we paid for, with the raw response downloadable, and the same fixture is run against the alternatives where their terms allow.
Spotted an error or a changed price? Email corrections@seodatabench.com with the page URL and a source. Corrections are logged on the corrections page.