Error Codes and Troubleshooting
All API errors return an applicable HTTP status code along with this JSON body:
{
"success": false,
"error": "Human-readable error description"
}
TLDR
| Code | Meaning | Your action |
|---|---|---|
| 200 | Success | Use the data |
| 400 | Bad Request | Fix parameters — check the error text |
| 401 | Unauthorized | Check API key header/parameter |
| 403 | Forbidden | The key is deactivated — contact support |
| 404 | Not Found | Verify the endpoint path |
| 429 | Rate Limited | Wait for Retry-After, then retry; upgrade if persistent |
| 500 | Server Error | Retry once after a delay |
| 501 | Not Implemented | /stream/events — poll instead |
| 503 | Service Unavailable | Wait and retry once |
Types of errors
Non-standard response format
An empty or non-JSON body means a transient network or server issue. Treat it as a 500: retry once after a short delay; contact support if it repeats.
400 Bad Request
Meaning: invalid or missing parameters. Check the reference; booleans must be true/false; dates ISO-8601; don't repeat a parameter; some combinations (live=true with ended=true) can't go together.
401 Unauthorized
Meaning: authentication failed or the key is missing. Send it as x-api-key (case-insensitive) or apiKey; check for whitespace; make sure you copied the key you were shown.
403 Forbidden
Meaning: the key exists but is deactivated. Email support with the keyID.
404 Not Found
Meaning: the path doesn't exist. Make sure it starts with /v2/.
429 Too Many Requests
Meaning: you exceeded your per-minute limit. Wait for Retry-After seconds; check /account/usage. A rejected call is counted for the minute but never billed.
500 Internal Server Error
Meaning: something went wrong on our end. Retry once after a few seconds; if it persists contact support. Please don't retry in a tight loop.
501 Not Implemented
Meaning: you called /v2/stream/events. SockOdds does not stream; see the polling guide.
503 Service Unavailable
Meaning: temporarily unavailable. Wait and retry once.
Error handling example
async function fetchSockOdds(url, options = {}, canRetry = true) {
let response, payload, error;
try { response = await fetch(url, options); payload = await response.json(); } catch (e) { error = e; }
if (payload?.success === true) return payload.data;
const status = response?.status;
if (status === 429 && canRetry) {
await new Promise((r) => setTimeout(r, 1000 * Number(response.headers.get("retry-after") || 5)));
return fetchSockOdds(url, options, false);
}
const isClientError = payload?.success === false && status >= 400 && status < 500;
if (canRetry && !isClientError) {
await new Promise((r) => setTimeout(r, 2000 + Math.random() * 3000));
return fetchSockOdds(url, options, false);
}
console.error(`SockOdds request failed: ${status} ${payload?.error || error?.message}`);
return null;
}import random, time, requests
def fetch_sirenodds(url, headers, can_retry=True):
try:
r = requests.get(url, headers=headers); payload = r.json()
except Exception as e:
r, payload = None, None
if payload and payload.get("success") is True:
return payload["data"]
status = r.status_code if r is not None else None
if status == 429 and can_retry:
time.sleep(int(r.headers.get("Retry-After", "5"))); return fetch_sirenodds(url, headers, False)
client_error = payload and payload.get("success") is False and status and 400 <= status < 500
if can_retry and not client_error:
time.sleep(2 + random.random() * 3); return fetch_sirenodds(url, headers, False)
print("SockOdds request failed:", status, (payload or {}).get("error"))
return Nonestatic JsonElement fetchSockOdds(String url, String key, boolean canRetry) throws Exception {
var req = HttpRequest.newBuilder().uri(URI.create(url)).header("x-api-key", key).GET().build();
var res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
var payload = JsonParser.parseString(res.body()).getAsJsonObject();
if (payload.has("success") && payload.get("success").getAsBoolean()) return payload.get("data");
int status = res.statusCode();
if (status == 429 && canRetry) { Thread.sleep(1000L * Long.parseLong(res.headers().firstValue("retry-after").orElse("5"))); return fetchSockOdds(url, key, false); }
boolean clientError = status >= 400 && status < 500;
if (canRetry && !clientError) { Thread.sleep(2000 + new Random().nextInt(3000)); return fetchSockOdds(url, key, false); }
System.err.println("SockOdds request failed: " + status + " " + payload.get("error"));
return null;
}def fetch_sirenodds(url, key, can_retry: true)
uri = URI(url); req = Net::HTTP::Get.new(uri); req["x-api-key"] = key
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
payload = JSON.parse(res.body) rescue nil
return payload["data"] if payload&.dig("success") == true
status = res.code.to_i
if status == 429 && can_retry
sleep(res["retry-after"].to_i.nonzero? || 5); return fetch_sirenodds(url, key, can_retry: false)
end
client_error = payload&.dig("success") == false && status.between?(400, 499)
if can_retry && !client_error
sleep(2 + rand * 3); return fetch_sirenodds(url, key, can_retry: false)
end
warn "SockOdds request failed: #{status} #{payload&.dig('error')}"; nil
endfunction fetchSockOdds(string $url, string $key, bool $canRetry = true) {
$ch = curl_init($url); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["x-api-key: $key"], CURLOPT_TIMEOUT => 30]);
$body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
$payload = $body !== false ? json_decode($body, true) : null;
if (($payload['success'] ?? false) === true) return $payload['data'];
if ($status === 429 && $canRetry) { sleep(5); return fetchSockOdds($url, $key, false); }
$clientError = ($payload['success'] ?? null) === false && $status >= 400 && $status < 500;
if ($canRetry && !$clientError) { usleep((2 + lcg_value() * 3) * 1000000); return fetchSockOdds($url, $key, false); }
error_log("SockOdds request failed: $status " . ($payload['error'] ?? 'Unknown'));
return null;
}