- Updated: March 19, 2026
- 7 min read
Enforcing Per‑Agent Token‑Bucket Limits with OpenClaw Rating API Mobile SDKs
Enforcing per‑agent token‑bucket limits with the OpenClaw Rating API Mobile SDKs means configuring each mobile client (identified by a unique agent ID) with its own bucket size and refill rate, so that every device consumes API credits fairly and predictably.
1. Introduction
Mobile developers often integrate third‑party rating or analytics services that charge per request. Without proper rate limiting, a single aggressive user or a buggy client can exhaust your quota, leading to service disruption and unexpected costs. OpenClaw’s Rating API provides a flexible token‑bucket algorithm that can be enforced directly from iOS (Swift) and Android (Kotlin) SDKs. This guide walks you through the theory, configuration, and step‑by‑step implementation for both platforms, and shows real‑world scenarios where per‑agent limits shine.
2. What is a Token‑Bucket?
A token‑bucket is a classic rate‑limiting mechanism that controls how many requests an entity can make over time. Imagine a bucket that holds a fixed number of tokens:
- Bucket size (capacity) – maximum tokens the bucket can hold.
- Refill rate – how many tokens are added per second (or minute).
- Consume – each API call removes one token; if the bucket is empty, the request is rejected or delayed.
The bucket “smooths” bursts of traffic while guaranteeing a long‑term average rate. When applied per agent, each device gets its own bucket, preventing one user from starving others.
3. Per‑Agent Limits in OpenClaw
OpenClaw’s Rating API identifies callers by an agentId you provide in every request header. The server maintains a separate token‑bucket for each agentId. If a bucket runs dry, the API returns HTTP 429 (Too Many Requests) together with a Retry‑After header.
This design gives you three major advantages:
- Fairness – every device respects the same quota.
- Scalability – the server does not need a global lock; buckets are independent.
- Granular control – you can assign different capacities to premium vs. free users.
4. Configuring Bucket Size and Refill Rate per Agent
OpenClaw lets you set defaults globally and override them per agentId via its admin console or API. Below is a typical configuration table:
| Agent Type | Bucket Size (tokens) | Refill Rate (tokens/min) |
|---|---|---|
| Free user | 30 | 10 |
| Premium user | 200 | 60 |
| Enterprise device | 1000 | 300 |
In practice, you’ll generate an agentId on first launch (e.g., a UUID stored securely) and send it with every request. The SDKs we’ll build next automatically handle token consumption and retry logic.
5. Swift Implementation (iOS)
Setup
Add the OpenClaw Mobile SDK to your Xcode project via Swift Package Manager:
// In Xcode: File → Swift Packages → Add Package Dependency
// URL:
https://github.com/openclaw/rating-sdk-ios.git
Import the module in the files where you’ll call the API:
import OpenClawRatingStep‑by‑Step Code
1️⃣ Generate or retrieve the agent identifier
import Foundation
class AgentManager {
private let key = "com.myapp.agentId"
static let shared = AgentManager()
var agentId: String {
if let existing = UserDefaults.standard.string(forKey: key) {
return existing
}
let newId = UUID().uuidString
UserDefaults.standard.set(newId, forKey: key)
return newId
}
}
2️⃣ Configure the token‑bucket client
import OpenClawRating
class RatingService {
private let client: OpenClawClient
init() {
// Base URL of your self‑hosted OpenClaw instance
let baseURL = URL(string: "https://api.mycompany.com/openclaw")!
client = OpenClawClient(baseURL: baseURL,
agentId: AgentManager.shared.agentId)
}
func submitRating(productId: String,
score: Int,
completion: @escaping (Result<Void, Error>) -> Void) {
// The SDK automatically checks the bucket before sending.
client.post(path: "/v1/rating",
body: ["productId": productId,
"score": score]) { result in
switch result {
case .success(let response):
if response.statusCode == 200 {
completion(.success(()))
} else if response.statusCode == 429 {
// Optional: read Retry‑After header
let retryAfter = response.headers["Retry-After"] ?? "60"
let delay = Double(retryAfter) ?? 60
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
self.submitRating(productId: productId,
score: score,
completion: completion)
}
} else {
completion(.failure(NSError(domain: "OpenClaw",
code: response.statusCode,
userInfo: nil)))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
3️⃣ Use the service in your UI
let ratingService = RatingService()
ratingService.submitRating(productId: "SKU12345", score: 4) { result in
switch result {
case .success:
print("Rating submitted")
case .failure(let error):
print("Failed: \(error.localizedDescription)")
}
}
The SDK internally stores the bucket state in memory and persists the last token count to UserDefaults so that app restarts don’t reset the quota.
6. Kotlin Implementation (Android)
Setup
Add the OpenClaw SDK to your build.gradle:
dependencies {
implementation "io.openclaw:rating-sdk-android:1.2.0"
}
Step‑by‑Step Code
1️⃣ Generate or retrieve the agent identifier
object AgentManager {
private const val PREFS_NAME = "openclaw_prefs"
private const val KEY_AGENT_ID = "agent_id"
fun getAgentId(context: Context): String {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
var id = prefs.getString(KEY_AGENT_ID, null)
if (id == null) {
id = UUID.randomUUID().toString()
prefs.edit().putString(KEY_AGENT_ID, id).apply()
}
return id!!
}
}
2️⃣ Configure the client with bucket handling
import io.openclaw.rating.OpenClawClient
import io.openclaw.rating.Response
class RatingRepository(private val context: Context) {
private val client: OpenClawClient
init {
val baseUrl = "https://api.mycompany.com/openclaw"
client = OpenClawClient.Builder()
.baseUrl(baseUrl)
.agentId(AgentManager.getAgentId(context))
.build()
}
fun submitRating(productId: String, score: Int, callback: (Result<Unit>) -> Unit) {
val payload = mapOf("productId" to productId, "score" to score)
client.post("/v1/rating", payload) { result: Result<Response> ->
when {
result.isSuccess -> {
val response = result.getOrThrow()
when (response.code) {
200 -> callback(Result.success(Unit))
429 -> {
// Read Retry‑After header (in seconds)
val retryAfter = response.headers["Retry-After"]?.toLongOrNull() ?: 60L
Handler(Looper.getMainLooper()).postDelayed({
submitRating(productId, score, callback)
}, retryAfter * 1000)
}
else -> callback(Result.failure(Exception("HTTP ${response.code}")))
}
}
result.isFailure -> callback(Result.failure(result.exceptionOrNull()!!))
}
}
}
}
3️⃣ Trigger the rating from an Activity or ViewModel
class ProductViewModel(application: Application) : AndroidViewModel(application) {
private val repo = RatingRepository(application)
fun rateProduct(productId: String, score: Int) {
repo.submitRating(productId, score) { result ->
result.onSuccess {
Log.d("Rating", "Submitted successfully")
}.onFailure { e ->
Log.e("Rating", "Failed: ${e.message}")
}
}
}
}
The Android SDK mirrors the iOS behavior: it checks the token bucket before each request, caches the remaining token count in SharedPreferences, and respects the Retry‑After header on 429 responses.
7. Practical Usage Scenarios
High‑traffic API endpoints
Suppose your app streams user‑generated reviews to OpenClaw for sentiment scoring. During a product launch, traffic spikes could exceed your quota. By assigning a modest bucket (e.g., 50 tokens, 20‑token/min refill) to each device, you guarantee that no single user can flood the endpoint, while the aggregate traffic remains within your paid tier.
Fair usage across multiple devices
A family may have several phones logged into the same account. If you only limit by IP address, one device could consume the entire allowance, leaving the others blocked. Per‑agent limits treat each device independently, delivering a smoother experience for all family members.
Tiered subscription models
Offer a “Free” tier with a 30‑token bucket and a “Pro” tier with 200 tokens. The SDKs automatically enforce the correct limits because the agentId is linked to the user’s subscription status in your backend. When a user upgrades, you simply update the bucket configuration in the OpenClaw admin console; no client‑side changes are required.
8. Monitoring and Adjusting Limits
OpenClaw ships with a real‑time dashboard that shows:
- Current token count per
agentId - Rate of token consumption (requests/min)
- Number of 429 responses over time
Use the dashboard to spot outliers—e.g., a device that consistently hits the limit. You can then:
- Increase its bucket size if it belongs to a premium user.
- Investigate potential bugs causing excessive calls.
- Temporarily block the offending
agentIdvia the admin API.
For automated adjustments, poll the OpenClaw Rating API documentation to fetch current usage metrics and programmatically update bucket parameters via the /admin/buckets endpoint.
9. Conclusion
Enforcing per‑agent token‑bucket limits with OpenClaw’s Mobile SDKs gives you granular control, protects your budget, and delivers a consistent user experience across iOS and Android. By generating a unique agentId, configuring bucket size and refill rates per user tier, and handling 429 responses gracefully, you can scale rating or analytics calls without fear of runaway traffic.
Ready to run OpenClaw on your own infrastructure? Check out our self‑hosting OpenClaw guide for step‑by‑step deployment instructions.