Skip to main content
All API errors return a consistent JSON structure with an HTTP status code.

Error Response Format

{
  "message": "Human-readable error description",
  "data": {
    "field": "additional context"
  }
}
FieldTypeDescription
messagestringError description
dataobject?Additional error details (optional)

HTTP Status Codes

400 Bad Request

Invalid request parameters or malformed request.
{
  "message": "Invalid parameter: start_time must be RFC3339 format",
  "data": {
    "parameter": "start_time",
    "received": "2024-01-01"
  }
}
Common causes:
  • Invalid date format (use RFC3339: 2024-01-01T00:00:00Z)
  • Invalid enum value for platform, status, or side
  • limit exceeds maximum (1000)
  • Invalid UUID format for market_id

401 Unauthorized

Missing or invalid authentication.
{
  "message": "Invalid API credentials"
}
Common causes:
  • Missing Authorization header
  • Incorrect format (should be Bearer API_KEY_ID:API_KEY_SECRET)
  • Invalid or revoked API key
  • Expired credentials
Fix:
# Correct format
curl -H "Authorization: Bearer api_xxx:sk_xxx" \
  https://api.probalytics.io/api/v1/markets

403 Forbidden

Valid credentials but insufficient permissions.
{
  "message": "Access denied: upgrade required for this resource"
}
Common causes:
  • Accessing data outside your plan’s date range (Starter: 30 days)
  • Requesting more data than your tier allows

429 Too Many Requests

Rate limit exceeded.
{
  "message": "Rate limit exceeded",
  "data": {
    "limit": 100,
    "window": "1 hour",
    "retry_after": 1800
  }
}
Rate limits by tier:
TierRequests/Hour
Starter100
Pro1,000
CustomUnlimited
Handling rate limits:
import time

response = requests.get(url, headers=headers)

if response.status_code == 429:
    retry_after = response.json().get("data", {}).get("retry_after", 60)
    time.sleep(retry_after)
    response = requests.get(url, headers=headers)

500 Internal Server Error

Unexpected server error.
{
  "message": "Internal server error"
}
What to do:
  • Retry with exponential backoff
  • If persistent, contact support with request details

Error Handling Examples

Python

import requests

API_KEY = "api_xxx:sk_xxx"
headers = {"Authorization": f"Bearer {API_KEY}"}

response = requests.get(
    "https://api.probalytics.io/api/v1/markets",
    headers=headers
)

if response.status_code == 200:
    markets = response.json()
elif response.status_code == 400:
    error = response.json()
    print(f"Bad request: {error['message']}")
elif response.status_code == 401:
    print("Check your API credentials")
elif response.status_code == 429:
    retry_after = response.json().get("data", {}).get("retry_after", 60)
    print(f"Rate limited. Retry in {retry_after} seconds")
else:
    print(f"Error {response.status_code}: {response.text}")

JavaScript

const API_KEY = "api_xxx:sk_xxx";

const response = await fetch("https://api.probalytics.io/api/v1/markets", {
  headers: { "Authorization": `Bearer ${API_KEY}` }
});

if (response.ok) {
  const markets = await response.json();
} else {
  const error = await response.json();

  switch (response.status) {
    case 400:
      console.error("Bad request:", error.message);
      break;
    case 401:
      console.error("Check your API credentials");
      break;
    case 429:
      const retryAfter = error.data?.retry_after || 60;
      console.error(`Rate limited. Retry in ${retryAfter}s`);
      break;
    default:
      console.error(`Error ${response.status}:`, error.message);
  }
}

Go

resp, err := http.Get(url)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
    var apiError struct {
        Message string                 `json:"message"`
        Data    map[string]interface{} `json:"data"`
    }
    json.NewDecoder(resp.Body).Decode(&apiError)

    switch resp.StatusCode {
    case 400:
        log.Printf("Bad request: %s", apiError.Message)
    case 401:
        log.Println("Check your API credentials")
    case 429:
        log.Println("Rate limited")
    default:
        log.Printf("Error %d: %s", resp.StatusCode, apiError.Message)
    }
    return
}

Retry Strategy

For transient errors (429, 500, 503), implement exponential backoff:
import time
import random

def fetch_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code == 200:
            return response.json()

        if response.status_code in [429, 500, 503]:
            # Exponential backoff with jitter
            wait = (2 ** attempt) + random.uniform(0, 1)

            # Use server's retry-after if provided
            if response.status_code == 429:
                wait = response.json().get("data", {}).get("retry_after", wait)

            time.sleep(wait)
            continue

        # Non-retryable error
        response.raise_for_status()

    raise Exception("Max retries exceeded")

Common Mistakes

Wrong Authorization Format

# Wrong: Missing Bearer
curl -H "Authorization: api_xxx:sk_xxx" ...

# Wrong: Only API Key ID
curl -H "Authorization: Bearer api_xxx" ...

# Correct
curl -H "Authorization: Bearer api_xxx:sk_xxx" ...

Invalid Date Format

# Wrong: Not RFC3339
?start_time=2024-01-01
?start_time=01/01/2024

# Correct: RFC3339 with timezone
?start_time=2024-01-01T00:00:00Z

Exceeding Limits

# Wrong: limit too high
?limit=5000

# Correct: max 1000, paginate for more
?limit=1000&offset=0
?limit=1000&offset=1000