| name | lokalise-rate-limits |
| description | Implement Lokalise rate limiting, backoff, and request queuing patterns.
Use when handling rate limit errors, implementing retry logic,
or optimizing API request throughput for Lokalise.
Trigger with phrases like "lokalise rate limit", "lokalise throttling",
"lokalise 429", "lokalise retry", "lokalise backoff".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Lokalise Rate Limits
Overview
Handle Lokalise rate limits gracefully with request queuing, exponential backoff, and monitoring.
Prerequisites
- Lokalise SDK installed
- Understanding of async/await patterns
- Access to rate limit headers
Rate Limit Specifications
Current Limits (2025)
| Limit Type | Value | Scope |
|---|
| Requests per second | 6 | Per API token + IP |
| Concurrent requests | 10 | Per project |
| Concurrent per token | 1 | Per token (data consistency) |
| Keys per list request | 500 | Per request |
| Keys per bulk create | 500 | Per request |
Rate Limit Headers
X-RateLimit-Limit: 6
X-RateLimit-Remaining: 5
X-RateLimit-Reset: 1640000000
Instructions
Step 1: Implement Request Queue
import PQueue from "p-queue";
const queue = new PQueue({
concurrency: 5,
interval: 1000,
intervalCap: 5,
carryoverConcurrencyCount: true,
});
export async function queuedRequest<T>(
operation: () => Promise<T>
): Promise<T> {
return queue.add(operation) as Promise<T>;
}
queue.on("active", () => {
console.log(`Queue: ${queue.size} waiting, ${queue.pending} running`);
});
Step 2: Add Exponential Backoff with Jitter
interface BackoffConfig {
maxRetries: number;
baseDelayMs: number;
maxDelayMs: number;
jitterMs: number;
}
const defaultConfig: BackoffConfig = {
maxRetries: 5,
baseDelayMs: 1000,
maxDelayMs: 32000,
jitterMs: 500,
};
export async function withExponentialBackoff<T>(
operation: () => Promise<T>,
config = defaultConfig
): Promise<T> {
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
try {
return await operation();
} catch (error: any) {
const isRetryable = error.code === 429 ||
(error.code >= 500 && error.code < 600);
if (!isRetryable || attempt === config.maxRetries) {
throw error;
}
exponentialDelay = config. * .(, attempt);
jitter = .() * config.;
delay = .(exponentialDelay + jitter, config.);
retryAfter = error.?.[];
actualDelay = retryAfter
? (retryAfter) *
: delay;
.();
( (r, actualDelay));
}
}
();
}
Step 3: Create Rate-Aware Client Wrapper
import { LokaliseApi, ApiError } from "@lokalise/node-api";
export class RateLimitedLokaliseClient {
private client: LokaliseApi;
private queue: PQueue;
private rateLimitRemaining = 6;
private rateLimitReset = 0;
constructor(apiKey: string) {
this.client = new LokaliseApi({ apiKey });
this.queue = new PQueue({
concurrency: 5,
interval: 1000,
intervalCap: 5,
});
}
async request<T>(operation: () => Promise<T>): Promise<T> {
if (this.shouldThrottle()) {
const waitTime = this.getWaitTime();
console.log(`Proactive throttle: waiting ms`);
( (r, waitTime));
}
..(
( () => {
{
result = ();
result;
} (: ) {
.(error);
error;
}
})
) <T>;
}
(): {
. < && .() < .;
}
(): {
.(, . - .());
}
() {
(error. === ) {
. = ;
. = .() + ;
}
}
() {
{
: .( ..().()),
: .( ..().(id)),
};
}
() {
{
: .( ..().(params)),
: .( ..().(params)),
};
}
}
Step 4: Implement Batch Processing
async function batchProcess<T, R>(
items: T[],
operation: (item: T) => Promise<R>,
batchSize = 100
): Promise<R[]> {
const results: R[] = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(item => queuedRequest(() => operation(item)))
);
results.push(...batchResults);
console.log(`Processed ${Math.min(i + batchSize, items.length)}/${items.length}`);
}
return results;
}
Output
- Request queue respecting 6 req/sec limit
- Automatic retry with exponential backoff
- Proactive throttling when quota low
- Batch processing for large operations
Error Handling
| Header | Description | Action |
|---|
| X-RateLimit-Limit | Max requests per window | Monitor usage |
| X-RateLimit-Remaining | Remaining requests | Throttle if low |
| X-RateLimit-Reset | Unix timestamp of reset | Wait until reset |
| Retry-After | Seconds to wait (on 429) | Honor this value |
Examples
CLI with Rate Limiting
#!/bin/bash
lokalise_request() {
local result
result=$(lokalise2 "$@" 2>&1)
local exit_code=$?
if echo "$result" | grep -q "429"; then
echo "Rate limited, waiting 2 seconds..."
sleep 2
lokalise_request "$@"
else
echo "$result"
return $exit_code
fi
}
lokalise_request --token "$TOKEN" project list
Monitor Rate Limit Usage
class RateLimitMonitor {
private requests: number[] = [];
private readonly windowMs = 1000;
private readonly limit = 6;
track() {
const now = Date.now();
this.requests = this.requests.filter(t => now - t < this.windowMs);
this.requests.push(now);
}
getCurrentRate(): number {
const now = Date.now();
return this.requests.filter(t => now - t < this.windowMs).length;
}
shouldWait(): boolean {
return this.getCurrentRate() >= this.limit;
}
getStats() {
return {
: .(),
: .,
: (.() / .) * ,
};
}
}
Bulk Operations with Progress
async function bulkCreateKeys(
projectId: string,
keys: any[],
onProgress?: (completed: number, total: number) => void
) {
const client = new RateLimitedLokaliseClient(process.env.LOKALISE_API_TOKEN!);
const batchSize = 100;
const results: any[] = [];
for (let i = 0; i < keys.length; i += batchSize) {
const batch = keys.slice(i, i + batchSize);
const result = await client.keys().create({
project_id: projectId,
keys: batch,
});
results.push(...result.items);
onProgress?.(Math.min(i + batchSize, keys.length), keys.length);
await new Promise(r => setTimeout(r, 200));
}
return results;
}
Resources
Next Steps
For security configuration, see lokalise-security-basics.