- Updated: March 19, 2026
- 7 min read
Persisting OpenClaw Rating API Edge Token‑Bucket State with Cloudflare Workers KV
Persisting the OpenClaw Rating API edge token‑bucket state with Cloudflare Workers KV is achieved by storing the bucket counters in a KV namespace and updating them atomically on each request, allowing rate‑limit data to survive across worker restarts and edge locations.
1. Introduction
Developers building high‑traffic APIs often need a lightweight, distributed rate‑limiting solution that works at the edge. OpenClaw’s Rating API uses a token‑bucket algorithm to throttle requests per client, but without persistence the bucket resets whenever a Cloudflare Worker instance is cold‑started. This article explains the edge‑KV model, walks you through a step‑by‑step deployment of a persistent token‑bucket layer, and shares production‑grade best practices.
2. Understanding the Edge‑KV Model
Cloudflare Workers KV (Key‑Value) is a globally replicated, eventually consistent storage system designed for read‑heavy workloads. Data written to KV is propagated to all edge locations within seconds, making it ideal for:
- Feature flags
- Configuration files
- Low‑frequency state such as rate‑limit counters
Because KV is eventually consistent, you must design your token‑bucket logic to tolerate slight staleness. The typical pattern is to read the current bucket, compute the new token count, and write it back in a single request cycle.
3. Why Persist Token‑Bucket State?
Without persistence, each worker instance starts with a fresh bucket, effectively disabling rate limiting during cold starts. Persisting state provides:
- Consistency: Clients see the same limit regardless of which edge node serves them.
- Reliability: Limits survive worker redeployments and platform upgrades.
- Analytics: Historical token usage can be aggregated for monitoring and billing.
4. Architecture Overview
The architecture consists of four core components:
- Cloudflare Worker: Executes the OpenClaw Rating API logic at the edge.
- KV Namespace: Stores a JSON object per client identifier (e.g., API key).
- Token‑Bucket Algorithm: Calculates token refill based on elapsed time.
- Monitoring Layer: Sends metrics to Cloudflare Logs, Datadog, or a custom dashboard.
Diagram (conceptual)
Client → Cloudflare Edge → Worker (read KV) → Token‑Bucket Logic → Write KV → Response5. Step‑by‑Step Deployment
5.1. Set Up Cloudflare Workers
First, install the wrangler CLI and initialise a new project:
npm install -g @cloudflare/wrangler
wrangler init openclaw-token-bucket --type=javascript
cd openclaw-token-bucketUpdate wrangler.toml with your account ID and a descriptive name.
5.2. Create KV Namespace
In the same wrangler.toml file, declare a KV binding:
kv_namespaces = [
{ binding = "TOKEN_BUCKET", id = "YOUR_KV_NAMESPACE_ID" }
]Run wrangler kv:namespace create "TOKEN_BUCKET" to generate the ID, then paste it back into the config.
5.3. Implement Token‑Bucket Logic
The core algorithm is straightforward: each bucket holds capacity tokens and refills at rate tokens per second.
/**
* Compute the new token count for a bucket.
* @param {number} capacity - Max tokens in the bucket.
* @param {number} rate - Tokens added per second.
* @param {number} lastRefill - Epoch ms of the last refill.
* @param {number} tokens - Tokens remaining at lastRefill.
* @returns {{tokens: number, lastRefill: number}}
*/
function refillBucket({capacity, rate, lastRefill, tokens}) {
const now = Date.now();
const elapsed = (now - lastRefill) / 1000; // seconds
const newTokens = Math.min(capacity, tokens + elapsed * rate);
return {tokens: newTokens, lastRefill: now};
}5.4. Persist State to KV
Below is a minimal Worker script that reads the bucket, refills it, checks the request, and writes the updated state back to KV.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
const BUCKET_CAPACITY = 100; // max requests per minute
const REFILL_RATE = BUCKET_CAPACITY / 60; // tokens per second
async function handleRequest(request) {
const apiKey = request.headers.get('x-api-key');
if (!apiKey) return new Response('Missing API key', {status: 401});
// 1️⃣ Read bucket state from KV
const raw = await TOKEN_BUCKET.get(apiKey);
const bucket = raw ? JSON.parse(raw) : {tokens: BUCKET_CAPACITY, lastRefill: Date.now()};
// 2️⃣ Refill tokens based on elapsed time
const {tokens, lastRefill} = refillBucket({
capacity: BUCKET_CAPACITY,
rate: REFILL_RATE,
lastRefill: bucket.lastRefill,
tokens: bucket.tokens,
});
// 3️⃣ Consume a token if available
if (tokens < 1) {
// Persist the unchanged state for accurate accounting
await TOKEN_BUCKET.put(apiKey, JSON.stringify({tokens, lastRefill}));
return new Response('Rate limit exceeded', {status: 429});
}
const newState = {tokens: tokens - 1, lastRefill};
// 4️⃣ Persist updated state
await TOKEN_BUCKET.put(apiKey, JSON.stringify(newState));
// 5️⃣ Forward request to OpenClaw backend (simplified)
const upstream = new URL('https://api.openclaw.io/rating');
upstream.search = new URL(request.url).search;
const upstreamResponse = await fetch(upstream, {
method: request.method,
headers: request.headers,
});
return new Response(upstreamResponse.body, {
status: upstreamResponse.status,
headers: upstreamResponse.headers,
});
}5.5. Testing Locally and on the Edge
Use wrangler dev to spin up a local preview that mimics the edge environment. Verify that repeated calls with the same x-api-key eventually hit the 429 response.
# In one terminal
wrangler dev
# In another terminal, fire 105 requests
for i in $(seq 1 105); do
curl -s -H "x-api-key: test-key" "http://localhost:8787/?item=123" -o /dev/null -w "%{http_code}\n"
doneAfter confirming local behavior, publish with wrangler publish. The KV namespace will automatically replicate to all Cloudflare edge locations.
6. Code Snippets (JavaScript/TypeScript)
Below are TypeScript‑typed equivalents for developers who prefer static typing.
type BucketState = {
tokens: number;
lastRefill: number;
};
function refillBucketTS(state: BucketState, capacity: number, rate: number): BucketState {
const now = Date.now();
const elapsed = (now - state.lastRefill) / 1000;
const newTokens = Math.min(capacity, state.tokens + elapsed * rate);
return {tokens: newTokens, lastRefill: now};
}
// Worker handler (TS)
export default {
async fetch(request: Request, env: { TOKEN_BUCKET: KVNamespace }) {
const apiKey = request.headers.get('x-api-key');
if (!apiKey) return new Response('Missing API key', {status: 401});
const raw = await env.TOKEN_BUCKET.get(apiKey);
const bucket: BucketState = raw ? JSON.parse(raw) : {tokens: BUCKET_CAPACITY, lastRefill: Date.now()};
const refreshed = refillBucketTS(bucket, BUCKET_CAPACITY, REFILL_RATE);
if (refreshed.tokens < 1) {
await env.TOKEN_BUCKET.put(apiKey, JSON.stringify(refreshed));
return new Response('Rate limit exceeded', {status: 429});
}
const newState = {tokens: refreshed.tokens - 1, lastRefill: refreshed.lastRefill};
await env.TOKEN_BUCKET.put(apiKey, JSON.stringify(newState));
// Forward to OpenClaw (omitted for brevity)
return new Response('OK', {status: 200});
},
};7. Production Best‑Practice Considerations
7.1. Namespace Management
Separate KV namespaces for development, staging, and production to avoid cross‑environment contamination. Use descriptive names like openclaw-prod-token-bucket. Cloudflare’s UBOS platform overview demonstrates how multi‑tenant KV can be orchestrated alongside other services.
7.2. Rate‑Limit Configuration
Choose bucket parameters based on business SLAs. For burst‑heavy traffic, increase capacity while keeping rate aligned with average QPS. Document the limits in your API spec so clients can self‑throttle.
7.3. Monitoring & Logging
Emit custom logs for each request that include:
- API key hash
- Tokens remaining after the request
- Timestamp of the refill operation
These logs can be shipped to Cloudflare Logs, Datadog, or a self‑hosted ELK stack. Alert when the 429 rate exceeds a threshold, indicating potential abuse.
7.4. Security & Access Controls
Restrict KV read/write permissions to the Worker’s service token only. Avoid exposing the KV namespace ID publicly. For added security, encrypt the stored bucket state using a per‑account secret, similar to the approach described in the OpenAI ChatGPT integration guide.
7.5. Leveraging Workflow Automation
Automate KV cleanup for stale API keys using Cloudflare’s Workflow automation studio. A nightly workflow can scan keys older than 90 days and purge them, keeping the KV store lean.
7.6. Cost Optimisation
KV pricing is based on reads, writes, and stored bytes. Batch writes when possible, and consider a UBOS pricing plans that include generous KV quotas if your traffic scales dramatically.
8. Conclusion
Persisting the OpenClaw Rating API token‑bucket state with Cloudflare Workers KV transforms a simple edge rate limiter into a robust, production‑ready component. By following the step‑by‑step guide, leveraging KV’s global replication, and applying the best‑practice checklist, you can protect your API from abuse while maintaining sub‑millisecond latency at the edge.
For teams building AI‑enhanced products, the same pattern can be reused for any ChatGPT and Telegram integration or other generative‑AI workloads that require per‑user throttling.
9. Further Reading
- Cloudflare Workers KV Documentation
- Token Bucket Algorithm (Wikipedia)
- Original OpenClaw Rating API announcement
- Enterprise AI platform by UBOS
- UBOS for startups