Skip to main content

Handle rate limits and pagination

The published limits​

APIRate limitNotes
Products10,000 calls/dayEnforced per endpoint, not per operation
Locations1,600 calls/day per endpointThree endpoints, each with its own budget

You can distribute your daily budget across operations however you like. For most applications, it makes sense to cache location lookups since stores do not move, leaving more of the budget for product searches.

Pagination​

Product search returns 10 results per page by default. You can control paging with two parameters:

products = kroger.product.search_products(
term="cereal",
location_id=location_id,
limit=50, # filter.limit: results per response
start=50, # filter.start: results to skip
)

Design around these two caveats:

  • Term search is fuzzy. The order of results may change between requests. Do not rely on page N and page N+1 to be stable. If you need a consistent snapshot, use a larger limit (up to the Products API's maximum of 50, with start capped at 250) in a single request.
  • The Locations API does not paginate. To get more results, increase limit (up to 200) and widen radius_in_miles. A larger limit inside the default 10-mile radius may not return enough results to fill the limit.

Batch product lookups through kroger-mcp​

If you use the MCP server, bulk_search_products can run up to 25 searches in a single tool call. This reduces round trips through the agent approval loop while keeping the cost against your daily rate limit the same.

Stop when the limit is reached​

The API returns HTTP 429 when your budget is exhausted, and kroger-api raises RateLimitError. This is a daily limit rather than a temporary issue, so exponential backoff will not work. Log the count, stop the run, and resume after the budget resets.

from kroger_api import RateLimitError

try:
products = kroger.product.search_products(term="cereal", location_id=location_id)
except RateLimitError as e:
print(f"Daily rate limit reached for {e.url}. Resume after it resets.")
if e.retry_after is not None:
print(f"The API asked for a wait of {e.retry_after:g} seconds.")
raise

retry_after holds the Retry-After header in seconds when the API sends one. Through kroger-mcp, the same response is an error result of type rate_limited, whose next step tells the agent to stop instead of retrying.