Pagination
Serply passes Google's own pagination parameters straight through, so paging
is offset-based: you ask for a page size with num and a starting offset with
start. There are no cursors and no pagination envelope in the response.
Both parameters go inside the path-packed query string, alongside q:
curl --header 'X-Api-Key: YOUR_API_KEY' \
'https://api.serply.io/v1/search/q=coffee&num=10&start=0'
Parameters
num
The number of results to return. Honored exactly for small values - num=5
returns 5 results.
Note that Google serves roughly 10 organic results per page, and Serply does
not stitch pages together for you. Asking for more than that does not produce
more: num=20, num=50, and num=100 all come back with about 10 results,
the same as num=10. To collect 50 results you must make five requests at
increasing offsets, not one request with num=50.
start
The zero-based offset into the result set. Omit it (or pass 0) for the first
page, 10 for the second, and so on.
# first page
'https://api.serply.io/v1/search/q=coffee&num=10&start=0'
# second page
'https://api.serply.io/v1/search/q=coffee&num=10&start=10'
The response has no pagination metadata
Search responses contain no pagination object, no cursor, and no usable
result count. The total field is present on some endpoints but is null for
Google Search, so you cannot use it to compute a page count in advance.
This means there is no way to know how many pages exist before you request them. You page until you stop getting results.
Detecting the last page
When you page past the end of the result set, the API returns 200 OK with an
empty results array:
{
"results": [],
"answers": [],
"related_searches": { "text": [] }
}
An empty results array is not proof you have reached the end. The same
response appears during transient upstream failures, which cluster with 502
responses. The two are indistinguishable from a single request.
To tell them apart, retry once or twice before concluding the page is empty. A genuine end-of-results is stable across retries; a transient failure is not.
Collecting multiple pages
A loop that stops on a confirmed-empty page:
import time
import requests
HEADERS = {'X-Api-Key': 'YOUR_API_KEY'}
BASE = 'https://api.serply.io/v1/search'
def fetch_page(query, start, num=10, retries=2):
"""Return one page. Retries so a transient blip isn't read as the end."""
for attempt in range(retries + 1):
r = requests.get(
f'{BASE}/q={query}&num={num}&start={start}',
headers=HEADERS,
timeout=15,
)
r.raise_for_status()
results = r.json().get('results', [])
if results:
return results
if attempt < retries:
time.sleep(1)
return []
def search_all(query, max_results=50, num=10):
collected, seen = [], set()
for start in range(0, max_results, num):
page = fetch_page(query, start, num)
if not page:
break # confirmed empty after retries - end of results
for item in page:
if item['link'] not in seen:
seen.add(item['link'])
collected.append(item)
return collected[:max_results]
Two details worth keeping:
Deduplicate by link. Adjacent pages occasionally repeat a result, because
Google reshuffles slightly between requests. In testing, four pages of 9
results yielded 34 unique links rather than 36.
Pin X-Proxy-Location across the whole loop. If you omit it, individual
requests may be served from different countries, and a page fetched from a
German proxy will share almost nothing with one fetched from a US proxy. That
inflates your unique-result count with what looks like new data but is really a
different regional SERP:
HEADERS = {
'X-Api-Key': 'YOUR_API_KEY',
'X-Proxy-Location': 'US',
}
Setting it once for every request in the loop keeps all pages on the same result set. The Webhooks guide has the measurements.
Always set an upper bound. Since there is no total to check against, a loop
without a max_results ceiling will keep paging - and keep billing - on any
query where the empty-page signal is delayed.
Billing
Each page is a separate request, so each page costs one credit. Paging to 50 results costs 5 credits, not 1.
Cached responses are free, and most endpoints cache for several minutes, so re-running the same paged query while developing does not bill again. See Pricing for credit rates.
Other endpoints
num and start follow Google's query-string conventions and apply to the
Google Search endpoint. Other resources use their own parameters - Reddit
listings take limit and an after token, and Google Maps takes num after a
?. Check the relevant page under API Endpoints before assuming
start applies.