Skip to main content
TheRundown API uses standard HTTP status codes to indicate whether a request succeeded or failed. This page covers every status code you may encounter, the structure of error responses, and how to handle them.

HTTP Status Codes

Status CodeMeaningDescription
200OKThe request was successful. The response body contains the requested data.
400Bad RequestThe request contains invalid parameters or is malformed.
401UnauthorizedAuthentication failed. The API key is missing, invalid, or expired.
404Not FoundThe requested resource does not exist.
429Too Many RequestsYou exceeded a burst throttle or a data-point cap. See Rate Limits.
500Internal Server ErrorAn unexpected error occurred on the server.

Error Response Format

When an error occurs, the API returns a JSON object with a message describing the problem.
{
  "message": "A human-readable description of the problem."
}

Detailed Status Code Reference

200 OK

The request succeeded. For list endpoints, the response is a JSON array. For single-resource endpoints, the response is a JSON object.
{
  "event_id": "abc123",
  "sport_id": 2,
  "teams": { ... },
  "score": { ... }
}

400 Bad Request

The request was rejected because one or more parameters are invalid. Check the message field for specifics. Common causes:
  • An invalid sport_id value
  • A malformed date format (expected YYYY-MM-DD)
  • An unrecognized query parameter value
{
  "message": "Invalid sport_id: 999. See /v2/sports for valid sport IDs."
}
How to fix: Review the request parameters against the API reference documentation. Ensure all required parameters are present and correctly formatted.

401 Unauthorized

Authentication failed. The API could not verify your identity. Common causes:
  • No API key was provided
  • The API key is invalid or has been revoked
  • The X-TheRundown-Key header is missing or malformed
{
  "message": "unauthorized"
}
How to fix: Verify that your API key is correct and included in the request. See the Authentication guide for supported methods.
# Verify your key is being sent correctly
curl -v -H "X-TheRundown-Key: YOUR_API_KEY" \
  "https://therundown.io/api/v2/sports/2/events/2026-02-12"

404 Not Found

The requested resource does not exist. This typically means the event ID, sport ID, or other identifier in the URL path does not match any record. Common causes:
  • An event ID that does not exist or has been archived
  • A URL path that is misspelled or references a deprecated endpoint
{
  "message": "Event not found: abc123"
}
How to fix: Confirm the resource identifier is correct. Use the appropriate list endpoint to discover valid IDs before requesting a specific resource.

429 Too Many Requests

TheRundown uses 429 for more than one condition. Read the response body and headers before deciding whether to retry immediately.
{
  "error": "Rate limit exceeded",
  "limit": 2
}
You may also see:
{
  "error": "Daily data point limit reached",
  "limit": 20000,
  "used": 20000
}
or:
{
  "error": "Monthly data point limit reached",
  "limit": 10000000,
  "used": 10000000,
  "period": "monthly"
}
How to fix:
  • Read Retry-After first.
  • Inspect X-Datapoints-Used, X-Datapoints-Remaining, X-Datapoints-Reset, X-Tier, and X-Rate-Limit.
  • If the body says Rate limit exceeded, retry after a short backoff.
  • If the body says Daily data point limit reached or Monthly data point limit reached, this is a usage-window issue, not a one-second throttle. Retrying immediately will not help.

500 Internal Server Error

An unexpected error occurred on the server side. This is not caused by your request.
{
  "message": "An unexpected error occurred. Please try again later."
}
How to fix: Retry the request after a brief delay. If the error persists, contact support at [email protected] and include the full request URL and timestamp.

The 0.0001 Sentinel Value

The value 0.0001 appearing in odds or line fields is not an error. It is a sentinel value indicating that a line is currently unavailable or has not yet been posted by the sportsbook.
When a sportsbook has not released a line, or when a previously available line has been taken down, the API returns 0.0001 rather than null or omitting the field. This ensures a consistent numeric type across all responses and makes it straightforward to filter in your code.
{
  "affiliate_id": 1,
  "spread": {
    "point_spread_home": 0.0001,
    "point_spread_away": 0.0001,
    "point_spread_home_money": 0.0001,
    "point_spread_away_money": 0.0001
  }
}

How to Handle 0.0001

Filter out the sentinel value when displaying or processing lines. Treat any field equal to 0.0001 as “not available.”
SENTINEL = 0.0001

def is_line_available(value):
    """Return True if the line value is real, not the sentinel."""
    return value is not None and value != SENTINEL

spread = event["spread"]["point_spread_home"]
if is_line_available(spread):
    print(f"Home spread: {spread}")
else:
    print("Home spread: not available")

Troubleshooting Checklist

If you are encountering errors, work through this checklist:
  1. Check your API key. Is it present in the request? Is it valid? Try the key against a public endpoint like /v2/sports.
  2. Inspect the full response. Read the message field in the error response for specific guidance.
  3. Review the request URL. Ensure the path, query parameters, and date formats are correct.
  4. Check your billing and throttle headers. If you are getting 429 responses, inspect Retry-After, X-Datapoints-Remaining, X-Datapoints-Reset, and X-Rate-Limit.
  5. Retry with backoff for 5xx errors. Server errors are usually transient. Retry after a short delay.
  6. Contact support. If the issue persists, email [email protected] with the request URL, response body, and timestamp.

Retryable vs Non-Retryable Errors

Not all errors should be retried. Retrying a 401 won’t fix a bad API key, but a 502 may resolve on the next attempt.
StatusRetryable?Action
400NoFix the request parameters
401NoCheck your API key
404NoVerify the resource ID or URL path
429Yes, sometimesRespect Retry-After; do not blindly retry cap-based 429s
500YesRetry with exponential backoff
502YesRetry with exponential backoff
503YesRetry with exponential backoff

Retry Strategy

For retryable errors, use exponential backoff with jitter to avoid thundering-herd problems when the server recovers. The pattern: wait base * 2^attempt seconds, add a random jitter, and cap the maximum delay. Three retries is usually sufficient — if the error persists after that, log it and move on.
import requests
import time
import random

API_KEY = "YOUR_API_KEY"
BASE = "https://therundown.io/api/v2"
RETRYABLE = {429, 500, 502, 503}

def fetch_with_retry(url, params=None, max_retries=3):
    """Fetch a URL with exponential backoff for transient errors."""
    for attempt in range(max_retries + 1):
        resp = requests.get(
            url,
            headers={"X-TheRundown-Key": API_KEY},
            params=params,
        )

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

        if resp.status_code not in RETRYABLE or attempt == max_retries:
            resp.raise_for_status()

        if resp.status_code == 429:
            retry_after = resp.headers.get("Retry-After")
            wait = max(int(retry_after), 1) if retry_after else 60
        else:
            wait = min(2 ** attempt + random.uniform(0, 1), 30)

        print(f"Retrying in {wait:.1f}s (attempt {attempt + 1}/{max_retries})")
        time.sleep(wait)

# Usage
events = fetch_with_retry(f"{BASE}/sports/4/events/2026-02-26")

Complete Error-Handling Wrapper

This wrapper combines retry logic with sentinel value filtering into a single utility you can use across your integration.
import requests
import time
import random

API_KEY = "YOUR_API_KEY"
BASE = "https://therundown.io/api/v2"
SENTINEL = 0.0001
RETRYABLE = {429, 500, 502, 503}

def api_request(path, params=None, max_retries=3):
    """Make an API request with retry logic. Returns parsed JSON."""
    url = f"{BASE}/{path.lstrip('/')}"

    for attempt in range(max_retries + 1):
        resp = requests.get(
            url,
            headers={"X-TheRundown-Key": API_KEY},
            params=params,
        )

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

        if resp.status_code not in RETRYABLE or attempt == max_retries:
            resp.raise_for_status()

        if resp.status_code == 429:
            retry_after = resp.headers.get("Retry-After")
            wait = max(int(retry_after), 1) if retry_after else 60
        else:
            wait = min(2 ** attempt + random.uniform(0, 1), 30)

        time.sleep(wait)

def is_available(price_value):
    """Return True if the price is real (not the 0.0001 sentinel)."""
    return price_value is not None and price_value != SENTINEL

def get_main_line_prices(event, market_id, affiliate_id):
    """Extract the main-line price for each participant in a market."""
    results = []
    for market in event.get("markets", []):
        if market["market_id"] != market_id:
            continue
        for participant in market.get("participants", []):
            for line in participant.get("lines", []):
                price_obj = line.get("prices", {}).get(str(affiliate_id))
                if not price_obj or not price_obj.get("is_main_line"):
                    continue
                price = price_obj["price"]
                results.append({
                    "participant": participant["name"],
                    "line": line["value"],
                    "price": price if is_available(price) else None,
                    "available": is_available(price),
                })
    return results

# Usage
data = api_request("sports/4/events/2026-02-26", {"market_ids": "1,2,3"})
for event in data.get("events", []):
    spreads = get_main_line_prices(event, market_id=2, affiliate_id=19)
    for s in spreads:
        if s["available"]:
            print(f"{s['participant']}: {s['line']} ({s['price']})")
        else:
            print(f"{s['participant']}: off the board")
For strategies to reduce the number of API calls you make (and the errors you encounter), see the Efficient Polling guide and Rate Limits.