Optimize Lokalise API performance with caching, pagination, and bulk operations.
Use when experiencing slow API responses, implementing caching strategies,
or optimizing request throughput for Lokalise integrations.
Trigger with phrases like "lokalise performance", "optimize lokalise",
"lokalise latency", "lokalise caching", "lokalise slow", "lokalise batch".
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Optimize Lokalise API performance with caching, pagination, and bulk operations.
Use when experiencing slow API responses, implementing caching strategies,
or optimizing request throughput for Lokalise integrations.
Trigger with phrases like "lokalise performance", "optimize lokalise",
"lokalise latency", "lokalise caching", "lokalise slow", "lokalise batch".
Designed for Claude Code, also compatible with Codex and OpenClaw
Lokalise Performance Tuning
Overview
Optimize Lokalise API throughput for translation pipelines by implementing cursor pagination, local caching, batch key operations (500/request), request throttling under the 6 req/s rate limit, and selective language downloads.
Prerequisites
@lokalise/node-api SDK v9+ (ESM) or REST API access
LOKALISE_API_TOKEN environment variable set
Understanding of project size (key count, language count) to calibrate batch sizes
Optional: Redis or LRU cache library for persistent caching
Instructions
Step 1: Use Cursor Pagination for Large Datasets
Cursor pagination is significantly faster than offset pagination for projects with 5K+ keys. Offset pagination degrades as page numbers increase because the server must skip rows; cursor pagination uses a pointer.
import { LokaliseApi } from'@lokalise/node-api';
const lok = newLokaliseApi({ apiKey: process.env.LOKALISE_API_TOKEN! });
// Generator that yields all keys using cursor paginationasyncfunction* getAllKeys(projectId: string) {
letcursor: string | undefined;
do {
const result = await lok.keys().list({
project_id: projectId,
limit: 500, // Maximum allowed per requestpagination: 'cursor',
cursor,
});
for ( key result.) key;
cursor = result.() ? result. : ;
} (cursor);
}
count = ;
( key ()) {
count++;
}
.();
const
of
items
yield
hasNextCursor
nextCursor
undefined
while
// Usage: 10,000 keys = 20 API calls (vs 100 with default limit=100)
let
0
for
await
const
of
getAllKeys
'PROJECT_ID'
console
log
`Fetched ${count} keys`
Offset pagination comparison (avoid for large projects):
Keys
Offset (limit=100)
Cursor (limit=500)
Time saved
1,000
10 requests
2 requests
80%
10,000
100 requests
20 requests
80%
50,000
500 requests (~84s)
100 requests (~17s)
80%
Step 2: Cache Translation Downloads Locally
Translation file downloads are the most expensive Lokalise operation. Cache them locally and use project last_activity timestamps to invalidate.
Lokalise supports creating, updating, and deleting up to 500 keys per request. Always batch instead of making individual requests.
// Bulk create keys — 500 per batch with rate limit awarenessasyncfunctioncreateKeysBatched(projectId: string, keys: any[]) {
constBATCH_SIZE = 500;
const results = [];
for (let i = 0; i < keys.length; i += BATCH_SIZE) {
const batch = keys.slice(i, i + BATCH_SIZE);
const result = await lok.keys().create({
project_id: projectId,
keys: batch,
});
results.push(...result.items);
console.log(`Batch ${Math.floor(i / BATCH_SIZE) + 1}: created ${result.items.length} keys`);
awaitnewPromise(r =>setTimeout(r, 200)); // Stay under 6 req/s
}
return results;
}
// Bulk update keys — same 500-key batch limitasyncfunctionupdateKeysBatched(projectId: string, updates: Array<{key_id: number; [k: string]: any}>) {
constBATCH_SIZE = 500;
for (let i = 0; i < updates.length; i += BATCH_SIZE) {
const batch = updates.slice(i, i + BATCH_SIZE);
await lok.keys().bulk_update({
project_id: projectId,
keys: batch,
});
awaitnewPromise(r =>setTimeout(r, 200));
}
}
// Bulk delete — up to 500 key IDs per requestasyncfunctiondeleteKeysBatched(projectId: string, keyIds: number[]) {
constBATCH_SIZE = 500;
for (let i = 0; i < keyIds.length; i += BATCH_SIZE) {
const batch = keyIds.slice(i, i + BATCH_SIZE);
await lok.keys().bulk_delete({
project_id: projectId,
keys: batch,
});
awaitnewPromise(r =>setTimeout(r, 200));
}
}
// 2,000 keys: 4 batched requests instead of 2,000 individual ones
Step 4: Implement Request Throttling
A proper request queue prevents 429 Too Many Requests errors and makes your integration resilient under load.
importPQueuefrom'p-queue';
// Lokalise rate limit: 6 requests/second// Use 5 concurrent with 1s interval for safety marginconst queue = newPQueue({
concurrency: 5,
interval: 1000,
intervalCap: 5,
});
asyncfunction throttledRequest<T>(fn: () =>Promise<T>): Promise<T> {
return queue.add(fn) asPromise<T>;
}
// All API calls go through the queue automaticallyconst project = awaitthrottledRequest(() => lok.projects().get(projectId));
const keys = awaitthrottledRequest(() => lok.keys().list({
project_id: projectId,
limit: 500,
pagination: 'cursor',
}));
// Works for parallel operations too — queue enforces the rate limitconst projectIds = ['PROJ_1', 'PROJ_2', 'PROJ_3', 'PROJ_4', 'PROJ_5'];
const allProjects = awaitPromise.all(
projectIds.map(id =>throttledRequest(() => lok.projects().get(id)))
);
Step 5: Async File Operations with Webhooks
File uploads and downloads are processed asynchronously by Lokalise. Instead of polling the process status endpoint, use webhooks to get notified when processing completes.
set -euo pipefail
# Set up a webhook for file operation events
curl -s -X POST "https://api.lokalise.com/api2/projects/${PROJECT_ID}/webhooks" \
-H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.company.com/lokalise",
"events": [
"project.imported",
"project.exported",
"project.keys_added"
]
}' | jq '{webhook_id: .webhook.webhook_id, url: .webhook.url, events: .webhook.events}'
Step 6: Selective Language Downloads (Delta Exports)
Downloading all languages when you only need one wastes bandwidth and API time. Always filter by language and, when possible, by modification timestamp.
// Download only changed translations since last syncasyncfunctiondownloadDelta(projectId: string, langIso: string, sinceTimestamp: string) {
// Filter keys modified after the given timestampconst keys = await lok.keys().list({
project_id: projectId,
limit: 500,
pagination: 'cursor',
filter_translation_lang_ids: langIso,
// Unfortunately, Lokalise doesn't support filter_modified_after on keys endpoint.// Workaround: download full file and diff locally, or use webhooks for real-time sync.
});
return keys.items;
}
// Download a single language file instead of all languagesasyncfunctiondownloadSingleLanguage(projectId: string, langIso: string) {
const result = await lok.files().download(projectId, {
format: 'json',
filter_langs: [langIso], // Only this languageoriginal_filenames: false, // Flat structurebundle_structure: '%LANG_ISO%.%FORMAT%', // e.g., fr.jsonexport_empty_as: 'base', // Fall back to base language for untranslatedinclude_tags: ['production'], // Only production-tagged keys
});
return result.bundle_url;
}
Cursor pagination implemented for all list operations (80% fewer API calls)
Translation download cache with timestamp-based invalidation
Batch key operations (create/update/delete) using 500-key batches
Request queue with PQueue throttling at 5 req/s (safety margin under 6 req/s limit)
Webhooks configured for async file operations (replacing polling)
Selective language downloads reducing bandwidth and processing time
Benchmark results for baseline performance measurement
Error Handling
Issue
Cause
Solution
429 Too Many Requests
Exceeded 6 req/s global rate limit
Use PQueue throttling (Step 4), retry with exponential backoff
Slow file downloads
Large project with 50+ languages
Filter by filter_langs to download one language at a time
Pagination timeout
Offset pagination on 50K+ key projects
Switch to cursor pagination (Step 1)
Bulk create partial failure
Network timeout on large batch
Reduce batch size from 500 to 200 and add retry logic per batch
Cache stale after team edits
last_activity not granular enough
Reduce cache TTL or use webhooks to invalidate on project.translation_updated
bundle_url expired
Download URL only valid for ~30 minutes
Fetch the URL and download immediately; do not store URLs for later
Examples
Quick Rate Limit Check
set -euo pipefail
# See how much rate limit headroom you have right now
curl -s -D - -o /dev/null \
-H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
"https://api.lokalise.com/api2/system/languages?limit=1" 2>/dev/null \
| grep -iE 'x-ratelimit' | whileread -r line; doecho" $line"; done