Errors
Error codes and retry guidance
Errors are never charged. Credits are deducted for searches that return results, and for searches whose query genuinely has no results (confirmed across multiple search sources). An empty response we could not confirm as genuinely empty is never charged; see When are empty results charged?. Your organization's own repeats of the same request within 5 minutes are free.
HTTP Status Codes
| Status | Meaning | Retryable |
|---|---|---|
| 400 | Bad request | No |
| 401 | Invalid or missing API key | No |
| 402 | Insufficient credits | No |
| 429 | Rate limit exceeded | Yes |
| 500 | Internal server error | Yes |
| 502 | Extraction service error (not charged) | Yes |
| 503 | Temporarily unavailable | Yes |
| 504 | Extract timed out (not charged) | Yes |
400 Bad Request
Returned when the request is malformed or missing required parameters.
Common causes:
- Missing
qparameter on the Search endpoint - Query exceeds 500 characters
include_contentis nottrueorfalse, orcontent_resultsis not5or10- Missing
urls, more than 10 URLs, or a URL that cannot be parsed on the Extract endpoint dayson the Usage endpoint is not a whole number of at least 1
{
"error": "Query parameter (q or query) is required",
"example": "/api/search?q=javascript"
}An Extract request with URLs that cannot be parsed lists them:
{
"error": "Invalid URLs provided",
"invalid_urls": ["not a url"]
}401 Unauthorized
Returned when the API key is missing, invalid, or inactive.
{
"error": "Invalid or inactive API key"
}When no key is sent at all, the message is "API key required".
Check that you are sending Authorization: Bearer sk_... and that the key is active in your dashboard.
402 Insufficient Credits
Returned when your account does not have enough credits to cover the request cost.
{
"error": "Insufficient credits. Please purchase more credits to continue.",
"remainingCredits": 0
}On the Extract endpoint the message is "Insufficient credits". Extract checks your balance against the maximum cost of the request (every URL charged) before it runs, and then charges only the successful URLs.
The remainingCredits field shows your current balance. Top up credits from the billing page.
429 Rate Limited
Returned when you exceed your plan's request-per-second limit.
{
"error": "Rate limit exceeded",
"limit": 50,
"retryAfterMs": 800
}Response headers include:
X-RateLimit-Limit— your plan's per-second ceilingX-RateLimit-Remaining— requests remaining in the current windowRetry-After— seconds to wait before retrying
Retry guidance: Wait for retryAfterMs milliseconds, then retry the request. Use exponential backoff if you are consistently hitting the limit.
| Plan tier | Rate limit |
|---|---|
| Starter (largest purchase under $50) | 5 req/sec |
| Standard ($50–$499) | 25 req/sec |
| Scale ($500+) | 50 req/sec |
| Enterprise (contact sales) | Custom |
500 Internal Server Error
Returned by Search and Extract when something unexpected fails on our side. The message is always the same and never includes internal details. The request is not charged.
{
"error": "Internal server error",
"request_id": "3f6c1a52-8d0e-4b7a-9c41-0e2d5a7b9f13"
}Quote the request_id when you contact support@serpex.dev so we can find the exact request.
502 Bad Gateway
Returned by the Extract endpoint when the extraction service fails. Nothing is charged.
{
"error": "Extraction service error. Not charged. Please retry.",
"retryable": true,
"request_id": "3f6c1a52-8d0e-4b7a-9c41-0e2d5a7b9f13"
}Retry guidance: Retry the request. If it keeps failing, contact support with the request_id.
Request ID
Error responses from Search and Extract that point to a problem on our side (500, 502) include a request_id field. Quote it to support.
If an Extract request is rejected because of its urls, the 400 body says which rule it broke:
{ "error": "URLs array is required" }{ "error": "Maximum 10 URLs allowed per request" }{
"error": "Invalid URLs provided",
"invalid_urls": ["not-a-url"]
}503 Service Unavailable
Returned when the request cannot be completed right now. It is never charged and is safe to retry.
Search returns this when the search could not be completed after automatic retries:
{
"status": "error",
"error": "Search temporarily unavailable, please retry.",
"retryable": true,
"metadata": {
"status": "error",
"response_time": 30412,
"timestamp": "2025-06-21T10:30:00.000Z"
}
}Retry guidance: Wait at least 1 second, then retry. Use exponential backoff with a maximum of 3 retries. If the issue persists beyond a few minutes, check the status page.
504 Gateway Timeout
Returned by the Extract endpoint when extraction does not finish in time. Nothing is charged.
{
"error": "Extraction timed out upstream and was not charged. Retry, or split the request into fewer URLs.",
"retryable": true
}Retry guidance: Retry the request, or send fewer URLs per request.
Error Handling Example
import { SerpexClient, SerpApiException } from "serpex";
const client = new SerpexClient("sk_your_api_key");
try {
const results = await client.search({ q: "test query" });
} catch (error) {
if (error instanceof SerpApiException) {
switch (error.statusCode) {
case 429:
console.log(`Rate limited. Retry after ${error.details?.retryAfterMs}ms`);
break;
case 402:
console.log(`Out of credits. Balance: ${error.details?.remainingCredits}`);
break;
default:
console.log(`API error ${error.statusCode}: ${error.message}`);
}
}
}from serpex import SerpexClient, SerpApiException
client = SerpexClient("sk_your_api_key")
try:
results = client.search({"q": "test query"})
except SerpApiException as e:
if e.status_code == 429:
print(f"Rate limited. Retry after {e.details.get('retryAfterMs')}ms")
elif e.status_code == 402:
print(f"Out of credits. Balance: {e.details.get('remainingCredits')}")
else:
print(f"API error {e.status_code}: {e}")