The Measurement API has rate limits in place to help ensure the availability of the service and prevent accidental plan overages. Rate limits are implemented per-game or API key as outlined in the table below

EndpointRate LimitCan be increased?
/eventsrequests per second500Yes
/events/batchrequests per minute500Yes

If you exceed these limits on your requests, you will begin to receive HTTP 429 Too Many Requests response.

📘

Limit Increases

If your integration will consistently be exceeding the default rate limits outlined above please contact your account manager who can help review your use case and raise your account limits.

Rate Limit Headers

The API returns several rate limit headers to help you implement throttling and retry mechanisms into your client

  • x-ratelimit-limit - The maximum number of requests permitted per time window. Formatted as defined in IEFT Proposal for Rate Limit Headers.
  • x-ratelimit-remaining - Indicates the number of remaining requests that you can make in the current time window
  • x-ratelimit-reset - The number of seconds until your rate limit resets for this endpoint

For example, if you receive a remaining value of 10 and a reset value of 60 then your client is allowed to make 10 more requests in the next 60 seconds before you would start to receive a 429 Too Many Requests response from the API.

Example

Below is a working Python example implementation that includes automated retries, backoff, and rate limit handling. This can be a good starting point when designing the error handling for your API integration.

import requests
import time

def send_events(events, retry_limit=15):
    """Send a batch of events to the Gamesight API.

    Args:
        events (list): List of events to send.
        retry_limit (int): Number of times to retry the request if it fails.

    Returns:
        bool: True if the request was successful, False otherwise.
    """
    attempts = 0
    while attempts < retry_limit:
        attempts += 1
        try:
            response = requests.post(
                "https://api.ingest.marketing.gamesight.io/events/batch", 
                headers = {"Authorization": "YOUR_API_KEY"}, 
                json = {"events": events}
            )

        except requests.exceptions.RequestException as e:
            print(f"Request failed with exception: {e}, retrying. Attempt {attempts}")
            time.sleep(0.5 * attempts)
            continue

        rate_limit_remaining = int(response.headers.get('x-ratelimit-remaining', 0))
        if response.status_code == 201:
            print("Request successful, rate limit remaining:", rate_limit_remaining)
            return True

        # If status is 422, the request was invalid and should not be retried
        elif response.status_code == 422:
            print("Invalid payload, skipping batch", response.json())
            return False

        print(f"Request failed with status code {response.status_code}, retrying")
        if rate_limit_remaining <= 1:
          	# This is a rate limit error, we should wait for the rate limit to reset
            sleep_duration = int(response.headers.get('x-ratelimit-reset', 10))
            print(f"Rate limit reached. Sleeping for {sleep_duration} seconds.")
            time.sleep(sleep_duration)
        else:
            # Exponential backoff if the error is not rate limiting
            time.sleep(0.5 * attempts)

    print("Reached retry limit, skipping batch")
    return False

if __name__ == "__main__":
    # Generate test events
    events = [
        {"type": "test", "user_id": "test-user-123", "identifiers": {}} for _ in range(1000)
    ]
    
    # Send is batches of batch_size
    batch_size = 10
    while len(events) > 0:
        batch = events[:batch_size]
        send_events(batch)
        events = events[batch_size:]

    print("Done")