Skip to content

Commit 1b4f3e0

Browse files
committed
readme update
1 parent 1cd8159 commit 1b4f3e0

File tree

1 file changed

+74
-2
lines changed

1 file changed

+74
-2
lines changed

README.md

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,81 @@ echo $quota->requests_per_minute;
3838

3939
---
4040

41-
## Rate Limiting
41+
## Two-Layer Rate Limiting Architecture
4242

43-
The SDK automatically handles API rate limits. When the API returns HTTP 429 (Too Many Requests), the client will:
43+
The SDK uses a two-layer approach to keep your requests within API limits:
44+
45+
| Layer | Type | How it works |
46+
|---|---|---|
47+
| **Native Throttling** | Proactive (client-side) | Sliding window rate limiter blocks outgoing requests *before* they hit the server |
48+
| **429 Retry & Adaptive Polling** | Reactive (server-side) | Catches HTTP 429 responses, retries with backoff, and slows polling when limits are low |
49+
50+
The proactive layer prevents most 429 errors from ever occurring. The reactive layer acts as a safety net for edge cases (clock drift, concurrent processes, plan changes).
51+
52+
---
53+
54+
## Native Throttling
55+
56+
Every `SharpApiClient` instance includes an in-memory **sliding window rate limiter** (`SlidingWindowRateLimiter`) that tracks request timestamps in a 60-second rolling window.
57+
58+
Before each API call, `waitIfNeeded()` checks the window. If capacity is available the request proceeds immediately; if not, the call sleeps until the oldest timestamp expires out of the window.
59+
60+
- **Default limit:** 60 requests/minute (matches most subscription plans)
61+
- **Per-process, in-memory** — no external dependencies (Redis, database, etc.)
62+
- **Server-adaptive** — when the server returns a higher `X-RateLimit-Limit` header (e.g. after a plan upgrade), the limiter automatically ratchets up to match. It never ratchets *down*, so your configured default is always the floor.
63+
- **Metadata bypass**`ping()` and `quota()` skip throttling to avoid deadlocks when checking connectivity or subscription status.
64+
65+
### Configuration
66+
67+
```php
68+
$client = new SharpApiClient('your-api-key');
69+
70+
// Change the throttle limit (default: 60)
71+
$client->setRequestsPerMinute(120);
72+
73+
// Disable throttling entirely (pass 0)
74+
$client->setRequestsPerMinute(0);
75+
```
76+
77+
### Advanced Throttling Controls
78+
79+
You can access the underlying `SlidingWindowRateLimiter` instance directly for fine-grained control:
80+
81+
```php
82+
$limiter = $client->getRateLimiter();
83+
84+
// Non-blocking check — returns true if a request can proceed without waiting
85+
$limiter->canProceed();
86+
87+
// How many requests are available in the current window
88+
$limiter->remaining(); // e.g. 42
89+
```
90+
91+
**Cross-process state caching** — save and restore the server-reported rate limit state (e.g. between HTTP requests in a web app):
92+
93+
```php
94+
// After an API call, cache the server-reported state
95+
$state = $client->getRateLimitState();
96+
// $state = ['limit' => 60, 'remaining' => 58]
97+
cache()->put('sharpapi_rate_state', $state, 60);
98+
99+
// In the next process/request, restore it
100+
$client->setRateLimitState(cache()->get('sharpapi_rate_state'));
101+
```
102+
103+
**Quick quota check** — verify the server still reports available capacity before making a request:
104+
105+
```php
106+
if ($client->canMakeRequest()) {
107+
// Server-reported remaining > 0 (or no server data yet)
108+
}
109+
```
110+
111+
---
112+
113+
## Automatic 429 Retry & Adaptive Polling
114+
115+
If a request does hit the server rate limit, the SDK handles it automatically:
44116

45117
1. **Retry automatically** — reads the `Retry-After` header, sleeps for the specified duration, and retries the request (up to 3 times by default).
46118
2. **Slow down polling** — during `fetchResults()`, when `X-RateLimit-Remaining` drops below the low threshold, polling intervals are automatically increased to avoid hitting the limit.

0 commit comments

Comments
 (0)