Skip to main content
The Go SDK separates two kinds of failure: errors returned before a request is even sent (bad configuration, invalid parameters, malformed URLs), and unsuccessful HTTP responses that still come back as a valid *Response. Automatic retries only apply to the second kind.

Basic usage

Retries are off by default (maxRetryCount is 0). Pass WithMaxRetryCount to turn them on:
client := scraperapi.NewClient(
    scraperapi.WithAPIKey("YOUR_ZENROWS_API_KEY"),
    scraperapi.WithMaxRetryCount(3),
)

response, err := client.Get(context.Background(), "https://www.scrapingcourse.com/ecommerce/", nil)
With WithMaxRetryCount(3), a failed request is retried up to 3 additional times before the SDK returns the last response or error it received.

When to enable retries

Enable retries (2 to 3 is a reasonable starting point) whenever you’re running requests at scale or in production, anywhere a transient 429 or 500 shouldn’t fail your whole job. Leave retries off (the default) for quick one-off scripts or debugging, where you’d rather see the raw failure immediately.

Which failures get retried

The SDK retries a request when either of these is true:
  • The request failed at the network level (DNS failure, connection reset, timeout), regardless of status code.
  • The response came back with one of these status codes:
Status codeMeaning
422Unprocessable Entity
429Too Many Requests
500Internal Server Error
502, 503, and 504 are not retried automatically by the Go SDK. If your use case needs to retry those too, check response.StatusCode() yourself and re-issue the request in a loop.
Between attempts, the SDK waits using WithRetryWaitTime as a starting point (default 5 seconds), doubling on each subsequent attempt, capped at WithRetryMaxWaitTime (default 30 seconds):
client := scraperapi.NewClient(
    scraperapi.WithAPIKey("YOUR_ZENROWS_API_KEY"),
    scraperapi.WithMaxRetryCount(5),
    scraperapi.WithRetryWaitTime(20*time.Second),
    scraperapi.WithRetryMaxWaitTime(25*time.Second),
)
You aren’t charged for retried attempts that fail. Only successful responses count toward your usage. See the Retry Failed Requests guide for more on how this works.

Handling failures

Even after retries are exhausted, the SDK returns a *Response with err == nil. It doesn’t turn a non-2xx status into a Go error on its own. Always check the response before trusting its content:
response, err := client.Get(context.Background(), url, nil)
if err != nil {
    log.Fatal(err)
}

if response.IsSuccess() {
    fmt.Println(response.String())
} else {
    fmt.Println("Failed after retries:", response.StatusCode())
    if prob := response.Problem(); prob != nil {
        fmt.Println("Detail:", prob.Detail)
    }
}

The four SDK error types

Error typeWhen it happens
NotConfiguredErrorNo API key was set (neither WithAPIKey nor ZENROWS_API_KEY).
InvalidHTTPMethodErrorYou called Scrape with a method other than GET, POST, or PUT.
InvalidTargetURLErrorThe target URL is empty or fails to parse; wraps the underlying url.Parse error via Unwrap().
InvalidParameterErrorA RequestParameters field fails validation, for example, ScreenshotQuality outside 1 to 100, or Screenshot: false combined with ScreenshotFullPage: true.
All four are returned as values (not pointers), and InvalidTargetURLError supports errors.Unwrap so you can inspect the underlying parse error with errors.As or errors.Is.

Troubleshooting

  • Response still fails after retries are exhausted: Check response.StatusCode() and look it up in API Error Codes. Some failures, like an invalid API key, aren’t fixed by retrying.
  • Getting InvalidParameterError for a parameter you didn’t set: Several RequestParameters fields depend on each other, for example, ScreenshotFullPage requires Screenshot: true, and WaitForSelector requires JSRender: true. Check Validate()’s rules in the SDK source if the message isn’t specific enough.
  • 429 errors even with retries enabled: You’re likely exceeding your plan’s concurrency limit, not hitting a one-off rate limit. See Concurrency.
  • Retries seem to take a long time: That’s the doubling backoff by design, capped at WithRetryMaxWaitTime. Lower WithMaxRetryCount or WithRetryMaxWaitTime if latency matters more than success rate.

Pricing

Retried attempts that fail are not billed. Only the final successful response counts:
ConfigurationCost multiplier vs. basic
Basic request1x
JSRender: true5x
UsePremiumProxies: true10x
JSRender: true + UsePremiumProxies: true25x
404 and 410 responses count as successful and are billed, since the request itself completed correctly.

FAQ

Zero. Retries are disabled unless you explicitly pass WithMaxRetryCount(n) with n > 0.
422, 429, and 500, plus any network-level failure. 502, 503, and 504 are not retried automatically.
No. A non-2xx HTTP response is still a successful round trip from the SDK’s point of view, it returns a *Response with err == nil. Check response.IsError() or response.StatusCode() yourself.

Look up a specific error code

For a full breakdown of ZenRows error codes (like AUTH002 or RESP001) and how to resolve each one, see the API Error Codes reference.