Maps

Handling errors, rate limits, and quotas

Catch the right error type, back off correctly, and tell a transient rate limit apart from a usage cap that will not clear on its own.

Maimaps fails in a handful of well-defined ways. This guide covers what each one means and what you should actually do about it, which is different in every case.

The shape of a failure

Every error comes back in the same envelope as a success, with status: false and a machine-readable code:

{
  "status": false,
  "code": "rate_limited",
  "message": "Per-key rate limit hit. Retry after 2 seconds."
}

The SDKs paper over this: the gateway codes become typed errors, and everything else surfaces as a MaimapsError with code and httpStatus intact.

Catch by type

import {
  InvalidApiKeyError,
  RateLimitError,
  QuotaExceededError,
  ServerError,
  NetworkError,
  MaimapsError,
} from "@maimaps/js";

try {
  const results = await client.search({ q: "garki market" });
} catch (err) {
  if (err instanceof RateLimitError) {
    // Transient. Back off and try again.
    await sleep((err.retryAfterSeconds ?? 1) * 1000);
  } else if (err instanceof QuotaExceededError) {
    // NOT transient. Retrying will not help.
    showUpgradePrompt();
  } else if (err instanceof InvalidApiKeyError) {
    // Your key is wrong, revoked, or for the wrong product.
    logAndAlertOps(err);
  } else if (err instanceof NetworkError) {
    // Never got a response at all. Offline, DNS, timeout.
    showOfflineBanner();
  } else if (err instanceof ServerError) {
    // 5xx. Safe to retry an idempotent call after a backoff.
  } else if (err instanceof MaimapsError) {
    // Everything else: ROUTING_FAILED, ENGINE_UNAVAILABLE, INVALID_PARAMS…
    console.error(err.code, err.httpStatus, err.message);
  }
}

Rate limit is not quota

These are the two that get conflated, and they call for opposite responses.

RateLimitError is transient. You sent requests too fast. The per-key limits are 10 requests per second for search, geocoding, point details, and loccodes, and 5 per second for routing, each with a burst allowance on top. Wait for retryAfterSeconds and try again. It will work.

QuotaExceededError is not transient. A usage cap set on the key has been reached. Retrying will not help until the cap is raised or reset. Show the user something useful, and alert yourself.

Note that running out of your monthly allowance does not raise this. Those requests are not blocked; they start billing from your credit wallet at the published Maimaps rates. What you run out of eventually is credits, not allowance.

Routing failures

Routing has two failure modes that mean genuinely different things:

  • 422 ROUTING_FAILED. The engine ran fine but could not find a path between those two points. Usually the origin or destination is somewhere with no routable road nearby. This is a real answer, not an outage. Tell the user no route exists.
  • 502 ENGINE_UNAVAILABLE. The map engine is unreachable, or a loccode origin or destination could not be resolved. The API does not currently distinguish these two. If you passed a loccode, validate it with loccode.resolve before blaming the engine.

Read the rate-limit headers

Every limited route returns them, on success as well as on failure:

X-RateLimit-Limit: 10
X-RateLimit-Remaining: 3
X-RateLimit-Reset: 1752403200
Retry-After: 2          (on 429 only)

If you are building a batch job, watch X-RateLimit-Remaining and slow down before you hit zero, rather than sprinting into a wall of 429s.

In Flutter

The exception hierarchy mirrors the JS one exactly:

try {
  final results = await Maimaps.instance.search.search('garki market');
} on RateLimitException catch (e) {
  await Future.delayed(e.retryAfter ?? const Duration(seconds: 1));
} on QuotaExceededException {
  showUpgradePrompt();
} on InvalidApiKeyException {
  logAndAlertOps();
} on MaimapsNetworkException {
  showOfflineBanner();
} on MaimapsException catch (e) {
  debugPrint('${e.code} ${e.statusCode}: ${e.message}');
}

Order matters: catch the specific types before the base MaimapsException, or the base will swallow everything.

What's next

  • The Maimaps API reference has the full error table, including which codes come from the gateway and which from the service.
  • Watch your usage against the caps in Analytics.