Error Reference
All API errors return an applicable HTTP status code (400-level or 500-level) along with the following 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 validation errors |
| 401 | Unauthorized | Check API key header/parameter |
| 403 | Forbidden | Check subscription status or feature access |
| 404 | Not Found | Verify endpoint path |
| 429 | Rate Limited | Wait and then retry. Upgrade subscriptionif persistent |
| 500 | Server Error | Retry once after delay, simplify query if persistent |
| 503 | Service Unavailable | Wait for service to reconnect |
| 504 | Gateway Timeout | Reduce query complexity, retry once after delay |
Types of Errors
Non-Standard Response Format
In rare cases you may receive:
- An empty response body
- A non-JSON response body (ex: text or html)
- A response body which isn’t parseable into JSON
- A response body without a success field
Meaning: These are typically due to transient networking issues or server errors.
Recommendation:
- Treat these the same as a 500-level error.
- Retry the request once after a short delay
- If you see these repeatedly and the request still fails after a retry, please contact support.
400 Bad Request
Meaning: Your request has invalid or missing parameters.
Troubleshooting:
- Typically the
errorfield in the response will be helpful to determine what the issue is - Check the reference docs and the data-types docs to help ensure you’re using the correct values.
- Make sure you haven’t included the same parameter multiple times.
- If it’s a boolean parameter, make sure it’s either
trueorfalse(as a string) - If it’s a date parameter, make sure it’s a valid ISO-formatted date or Unix timestamp. Ex:
2026-01-01T00:00:00.000Zor1736899200000 - In some cases, you may be using an invalid combination of parameters. These are combinations of query parameters which can’t logically go together such as
live=truewithended=true.
401 Unauthorized
Meaning: Authentication failed or API key is missing.
Troubleshooting:
- Make sure you’ve included your API key either in the header as
x-api-key(case-insensitive) or as a query parameter asapiKey. - Ensure that the API key doesn’t contain any leading or trailing whitespace.
- Make sure you copied your API key correctly. Check your email and ensure you’re using the same key which was sent to you.
- Make sure your API key hasn’t been re-generated or revoked.
403 Forbidden
Meaning: Your API key doesn’t have permission to access this data
Troubleshooting:
- Make sure you haven’t cancelled your subscription. Check your subscription here.
- If you’re on a paid plan, make sure you didn’t have a failed payment/billing issue
- Make sure you’re using the latest API version. By default API keys are not granted access to old/legacy API versions.
- Certain endpoints may be restricted by tier. For example, the /stream/events endpoint is only available on the AllStar plan. Contact support to upgrade.
404 Not Found
Meaning: The requested resource doesn’t exist.
Troubleshooting:
- Make sure you’re using the correct URL to query the API. Check for typos.
- The version of your API request was invalid. Make sure your URL path starts with
/v2/or/v1/or another valid version.
429 Too Many Requests
Meaning: You’ve exceeded your rate limits
Troubleshooting:
- Try waiting up to a minute and then try again
- Check your rate limit usage using the
/account/usageendpoint - If you ran out of “objects per month” then you may need to upgrade your plan
500 Internal Server Error
Meaning: Something went wrong on our end.
Troubleshooting:
- Wait a few seconds and then retry the request once.
- If it still fails, then contact support
- Consider trying a different endpoint or different set of query parameters. Sometimes active problems are isolated to only specific cases of these
- In general, these errors are not your fault and should be exceedingly rare
- We ask that you do not continuously retry the request as this tends to overload our servers and may result in additional errors
503 Service Unavailable
Meaning: API is temporarily unavailable or our server is offline
Troubleshooting:
- Wait a few seconds and then retry the request once.
- If it still fails, then contact support
- In some cases, these types of errors are isolated to a single server instance. If that’s what’s happening, your retry may work. Otherwise, it’s likely a more widespread issue with our servers.
504 Gateway Timeout
Meaning: Request took too long to complete.
Troubleshooting:
- Your query may be too complex. The following can help reduce query complexity:
- Remove these parameters:
includeAltLines,startsBefore,startsAfter,playerID/playerIDs,bookmakerID/bookmakerIDs,teamID/teamIDs - Reduce the overall number of query parameters you’re using
- Remove these parameters:
- Our servers may be overloaded. Wait a few seconds and then retry the request once.
- If it still fails, then contact support
Error Handling Example
async function fetchSGO(url, options = {}, canRetry = true) {
let response, payload, error;
try {
response = await fetch(url, options);
payload = await response.json();
} catch (e) {
error = e;
}
const success = payload?.success;
const status = response?.status;
if (success === true) return payload.data;
const isClientError = success === false && status >= 400 && status < 500 && status !== 429;
if (canRetry && !isClientError) {
const backoffMs = 2000 + Math.floor(Math.random() * 3000);
await new Promise((resolve) => setTimeout(resolve, backoffMs));
return fetchSGO(url, options, false);
}
console.error(`SGO Request Failed: ${status} ${payload?.error || error?.message}`, { url, options, payload, error });
return null;
}import requests
import random
import time
def fetch_sgo(url, options=None, can_retry=True):
options = options or {}
response = None
payload = None
try:
response = requests.request(
method=options.get('method', 'GET'),
url=url,
headers=options.get('headers'),
json=options.get('body')
)
payload = response.json()
except Exception as e:
error = e
else:
error = None
if payload and payload.get('success') is True:
return payload.get('data')
status = response.status_code if response else None
is_client_error = (
payload and payload.get('success') is False
and status and 400 <= status < 500 and status != 429
)
if can_retry and not is_client_error:
time.sleep(2 + random.random() * 3)
return fetch_sgo(url, options, can_retry=False)
error_msg = (payload or {}).get('error') or (str(error) if error else 'Unknown')
print(f"SGO Request Failed: {status} {error_msg}")
return Noneimport java.net.URI;
import java.net.http.*;
import java.time.Duration;
import java.util.Map;
import java.util.Random;
import com.google.gson.*;
public class SgoClient {
private static final HttpClient client = HttpClient.newHttpClient();
private static final Gson gson = new Gson();
private static final Random random = new Random();
public static JsonElement fetchSgo(String url, Map<String, String> headers, String body, boolean canRetry) {
HttpResponse<String> response = null;
JsonObject payload = null;
Exception error = null;
try {
var builder = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(30));
if (headers != null) {
headers.forEach(builder::header);
}
if (body != null) {
builder.POST(HttpRequest.BodyPublishers.ofString(body));
}
response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString());
payload = gson.fromJson(response.body(), JsonObject.class);
} catch (Exception e) {
error = e;
}
if (payload != null && payload.has("success") && payload.get("success").getAsBoolean()) {
return payload.get("data");
}
int status = response != null ? response.statusCode() : 0;
boolean isClientError = payload != null
&& payload.has("success") && !payload.get("success").getAsBoolean()
&& status >= 400 && status < 500 && status != 429;
if (canRetry && !isClientError) {
try {
Thread.sleep(2000 + random.nextInt(3000));
} catch (InterruptedException ignored) {}
return fetchSgo(url, headers, body, false);
}
String errorMsg = payload != null && payload.has("error")
? payload.get("error").getAsString()
: (error != null ? error.getMessage() : "Unknown");
System.err.println("SGO Request Failed: " + status + " " + errorMsg);
return null;
}
}require 'net/http'
require 'json'
require 'uri'
def fetch_sgo(url, options = {}, can_retry: true)
response = nil
payload = nil
error = nil
begin
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Get.new(uri)
options[:headers]&.each { |k, v| request[k] = v }
response = http.request(request)
payload = JSON.parse(response.body)
rescue => e
error = e
end
return payload['data'] if payload&.dig('success') == true
status = response&.code&.to_i
is_client_error = payload&.dig('success') == false &&
status && status >= 400 && status < 500 && status != 429
if can_retry && !is_client_error
sleep(2 + rand * 3)
return fetch_sgo(url, options, can_retry: false)
end
error_msg = payload&.dig('error') || error&.message || 'Unknown'
warn "SGO Request Failed: #{status} #{error_msg}"
nil
end<?php
function fetchSgo(string $url, array $options = [], bool $canRetry = true): mixed {
$response = null;
$payload = null;
$error = null;
$httpCode = 0;
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $options['headers'] ?? [],
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($body !== false) {
$payload = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$payload = null;
}
}
if (isset($payload['success']) && $payload['success'] === true) {
return $payload['data'] ?? null;
}
$isClientError = isset($payload['success']) && $payload['success'] === false
&& $httpCode >= 400 && $httpCode < 500 && $httpCode !== 429;
if ($canRetry && !$isClientError) {
usleep((2 + lcg_value() * 3) * 1_000_000);
return fetchSgo($url, $options, false);
}
$errorMsg = $payload['error'] ?? $error ?: 'Unknown';
error_log("SGO Request Failed: {$httpCode} {$errorMsg}");
return null;
}