Rate Limits
KalimaLab uses a Data Points (DP) billing model. Each API call costs a certain number of DP based on the data returned. Learn how limits work and how to build resilient applications.
DP budget by plan
DP budgets are applied per API key. All budgets reset at midnight UTC. Common words served from cache cost 0 DP.
| Plan | Price | DP/day | DP/month | Concurrent |
|---|---|---|---|---|
| Free | $0 | 500 DP | 5,000 DP | 5 |
| Starter | $9/mo | 5,000 DP | 100,000 DP | 20 |
| ProMost Popular | $29/mo | 25,000 DP | 500,000 DP | 100 |
| Business | $99/mo | 100,000 DP | 2,000,000 DP | Unlimited |
| Enterprise | $399/mo | 500,000 DP | 10,000,000 DP | Unlimited |
Need higher limits? Contact us at enterprise@kalimalab.com for custom plans.
Rate limit response headers
Every API response includes DP usage headers so you can track your budget in real time:
| Header | Description | Example |
|---|---|---|
| X-RateLimit-Limit | Your daily DP limit. | 10000 |
| X-RateLimit-Remaining | Data Points remaining today. | 9847 |
| X-RateLimit-Reset | Unix timestamp (UTC) when the limit resets. | 1710374400 |
| X-RateLimit-Policy | Rate limit policy (always "day"). | day |
| Retry-After | Seconds to wait before retrying (only present on 429). | 3600 |
const response = await fetch('https://api.kalimalab.com/v1/words/random', { headers: { Authorization: `Bearer ${process.env.KALIMALAB_API_KEY}` },})const remaining = response.headers.get('X-RateLimit-Remaining')const resetAt = response.headers.get('X-RateLimit-Reset')console.log(`${remaining} DP remaining today`)console.log(`Budget resets at ${new Date(Number(resetAt) * 1000).toISOString()}`)Handling 429 Too Many Requests
When you exhaust your daily DP budget, the API returns HTTP 429 with this response body:
{ "data": null, "error": { "code": "ERR_RATE_LIMIT_EXCEEDED", "message": "Daily DP budget exhausted. Your budget resets at 2026-03-18T00:00:00Z.", "limit": 500, "remaining": 0, "resetAt": "2026-03-18T00:00:00Z" }, "meta": { "requestId": "req_01j9..." }}Handling 429 gracefully with the SDK
import { KalimaLab, RateLimitError } from 'kalimalab'const client = new KalimaLab({ apiKey: process.env.KALIMALAB_API_KEY! })async function fetchWord() { try { return await client.words.random() } catch (err) { if (err instanceof RateLimitError) { const resetMs = err.resetAt.getTime() - Date.now() console.warn(`Rate limit hit. Resets in ${Math.ceil(resetMs / 1000 / 60)} minutes.`) console.warn(`Daily limit: ${err.limit} requests`) // Return cached data, queue for later, or surface to user return null } throw err }}Handling 429 with native fetch
async function fetchWithRateLimitHandling(url: string) { const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.KALIMALAB_API_KEY}` }, }) if (res.status === 429) { const retryAfter = res.headers.get('Retry-After') const waitSeconds = retryAfter ? Number(retryAfter) : 3600 console.warn(`Rate limited. Try again in ${waitSeconds}s.`) return null } return res.json()}Best practices for staying within your DP budget
Leverage the 0-DP cache
The top 5,000 most-queried Arabic words are cached server-side. Basic-tier lookups for these words cost 0 DP. Build your most common lookups around this cache.
Choose the right tier
Use the basic tier (1 DP) for autocomplete and discovery. Reserve enriched (8 DP) and full (20 DP) tiers for detailed views where the user explicitly requests more data.
Use bulk endpoints
POST /v1/words/bulk processes up to 50 lemmas per request. Batch lookups instead of calling the single-word endpoint repeatedly.
Debounce autocomplete
Add a 150–300ms debounce to /v1/words/autocomplete calls. Autocomplete costs 0.5 DP per result — debouncing prevents unnecessary spend on partial keystrokes.
Monitor your DP usage
Check X-RateLimit-Remaining in responses and log a warning when you drop below 10% of your daily budget. The dashboard also shows per-key DP consumption.
✓Need a bigger DP budget?