Enhanced Requests Library with Adaptive Rate Limit Estimation
Project description
SmartSurge: Intelligent HTTP Client with Adaptive Rate Limit Detection
SmartSurge is an intelligent HTTP client library that enhances the popular requests library with automatic rate limit detection, handling, and request throttling. Using statistical analysis and machine learning techniques, SmartSurge can automatically detect and adapt to rate limits without requiring any API-specific configuration.
🚀 Key Features
- ⚡ Drop-in Replacement: Works seamlessly with your existing requests-based code
- 🔄 Resumable Downloads: Stream large files with automatic resume capability on connection failures
- 🔀 Async Support: Full support for asynchronous operations using aiohttp
- 📊 Multi-Tiered Rate Limit Detection: Supports manual, server-provided, and HMM-based rate limit detection
- 🧠 Intelligent Rate Limit Detection: Automatically detects rate limits using Hidden Markov Models (HMM)
- 🛡️ Automatic Retries: Built-in retry logic with exponential backoff
IMPORTANT NOTE: The Hidden Markov Model component is currently under development and is not yet production-ready.
📦 Installation
pip install smartsurge
For development or additional features:
# Install with development tools
pip install smartsurge[dev]
# Install with benchmarking tools
pip install smartsurge[benchmark]
# Install with documentation tools
pip install smartsurge[docs]
# Install everything
pip install smartsurge[dev,benchmark,docs]
🎯 Quick Start
Basic Usage
from smartsurge import SmartSurgeClient
# Create a client - it works just like requests!
client = SmartSurgeClient()
# Make requests without worrying about rate limits
response = client.get("https://api.example.com/data")
print(response.json())
# Get response with request history for insights
response, history = client.get("https://api.example.com/data", return_history=True)
# Check detected rate limits
limits = client.list_rate_limits()
for (endpoint, method), limit in limits.items():
if limit:
print(f"{endpoint} {method}: {limit.max_requests} requests per {limit.time_period}s")
Configuration
from smartsurge import SmartSurgeClient
# Create a client with custom configuration
client = SmartSurgeClient(
base_url="https://api.example.com",
timeout=(10.0, 30.0), # (connect, read) timeouts
max_retries=3, # Maximum retry attempts
backoff_factor=0.3, # Exponential backoff multiplier
refit_every=20, # Refit HMM every N requests
)
# All requests will use the base URL
response = client.get("/users") # Requests https://api.example.com/users
Manual Rate Limit Configuration
# Set a known rate limit
client.set_rate_limit(
endpoint="https://api.example.com/users",
method="GET",
max_requests=100,
time_period=60.0 # 100 requests per minute
)
# Check current rate limit
rate_limit = client.get_rate_limit("https://api.example.com/users", "GET")
if rate_limit:
print(f"Rate limit: {rate_limit.max_requests} per {rate_limit.time_period}s")
print(f"Source: {rate_limit.source}") # 'manual', 'header', or 'estimated'
Streaming Large Files
from smartsurge import JSONStreamingRequest
# Stream large files with automatic resume capability
result = client.stream_request(
streaming_class=JSONStreamingRequest,
endpoint="https://example.com/large-dataset.json",
state_file="download_state.json", # Automatically saves progress
chunk_size=1024 * 1024 # 1MB chunks
)
# If interrupted, the download will resume from where it left off
Async Support
import asyncio
async def fetch_data():
client = SmartSurgeClient()
# Make async requests
response = await client.async_get("https://api.example.com/data")
data = await response.json()
# Parallel requests
tasks = [
client.async_get(f"https://api.example.com/items/{i}")
for i in range(10)
]
responses = await asyncio.gather(*tasks)
return responses
# Run the async function
results = asyncio.run(fetch_data())
🧠 How It Works
SmartSurge uses a three-tier approach to rate limiting:
-
Manual Rate Limits (Highest Priority)
- Explicitly set rate limits using
set_rate_limit() - Useful when you know the API's limits
- Explicitly set rate limits using
-
Server-Provided Limits
- Automatically reads standard rate limit headers:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-ResetRetry-Afterheaders from 429 responses
- Automatically reads standard rate limit headers:
-
HMM-Based Learning (Automatic)
- Uses a 3-state Hidden Markov Model to detect patterns:
- State 0: Normal operation
- State 1: Approaching rate limit
- State 2: Rate limited
- Continuously adapts to changing patterns
- Uses a 3-state Hidden Markov Model to detect patterns:
📊 Advanced Features
Request History Analysis
# Get detailed request history
response, history = client.get("/api/data", return_history=True)
# Analyze the history
print(f"Total requests: {len(history.requests)}")
# Check if rate limit was detected
if history.rate_limit:
print(f"Detected limit: {history.rate_limit}"))
Custom Configuration
from smartsurge import ClientConfig
# Create reusable configuration
config = ClientConfig(
base_url="https://api.example.com",
timeout=(5.0, 30.0),
max_retries=5,
backoff_factor=0.5,
# HMM Configuration
min_time_period=1.0, # Minimum rate limit window (seconds)
max_time_period=3600.0, # Maximum rate limit window (1 hour)
refit_every=20, # Refit HMM every N requests
# Other options
verify_ssl=True,
user_agent="MyApp/1.0 (SmartSurge)"
)
client = SmartSurgeClient(**config.model_dump())
Error Handling
from smartsurge import (
RateLimitExceeded,
StreamingError,
ResumeError,
ValidationError,
ConfigurationError
)
try:
response = client.get("/api/resource")
except RateLimitExceeded as e:
print(f"Rate limit hit: {e.message}")
if e.retry_after:
print(f"Retry after {e.retry_after} seconds")
except StreamingError as e:
print(f"Streaming failed: {e.message}")
except Exception as e:
print(f"Unexpected error: {e}")
🔧 Utilities
SmartSurge includes helpful utilities:
from smartsurge import (
SmartSurgeTimer,
configure_logging,
merge_histories,
async_request_with_history
)
# Configure logging
configure_logging(level=logging.DEBUG)
# Time your requests
with SmartSurgeTimer() as timer:
response = client.get("/api/data")
print(f"Request took {timer.duration:.3f} seconds")
# Merge multiple histories
history1 = client.get("/api/users", return_history=True)[1]
history2 = client.get("/api/posts", return_history=True)[1]
combined = merge_histories([history1, history2])
📈 Benchmarking
When installed with benchmark extras, you can test rate limit detection:
from smartsurge import create_benchmark_server, SmartSurgeClient
# Create a mock server with known rate limits
server = create_benchmark_server(
max_requests=10,
time_period=1.0 # 10 requests per second
)
# Test SmartSurge against it
client = SmartSurgeClient()
# ... run your tests
🤝 Contributing
We welcome contributions! Please see our Contributing Guidelines for details.
📄 License
SmartSurge is released under the Apache License 2.0. See the LICENSE file for details.
🔗 Links
- Documentation: Full documentation
- PyPI: smartsurge
- GitHub: dingo-actual/smartsurge
- Issues: Report bugs or request features
📧 Contact
- Author: Ryan Taylor
- Email: ryan@beta-reduce.net
🏆 Credits
SmartSurge uses Hidden Markov Models for intelligent rate limit detection, leveraging the power of statistical learning to provide a truly adaptive HTTP client experience.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file smartsurge-0.0.5.tar.gz.
File metadata
- Download URL: smartsurge-0.0.5.tar.gz
- Upload date:
- Size: 1.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1fe48cf0bd87109842c26efea8d36313574cff8fec74957e9f349c106df4f66c
|
|
| MD5 |
5003c255fb68486d09a7b18fc3ebc72e
|
|
| BLAKE2b-256 |
5f686c3a47d397f782c799902675b83edf1d5bbfc001a9f49e14c1c2ae269693
|
Provenance
The following attestation bundles were made for smartsurge-0.0.5.tar.gz:
Publisher:
publish.yml on dingo-actual/smartsurge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
smartsurge-0.0.5.tar.gz -
Subject digest:
1fe48cf0bd87109842c26efea8d36313574cff8fec74957e9f349c106df4f66c - Sigstore transparency entry: 220053495
- Sigstore integration time:
-
Permalink:
dingo-actual/smartsurge@79326706c226fa5dca574fe8a178ffd03230caca -
Branch / Tag:
refs/tags/v0.0.5-alpha - Owner: https://github.com/dingo-actual
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@79326706c226fa5dca574fe8a178ffd03230caca -
Trigger Event:
release
-
Statement type:
File details
Details for the file smartsurge-0.0.5-py3-none-any.whl.
File metadata
- Download URL: smartsurge-0.0.5-py3-none-any.whl
- Upload date:
- Size: 133.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f015d392dcd48bf4df1e8c82ddc7806237bcd963ccec95617b7ed488e4f61216
|
|
| MD5 |
5fafd26453440801cf1b9776f8c51738
|
|
| BLAKE2b-256 |
2eda32e21f4b90aeef216401f767d5a9d28001051b78a718de55e7886258d870
|
Provenance
The following attestation bundles were made for smartsurge-0.0.5-py3-none-any.whl:
Publisher:
publish.yml on dingo-actual/smartsurge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
smartsurge-0.0.5-py3-none-any.whl -
Subject digest:
f015d392dcd48bf4df1e8c82ddc7806237bcd963ccec95617b7ed488e4f61216 - Sigstore transparency entry: 220053496
- Sigstore integration time:
-
Permalink:
dingo-actual/smartsurge@79326706c226fa5dca574fe8a178ffd03230caca -
Branch / Tag:
refs/tags/v0.0.5-alpha - Owner: https://github.com/dingo-actual
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@79326706c226fa5dca574fe8a178ffd03230caca -
Trigger Event:
release
-
Statement type: