Skip to main content
The Snipe-IT API implements rate limiting to ensure fair usage and maintain system performance. All API requests are subject to throttling based on the number of requests per minute.

Rate Limit Configuration

Default Limits

By default, the API allows 120 requests per minute per authenticated user. This limit applies to all endpoints under the /api/v1 prefix.

Customizing Rate Limits

Administrators can adjust the rate limit by setting the API_THROTTLE_PER_MINUTE environment variable:
.env
API_THROTTLE_PER_MINUTE=120
After changing this value, restart your web server for the changes to take effect.

Rate Limit Headers

Every API response includes headers that inform you about your current rate limit status:
X-RateLimit-Limit
integer
The maximum number of requests allowed per minute (e.g., 120)
X-RateLimit-Remaining
integer
The number of requests remaining in the current window (e.g., 115)
X-RateLimit-Reset
integer
The number of seconds until the rate limit resets (e.g., 45)
X-RateLimit-Reset-Timestamp
integer
The Unix timestamp when the rate limit will reset (e.g., 1710507600)
Retry-After
integer
Only present when rate limited. Seconds to wait before retrying (e.g., 60)

Example Response Headers

HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 115
X-RateLimit-Reset: 45
X-RateLimit-Reset-Timestamp: 1710507600

Rate Limit Exceeded

When you exceed the rate limit, the API returns an HTTP 429 Too Many Requests status code:
{
  "message": "Too Many Requests"
}

Handling Rate Limit Errors

When you receive a 429 response:
  1. Check the Retry-After header to determine how long to wait
  2. Pause your requests for the specified duration
  3. Implement exponential backoff if you continue hitting limits
  4. Review your request patterns to optimize API usage

Implementation Details

Rate limiting in Snipe-IT uses Laravel’s built-in throttling middleware with custom extensions:
The API uses the api-throttle middleware defined in /routes/api.php:
Route::group(['prefix' => 'v1', 'middleware' => ['api', 'api-throttle:api']], function () {
    // All API routes
});
This middleware extends Laravel’s ThrottleRequests class with additional header support.
  • Limits are calculated per authenticated user
  • The counter resets every 60 seconds (sliding window)
  • Each request increments the counter by 1
  • Limits are enforced before the request is processed
Rate limit counters are stored using your configured cache driver (file, Redis, Memcached, etc.). Configure this in your .env:
CACHE_DRIVER=redis  # Recommended for production

Best Practices

1. Monitor Rate Limit Headers

Always check the X-RateLimit-Remaining header to track your usage:
import requests

headers = {
    'Authorization': 'Bearer YOUR_TOKEN',
    'Accept': 'application/json',
}

response = requests.get(
    'https://your-snipe-it-instance.com/api/v1/hardware',
    headers=headers
)

print(f"Remaining requests: {response.headers.get('X-RateLimit-Remaining')}")
print(f"Limit resets in: {response.headers.get('X-RateLimit-Reset')} seconds")

if int(response.headers.get('X-RateLimit-Remaining', 0)) < 10:
    print("Warning: Approaching rate limit!")

2. Implement Backoff Strategies

Handle rate limit errors gracefully with exponential backoff:
import requests
import time
from requests.exceptions import HTTPError

def make_api_request(url, max_retries=3):
    headers = {
        'Authorization': 'Bearer YOUR_TOKEN',
        'Accept': 'application/json',
    }
    
    for attempt in range(max_retries):
        try:
            response = requests.get(url, headers=headers)
            response.raise_for_status()
            return response.json()
        
        except HTTPError as e:
            if e.response.status_code == 429:
                # Get retry-after header or use exponential backoff
                retry_after = int(e.response.headers.get('Retry-After', 2 ** attempt))
                print(f"Rate limited. Retrying in {retry_after} seconds...")
                time.sleep(retry_after)
            else:
                raise
    
    raise Exception("Max retries exceeded")

# Usage
data = make_api_request('https://your-snipe-it-instance.com/api/v1/hardware')

3. Batch Requests Efficiently

Use pagination and filters to reduce the number of requests:
curl "https://your-snipe-it-instance.com/api/v1/hardware?limit=100&offset=0" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
The maximum results per request is controlled by the MAX_RESULTS setting (default: 500).

4. Optimize Request Frequency

Use Webhooks

Consider using webhooks for event-driven updates instead of polling

Cache Responses

Cache API responses locally when data doesn’t change frequently

Schedule Bulk Operations

Run large batch operations during off-peak hours

Parallel Tokens

Use multiple API tokens for different services if needed

5. Handle Edge Cases

Python Complete Example
import requests
import time
from typing import Optional, Dict, Any

class SnipeITClient:
    def __init__(self, base_url: str, token: str):
        self.base_url = base_url
        self.headers = {
            'Authorization': f'Bearer {token}',
            'Accept': 'application/json',
        }
        self.rate_limit_remaining = None
        self.rate_limit_reset = None
    
    def _make_request(self, method: str, endpoint: str, **kwargs) -> Dict[Any, Any]:
        url = f"{self.base_url}/api/v1/{endpoint}"
        
        # Wait if we're close to the limit
        if self.rate_limit_remaining and self.rate_limit_remaining < 5:
            wait_time = self.rate_limit_reset or 60
            print(f"Approaching rate limit. Waiting {wait_time} seconds...")
            time.sleep(wait_time)
        
        response = requests.request(method, url, headers=self.headers, **kwargs)
        
        # Update rate limit tracking
        self.rate_limit_remaining = int(
            response.headers.get('X-RateLimit-Remaining', 120)
        )
        self.rate_limit_reset = int(
            response.headers.get('X-RateLimit-Reset', 60)
        )
        
        # Handle rate limiting
        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            print(f"Rate limited! Waiting {retry_after} seconds...")
            time.sleep(retry_after)
            return self._make_request(method, endpoint, **kwargs)  # Retry
        
        response.raise_for_status()
        return response.json()
    
    def get(self, endpoint: str, **kwargs) -> Dict[Any, Any]:
        return self._make_request('GET', endpoint, **kwargs)
    
    def post(self, endpoint: str, **kwargs) -> Dict[Any, Any]:
        return self._make_request('POST', endpoint, **kwargs)

# Usage
client = SnipeITClient('https://your-snipe-it-instance.com', 'YOUR_TOKEN')
assets = client.get('hardware')
print(f"Remaining API calls: {client.rate_limit_remaining}")

Troubleshooting

  • Increase the limit: Adjust API_THROTTLE_PER_MINUTE in your .env file
  • Optimize queries: Use filters and pagination to reduce request volume
  • Implement caching: Cache frequently accessed data
  • Review architecture: Consider if your integration pattern is optimal
  • Ensure you’re using /api/v1 endpoints (rate limiting is only applied to API routes)
  • Check that the api-throttle middleware is active in routes/api.php
  • Verify your cache driver is working correctly
Rate limits are per-user, based on the authenticated user’s API token. If you’re seeing shared limits:
  • Verify you’re using different tokens for different users
  • Check if requests are being proxied through a single account
Rate limits use a sliding 60-second window. If you:
  • Make 120 requests at 0:00
  • Wait until 0:30
  • Make another request
You’ll still be rate limited because the window hasn’t fully elapsed since your first request.

Rate Limit Monitoring

For production integrations, implement monitoring to track rate limit usage:
Monitoring Example
import logging
from prometheus_client import Counter, Gauge

# Prometheus metrics
api_requests = Counter('snipeit_api_requests_total', 'Total API requests')
rate_limit_remaining = Gauge('snipeit_rate_limit_remaining', 'Remaining API requests')
rate_limit_exceeded = Counter('snipeit_rate_limit_exceeded_total', 'Rate limit exceeded count')

def track_api_request(response):
    api_requests.inc()
    
    remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
    rate_limit_remaining.set(remaining)
    
    if response.status_code == 429:
        rate_limit_exceeded.inc()
        logging.warning(f"Rate limit exceeded! Retry after {response.headers.get('Retry-After')}s")
    
    if remaining < 10:
        logging.warning(f"Low rate limit: {remaining} requests remaining")

Next Steps

Authentication

Learn about API token management

Pagination

Efficiently handle large datasets

Build docs developers (and LLMs) love