← Back to all snippets
PHP

Client-Side Leaky Bucket Rate Limiter for API Calls

Implement a simple client-side leaky bucket rate limiter in PHP to control your outbound API request frequency and avoid exceeding external API limits.

<?php

class LeakyBucketRateLimiter {
    private $bucketCapacity; // Max requests allowed in a burst
    private $leakRatePerSecond; // How many requests 'leak' out per second
    private $tokens;
    private $lastRefillTime;

    /**
     * @param int $capacity The maximum number of tokens the bucket can hold (burst capacity).
     * @param float $leakRate The rate at which tokens are added to the bucket (requests per second).
     */
    public function __construct($capacity, $leakRate) {
        if ($capacity <= 0 || $leakRate <= 0) {
            throw new InvalidArgumentException("Capacity and leak rate must be positive.");
        }
        $this->bucketCapacity = $capacity;
        $this->leakRatePerSecond = $leakRate;
        $this->tokens = $capacity; // Start with a full bucket
        $this->lastRefillTime = microtime(true);
    }

    private function refill() {
        $currentTime = microtime(true);
        $timeElapsed = $currentTime - $this->lastRefillTime;
        
        if ($timeElapsed > 0) {
            $tokensToAdd = $timeElapsed * $this->leakRatePerSecond;
            $this->tokens = min($this->bucketCapacity, $this->tokens + $tokensToAdd);
            $this->lastRefillTime = $currentTime;
        }
    }

    /**
     * Attempts to consume a token. If the bucket is empty, it will wait.
     * @return bool True if a token was consumed (request can proceed), false if it timed out waiting.
     */
    public function acquireToken($timeoutSeconds = 30) {
        $startTime = microtime(true);

        while (true) {
            $this->refill();

            if ($this->tokens >= 1) {
                $this->tokens -= 1;
                return true; // Token acquired, proceed with request
            }

            if ((microtime(true) - $startTime) > $timeoutSeconds) {
                // Timeout reached, could not acquire token
                return false;
            }
            
            // Sleep for a short period before retrying
            usleep(50000); // 50 milliseconds
        }
    }
}

// Example Usage:
// Assume API allows 100 requests per minute (1.66 requests per second)
// and a burst of 10 requests.
// $rateLimiter = new LeakyBucketRateLimiter(10, 1.66);

// for ($i = 0; $i < 20; $i++) {
//     if ($rateLimiter->acquireToken()) {
//         echo "Request " . ($i + 1) . ": Sending API call at " . date('H:i:s.v') . "
";
//         // Simulate API call
//         // usleep(100000); // Simulate network latency
//     } else {
//         echo "Request " . ($i + 1) . ": Rate limit exceeded or timed out waiting.
";
//         break;
//     }
// }
How it works: This PHP snippet implements a client-side leaky bucket rate limiter, a mechanism to control the outgoing frequency of API requests to respect external API limits. The bucket has a `capacity` (burst limit) and a `leakRate` (steady-state request rate). When `acquireToken()` is called, the bucket is 'refilled' based on elapsed time, and if there are enough 'tokens' (representing available requests), one is consumed, and the request proceeds. If the bucket is empty, the client waits until a token becomes available or a timeout occurs, preventing the application from overwhelming the target API and incurring rate limit errors.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs