Search docs

Quotas and errors

Monthly quotas per plan, the X-Quota headers, rate limits, and every error status and body of api.geolith.app.

By ยท Updated

Each plan has a monthly request quota and a per-second rate limit per key. Over quota, requests stop with 429 quota_exceeded until the month ends or you upgrade. There are no overage charges.

Quotas

PlanPriceRequests a month
Free$0100,000
Starter$19/month1,000,000
Growth$99/month7,500,000
Scale$299/month25,000,000

Every metered endpoint counts as one request: search, autocomplete, structured, reverse, nearby, place, TileJSON and tiles. GET /health and OPTIONS preflight requests are free. The month is the calendar month in UTC. Card checkout is not live yet; for a paid plan, email contact@geolith.app.

Every answered request carries two headers:

HeaderMeaning
X-Quota-LimitRequests a month on your plan
X-Quota-RemainingRequests left this month

Errors

Key, origin, rate and quota errors are JSON with a stable code:

{ "error": { "code": "missing_api_key", "message": "Pass an API key as ?api_key= or an Authorization: Bearer header." } }
StatusCodeWhenWhat to do
400(geocoding.errors)A parameter is missing or invalidFix the request
401missing_api_keyNo keySend api_key or Authorization: Bearer
401invalid_api_keyMalformed, unknown or revoked keyCheck the key in the dashboard
403origin_not_allowedPublishable key from an origin it does not listAdd the origin, or use a secret key on a server
404not_foundUnknown path or tile setCheck the path
405method_not_allowedNot GET, HEAD or OPTIONSUse GET
429rate_limitedToo many requests per second on one keyWait for Retry-After (1 s) and retry
429quota_exceededThe monthly quota is used upWait for the next month or upgrade
502(geocoding.errors)The index could not be readRetry later

Parameter errors keep the GeoJSON shape, so a client that reads features gets an empty list:

{ "geocoding": { "errors": ["missing param 'text'"] }, "type": "FeatureCollection", "features": [] }

Retrying

Retry 429 rate_limited and 502 with a short back-off. Do not retry 400, 401, 403 or 429 quota_exceeded.

async function geolith(url, init, tries = 4) {
  for (let i = 0; i < tries; i++) {
    const res = await fetch(url, init);
    if (res.status === 429) {
      const body = await res.clone().json();
      if (body.error?.code === 'quota_exceeded') return res;
      await new Promise((r) => setTimeout(r, Number(res.headers.get('Retry-After') ?? 1) * 1000));
      continue;
    }
    if (res.status >= 500) {
      await new Promise((r) => setTimeout(r, 500 * 2 ** i));
      continue;
    }
    return res;
  }
  throw new Error('geolith: gave up after retries');
}