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.

PlanPriceDP/dayDP/monthConcurrent
Free$0500 DP5,000 DP5
Starter$9/mo5,000 DP100,000 DP20
ProMost Popular$29/mo25,000 DP500,000 DP100
Business$99/mo100,000 DP2,000,000 DPUnlimited
Enterprise$399/mo500,000 DP10,000,000 DPUnlimited

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:

HeaderDescriptionExample
X-RateLimit-LimitYour daily DP limit.10000
X-RateLimit-RemainingData Points remaining today.9847
X-RateLimit-ResetUnix timestamp (UTC) when the limit resets.1710374400
X-RateLimit-PolicyRate limit policy (always "day").day
Retry-AfterSeconds 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?

Upgrade your plan at any time from the dashboard. Changes take effect immediately — no restart required.