Google returns ~10 organic results per page. To collect up to 100, you page through results with the page parameter. Here’s how to do it efficiently with serp.cheap.
The basics
pageis 1-indexed (1= first page).- Each page returns ~10 organic results.
- Each page is a separate billed request — so 100 results ≈ 10 requests.
A single page is one POST. Page 2, for example:
curl -X POST https://api.serp.cheap/v1/search \
-H "X-API-Key: $SERP_KEY" \
-H "Content-Type: application/json" \
-d '{"q":"best project management software","gl":"us","page":2}'
Python: collect up to 100 results
Pages can occasionally overlap by a result or two, so dedupe by link as you merge:
import os
import requests
API_KEY = os.environ["SERP_KEY"] # export SERP_KEY=your_key
def search(q, gl="us", page=1):
r = requests.post(
"https://api.serp.cheap/v1/search",
headers={"X-API-Key": API_KEY},
json={"q": q, "gl": gl, "page": page},
timeout=30,
)
r.raise_for_status()
return r.json()
def fetch_results(q, gl="us", max_results=100):
out, seen, page = [], set(), 1
while len(out) < max_results:
batch = search(q, gl=gl, page=page).get("organic", [])
if not batch:
break # no more results
for item in batch:
if item["link"] not in seen: # pages can occasionally overlap
seen.add(item["link"])
out.append(item)
page += 1
return out[:max_results]
results = fetch_results("best project management software")
print(len(results), "results")
Node.js
Same pattern: one search() helper, and the pagination loop calls it.
const API_KEY = process.env.SERP_KEY; // export SERP_KEY=your_key
async function search(q, { gl = "us", page = 1 } = {}) {
const res = await fetch("https://api.serp.cheap/v1/search", {
method: "POST",
headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ q, gl, page }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
return res.json();
}
async function fetchResults(q, max = 100) {
const out = [];
const seen = new Set();
for (let page = 1; out.length < max; page++) {
const { organic = [] } = await search(q, { page });
if (!organic.length) break; // no more results
for (const item of organic) {
if (!seen.has(item.link)) { // dedupe across pages
seen.add(item.link);
out.push(item);
}
}
}
return out.slice(0, max);
}
const results = await fetchResults("best project management software");
console.log(results.length, "results");
Example output
Each item in organic[] carries position, title, link, snippet (plus date and sitelinks when Google shows them). Positions restart at 1 on every page — so a page-2 response also starts at position: 1. If you need a global rank, compute (page - 1) * 10 + position when you merge:
[
{
"position": 1,
"title": "12 Best Project Management Software Tools Compared",
"link": "https://example.com/project-management-tools",
"snippet": "We compared the most popular project management tools on pricing, core features, and team size..."
},
{
"position": 2,
"title": "Project Management Software - Wikipedia",
"link": "https://en.wikipedia.org/wiki/Project_management_software",
"snippet": "Project management software are computer programs that help plan, organize, and manage resources..."
}
]
Tips
- Dedupe by link when you merge pages — the code above does this with a
seenset, since occasional overlap can occur. - Stop early when a page returns no results.
- Fetch lazily — don’t pull 100 results if you only show 10. Cost scales with pages.
Next
Try serp.cheap
Page through real Google results without per-page surprises — every page is one flat-priced request. 100 free searches on signup — no card required; pay-as-you-go from $0.75/1k after that, and credits stay valid for 12 months.
- Get your free API key →
- Prefer paying by card? serp.cheap is also on RapidAPI (
$0.90/1k) and Apify ($1.50/1k).