To compare SERP API latency, run the same request set in the same environment and record the full result of every call. A vendor table cannot tell you how a specific query, country, response type, network path, or retry policy will behave in your application. Measure elapsed time, HTTP status, the API response code, and the request identifier when one is returned.
This guide uses SERPpost V1 as an example. It does not rank providers or promise a response time. The point is to create a test you can rerun when your query mix, release, or usage changes.
Start with a fixed test plan
Write down the inputs before you collect a timing. Keep the search engine, country, language, page, depth, client timeout, and cache setting fixed for a run. If you compare Google and Bing, report them as separate groups rather than averaging them together.
Use queries that resemble the work your application will actually do. A short list of representative requests is more useful than one carefully chosen keyword.
| Record for each run | Why it matters |
|---|---|
| Query and engine | Different queries and result types can return different result shapes. |
| Country and language | A location or language change creates a different search request. |
| Request parameters | Page, depth, cache, and timeout settings affect what you asked the API to do. |
| Client environment | Record the region and runtime that made the request. |
| Time of the run | It makes a later comparison easier to interpret. |
Time one request and keep the response evidence
SERPpost V1 search requests use POST /api/v1/search. The example below records the client-observed duration. It treats an HTTP success and an application-level code as separate checks.
import os
import time
import requests
API_URL = "https://serppost.com/api/v1/search"
def measure_search(query: str) -> dict:
api_key = os.environ.get("SERPPOST_API_KEY", "").strip()
if not api_key:
raise ValueError("Set SERPPOST_API_KEY before running the benchmark.")
payload = {
"s": query,
"t": "google",
"p": 1,
"d": 20000,
"country": "us",
"language": "en",
"maxCache": 0,
}
result = {
"query": query,
"elapsed_ms": None,
"http_status": None,
"api_code": None,
"result_id": None,
"error_type": None,
}
started = time.perf_counter()
try:
response = requests.post(
API_URL,
headers={"Authorization": f"Bearer {api_key}"},
json=payload,
timeout=30,
)
except requests.RequestException as exc:
result["elapsed_ms"] = round((time.perf_counter() - started) * 1000, 1)
result["error_type"] = type(exc).__name__
if exc.response is not None:
result["http_status"] = exc.response.status_code
return result
result["elapsed_ms"] = round((time.perf_counter() - started) * 1000, 1)
result["http_status"] = response.status_code
try:
body = response.json()
except ValueError:
result["error_type"] = "NonJSONResponse"
return result
if not isinstance(body, dict):
result["error_type"] = "UnexpectedJSONShape"
return result
data = body.get("data") if isinstance(body.get("data"), dict) else {}
result["api_code"] = body.get("code")
result["result_id"] = data.get("id")
return result
if __name__ == "__main__":
print(measure_search("web to markdown api"))
The client timeout and the d value are limits for the request. They are not latency results. Keep error records in the same output as successful requests so a faster-looking sample does not hide failures or timeouts.
Set SERPPOST_API_KEY before running the example. A missing key stops the test with a configuration error. A timeout or connection failure returns a record with error_type; the HTTP status is None when no response is available. Non-JSON responses and unexpected JSON shapes are recorded separately. An empty error_type is not a success verdict: still check the HTTP status and API code. The example does not retry requests or log credentials.
Example checked on September 10, 2026 with simulated responses and network failures. These checks verify the error-handling paths, not production latency.
Compare like with like
Do not put unrelated workloads in one average. A search request, a public-page capture, and a file extraction can have different inputs and outputs. Split your results by the work each request performs.
For each group, calculate a summary from the records you collected. Keep the raw rows as well. The raw rows show whether a change in the average came from a different query, an error, or a real change in the observed request times.
| Question | Evidence to keep |
|---|---|
| How long did successful requests take? | The individual client-observed durations and the input settings. |
| How often did the application return an error code? | HTTP status, response code, and the time of each result. |
| Did a configuration change help? | A before-and-after run with the same request set. |
| Can another developer reproduce the test? | Code version, environment, date, and the exact payload fields. |
Test errors separately from latency
An error is not a slow success. Record it as its own outcome. If your workflow retries requests, record the first attempt and the final outcome. Otherwise a retry can make a later request look slow without showing why.
Use application behavior, not a guessed provider threshold, to decide what your code does next. For example, log the HTTP status and the V1 response code, then decide whether your own application should stop, surface the result, or retry according to its requirements.
Check concurrency with Request Slots
Request Slots are the number of live requests an account can run at once. They affect how many requests you can have in flight; they do not promise a faster individual response. Keep a separate concurrency test, raise load gradually, and preserve the same request set so the result is interpretable.
The current account value is visible in the Dashboard. Use that value and the current plan terms for your own capacity test instead of copying a stale plan table into a benchmark.
Benchmark source capture as a separate workflow
If your application follows search with public-page capture, measure that stage separately. SERPpost Web Reader uses POST /api/v1/url and can return Markdown, metadata, and HTML for a public page. A source-capture run should record the selected URL, reader settings, HTTP status, response code, and the fields your application needs.
That separation makes a bottleneck easier to find. It also keeps a search benchmark from making a claim about a different type of request.
Decide with a rerunnable test, not a static winner
Use the results to answer a narrow question: can this request pattern meet the behavior your application needs today? Re-run the same test when you change providers, regions, query mix, release versions, or error handling.
For a cost model that uses the same request inventory, see How to compare SERP API costs. For current request parameters, use the SERPpost docs and test a representative input in the API Playground.
FAQ
Is one request enough to compare SERP API latency?
No. One request can show that an integration works, but it cannot represent a workload. Use a small fixed set of queries and keep the inputs, environment, and error records for every result.
Should I compare a Search request and Reader capture in the same average?
No. They have different inputs and output expectations. Measure the search request and the source-capture request as separate stages, then decide whether the combined workflow fits your application.
Do more Request Slots make one request faster?
No individual-response-time claim follows from Request Slots. They describe how many live requests an account can run at once. Test concurrency separately from per-request timing.
Where can I confirm the current request fields?
The SERPpost docs list the current V1 request fields. The API Playground lets you inspect a live example before you add it to a benchmark.