APIVoid logo

API Errors & Retries

Successful requests always return HTTP 200. Any other status code is an error, and the JSON body tells you what went wrong. Your credits are never decreased in the event of an error.

Error format

Error responses contain a single error field with a human-readable message:

json
{
   "error": "API key is not valid"
}

4xx — client errors

4xx status codes indicate a problem with the request: an invalid API key, a missing required parameter, an unknown parameter, a malformed value, or rate limiting. Check the error message, fix the request, and try again.

Only retry 429 (rate limited) automatically. All other 4xx errors will keep failing until the request itself is fixed; retrying them wastes requests, can get you rate limited, and won't fix the problem. See Rate Limits for why 429 happens, the Retry-After header, and how to size your concurrency to avoid it.

5xx — server errors

5xx status codes indicate a problem on the APIVoid side. Only 500, 502, 503 and 504 should be retried; treat other 5xx codes as final for that request and check the service status page if they persist.

Retry with incremental backoff

When retrying (a 429, or a 500/502/503/504), wait progressively longer between attempts and cap the number of attempts. The examples below wait 1, 5, 15, 30 and then 60 seconds: the two longer waits let your code ride out a brief maintenance window (up to about a minute of downtime) and still come back with a successful response instead of giving up. Each example uses no third-party dependencies, and also retries transient connection errors and timeouts. On a 429 the response carries a Retry-After header, which is a floor rather than an estimate, so the examples wait for whichever is longer, the header or the current backoff step; that way a retry never arrives before the limit has actually cleared:

python
import json
import socket
import time
import urllib.request
import urllib.error

MAX_ATTEMPTS = 6  # Initial attempt + one retry per delay
DELAYS = [1, 5, 15, 30, 60]  # Seconds before each retry
RETRYABLE = {429, 500, 502, 503, 504}
TIMEOUT = 180  # Seconds; covers even the slowest APIs (up to 150s)

def call_apivoid(payload: dict) -> dict:
    request = urllib.request.Request(
        "https://api.apivoid.com/v2/ip-reputation",
        data=json.dumps(payload).encode("utf-8"),
        headers={
            "Content-Type": "application/json",
            "X-API-Key": "YOUR_API_KEY_HERE",
        },
        method="POST",
    )
    for attempt in range(MAX_ATTEMPTS):
        retry_after = 0
        try:
            with urllib.request.urlopen(request, timeout=TIMEOUT) as response:
                return json.loads(response.read().decode("utf-8"))  # Success (200)
        except urllib.error.HTTPError as error:
            # The API responded with a non-200 status code
            if error.code not in RETRYABLE:
                raise  # Not retryable: fix the request instead
            retry_after = int(error.headers.get("Retry-After", 0) or 0)  # Sent on 429
        except (urllib.error.URLError, socket.timeout):  # socket.timeout is TimeoutError on Python 3.10+
            # Connection error (DNS failure, refused, reset) or timeout: retry
            pass
        if attempt < MAX_ATTEMPTS - 1:
            time.sleep(max(retry_after, DELAYS[attempt]))  # Never retry before Retry-After
    raise RuntimeError("Request failed after all retry attempts")

result = call_apivoid({"ip": "80.82.77.139"})
print(result)
php
$maxAttempts = 6; // Initial attempt + one retry per delay
$delays = [1, 5, 15, 30, 60]; // Seconds before each retry
$retryable = [429, 500, 502, 503, 504];

function callApiVoid(array $payload): array
{
    $curl = curl_init('https://api.apivoid.com/v2/ip-reputation');
    curl_setopt($curl, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'X-API-Key: YOUR_API_KEY_HERE',
    ]);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_TIMEOUT, 180); // Seconds; covers even the slowest APIs (up to 150s)
    $retryAfter = 0;
    curl_setopt($curl, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$retryAfter) {
        if (stripos($header, 'Retry-After:') === 0) { // Sent on 429
            $retryAfter = (int) trim(substr($header, 12));
        }
        return strlen($header);
    });
    $body = curl_exec($curl);
    // http_code is 0 on connection error (DNS failure, refused, reset) or timeout
    $httpCode = ($body === false) ? 0 : curl_getinfo($curl, CURLINFO_HTTP_CODE);
    curl_close($curl);
    return ['http_code' => $httpCode, 'body' => ($body === false) ? null : json_decode($body, true), 'retry_after' => $retryAfter];
}

$result = null;
for ($attempt = 0; $attempt < $maxAttempts; $attempt++) {
    $response = callApiVoid(['ip' => '80.82.77.139']);
    if ($response['http_code'] === 200) {
        $result = $response['body'];
        break; // Success (200)
    }
    if ($response['http_code'] !== 0 && !in_array($response['http_code'], $retryable, true)) {
        // Not retryable: fix the request instead
        throw new RuntimeException('API error ' . $response['http_code'] . ': ' . ($response['body']['error'] ?? 'unknown'));
    }
    // Connection error, timeout or retryable status code: retry
    if ($attempt < $maxAttempts - 1) {
        sleep(max($response['retry_after'], $delays[$attempt])); // Never retry before Retry-After
    }
}
if ($result === null) {
    throw new RuntimeException('Request failed after all retry attempts');
}
print_r($result);
node.js
// Requires Node.js 18+ (uses the built-in fetch)
const MAX_ATTEMPTS = 6; // Initial attempt + one retry per delay
const DELAYS = [1000, 5000, 15000, 30000, 60000]; // Milliseconds before each retry
const RETRYABLE = new Set([429, 500, 502, 503, 504]);
const TIMEOUT = 180000; // Milliseconds; covers even the slowest APIs (up to 150s)

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function callApiVoid(payload) {
    for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
        let retryAfter = 0;
        try {
            const response = await fetch("https://api.apivoid.com/v2/ip-reputation", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "X-API-Key": "YOUR_API_KEY_HERE",
                },
                body: JSON.stringify(payload),
                signal: AbortSignal.timeout(TIMEOUT),
            });
            if (response.ok) {
                return await response.json(); // Success (200)
            }
            if (!RETRYABLE.has(response.status)) {
                const { error } = await response.json();
                throw new Error(`API error ${response.status}: ${error}`); // Not retryable: fix the request instead
            }
            retryAfter = (parseInt(response.headers.get("retry-after") ?? "0", 10) || 0) * 1000; // Sent on 429
        } catch (error) {
            if (error instanceof Error && error.message.startsWith("API error")) {
                throw error; // Non-retryable API error from above
            }
            // Connection error (DNS failure, refused, reset) or timeout: retry
        }
        if (attempt < MAX_ATTEMPTS - 1) {
            await sleep(Math.max(retryAfter, DELAYS[attempt])); // Never retry before Retry-After
        }
    }
    throw new Error("Request failed after all retry attempts");
}

callApiVoid({ ip: "80.82.77.139" }).then(console.log).catch(console.error);
go
package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

const maxAttempts = 6 // Initial attempt + one retry per delay

var delays = []time.Duration{ // Wait before each retry
	1 * time.Second, 5 * time.Second, 15 * time.Second, 30 * time.Second, 60 * time.Second,
}
var retryable = map[int]bool{429: true, 500: true, 502: true, 503: true, 504: true}

// Timeout covers even the slowest APIs (up to 150s)
var client = &http.Client{Timeout: 180 * time.Second}

func callApiVoid(payload map[string]any) (map[string]any, error) {
	body, err := json.Marshal(payload)
	if err != nil {
		return nil, err
	}
	for attempt := 0; attempt < maxAttempts; attempt++ {
		retryAfter := time.Duration(0)
		req, err := http.NewRequest("POST", "https://api.apivoid.com/v2/ip-reputation", bytes.NewReader(body))
		if err != nil {
			return nil, err
		}
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("X-API-Key", "YOUR_API_KEY_HERE")

		resp, err := client.Do(req)
		if err == nil { // The API responded; check the status code
			respBody, readErr := io.ReadAll(resp.Body)
			resp.Body.Close()
			if resp.StatusCode == http.StatusOK && readErr == nil {
				var result map[string]any
				if err := json.Unmarshal(respBody, &result); err == nil {
					return result, nil // Success (200)
				}
			} else if !retryable[resp.StatusCode] {
				// Not retryable: fix the request instead
				return nil, fmt.Errorf("API error %d: %s", resp.StatusCode, respBody)
			}
			if n, convErr := strconv.Atoi(resp.Header.Get("Retry-After")); convErr == nil { // Sent on 429
				retryAfter = time.Duration(n) * time.Second
			}
		}
		// Connection error, timeout or retryable status code: retry
		if attempt < maxAttempts-1 {
			wait := delays[attempt]
			if retryAfter > wait { // Never retry before Retry-After
				wait = retryAfter
			}
			time.Sleep(wait)
		}
	}
	return nil, errors.New("request failed after all retry attempts")
}

func main() {
	result, err := callApiVoid(map[string]any{"ip": "80.82.77.139"})
	if err != nil {
		panic(err)
	}
	fmt.Println(result)
}
java
// Requires Java 11+ (uses the built-in java.net.http.HttpClient)
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Set;

public class Main {
    static final int MAX_ATTEMPTS = 6; // Initial attempt + one retry per delay
    static final int[] DELAYS = {1, 5, 15, 30, 60}; // Seconds before each retry
    static final Set<Integer> RETRYABLE = Set.of(429, 500, 502, 503, 504);
    static final Duration TIMEOUT = Duration.ofSeconds(180); // Covers even the slowest APIs (up to 150s)
    static final HttpClient CLIENT = HttpClient.newHttpClient();

    static String callApiVoid(String payload) throws InterruptedException {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://api.apivoid.com/v2/ip-reputation"))
                .header("Content-Type", "application/json")
                .header("X-API-Key", "YOUR_API_KEY_HERE")
                .timeout(TIMEOUT)
                .POST(HttpRequest.BodyPublishers.ofString(payload))
                .build();
        for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
            long retryAfter = 0;
            try {
                HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
                if (response.statusCode() == 200) {
                    return response.body(); // Success (200)
                }
                if (!RETRYABLE.contains(response.statusCode())) {
                    // Not retryable: fix the request instead
                    throw new RuntimeException("API error " + response.statusCode() + ": " + response.body());
                }
                retryAfter = response.headers().firstValueAsLong("Retry-After").orElse(0); // Sent on 429
            } catch (IOException error) {
                // Connection error (DNS failure, refused, reset) or timeout: retry
            }
            if (attempt < MAX_ATTEMPTS - 1) {
                Thread.sleep(Math.max(retryAfter, DELAYS[attempt]) * 1000L); // Never retry before Retry-After
            }
        }
        throw new RuntimeException("Request failed after all retry attempts");
    }

    public static void main(String[] args) throws InterruptedException {
        String result = callApiVoid("{\"ip\": \"80.82.77.139\"}");
        System.out.println(result);
    }
}

These examples are reactive: they send a request, and back off when the API pushes back. That is the right shape for occasional or user-facing calls, where one request has to succeed and there is nothing to pace. For batch work the better approach is proactive: avoid the 429 in the first place by reading X-RateLimit-Remaining and X-RateLimit-Reset-After and pacing against them, and by sizing your worker pool from X-Concurrency-Limit.

See Rate Limits for worked examples of that pattern.

Choosing a request timeout

The examples above set the request timeout to 180 seconds, which safely covers every APIVoid API, including the slowest ones (up to 150 seconds, e.g. the Screenshot API). A timeout is an upper bound, not a wait: fast responses still come back immediately. See the Request Timeouts page for average and maximum response times per API, suggested timeouts for each, and what the timeout applies to in each language. Never use a short timeout (e.g. 30 seconds) with the slower APIs: you would abort requests the API was about to answer, and each aborted request is retried from zero.

Quick reference

StatusMeaningRetry?
200SuccessNo
4xxClient error (fix the request, see error message)No, except 429
429Rate limitedYes, with backoff
500 / 502 / 503 / 504Server error (see error message)Yes, with backoff
Other 5xxServer errorNo