The Keepa API offers numerous endpoints, which are explained on the individual pages of this documentation (see the Endpoints section in the sidebar, or the overview).
Official API Clients#
Keepa provides clients for Java and PHP for fast integration. Even if you do not use Java or PHP, these resources might still be helpful for insights and examples. There is also a community-developed Python client available.
Price Plans#
Requests consume tokens from your subscription's token bucket. Find out how the Keepa API price plans and the token system work on the Plans & Tokens page.
Request Basics#
| Aspect | Requirement |
|---|---|
| Base URL | https://api.keepa.com/ |
| API Access Key | Every request requires your API access key as a parameter (key). You can find and change your key on the Keepa website. |
| HTTP Method | All requests are issued over HTTPS and must accept gzip encoding; zstd is supported as well and recommended (see Response compression). GET works for every endpoint. Endpoints that take a large JSON query — Product Finder, Seller Finder, Browsing Deals and Add Tracking — additionally accept POST, which avoids URL-encoding the query and the URL length limit. If possible, use a Keep-Alive connection. |
| Parallel Requests | Multiple requests can be made in parallel to increase throughput. |
Response compression#
Responses are always compressed. gzip is the baseline that every client must support; zstd is supported as well and is the recommended choice — it is smaller than gzip and faster to decompress.
The coding is selected from the Accept-Encoding request header:
- Send
Accept-Encoding: gzip, zstdto receive zstd. The token must be listed explicitly — a wildcard (*) does not enable it, andzstd;q=0is honored as a refusal. - Anything else, including no
Accept-Encodingheader at all, is served gzip.
Always decode based on the Content-Encoding response header rather than assuming a coding: a request that advertises zstd can still be answered with gzip. Most HTTP clients handle both transparently (curl with --compressed, Python requests, browser fetch); older ones may need an extra package for zstd (for example pip install "urllib3[zstd]"). If your client cannot decode zstd, simply keep advertising gzip only.
Response Format#
The response is always in JSON format. Every response will contain information about your current API access:
{
"refillRate": Integer,
"refillIn": Integer,
"tokensLeft": Integer,
"tokensConsumed": Integer,
"tokenFlowReduction": Double,
"processingTimeInMs": Integer,
"error": {
"type": String,
"message": String,
"details": String
}
}
Response fields#
| Field | Type | Description |
|---|---|---|
refillRate |
Integer | Number of tokens generated by your active subscriptions. |
refillIn |
Integer | Time in milliseconds until your token bucket will be refilled (happens every minute). |
tokensLeft |
Integer | How many tokens you currently have left. This value can be negative, as requests are always executed as long as there is a positive token balance when the request is submitted. |
tokensConsumed |
Integer | Number of tokens this request consumed. |
tokenFlowReduction |
Double | Tracking products through the API reduces your actual token refill rate by this value (rounded to an integer). |
processingTimeInMs |
Integer | Time the request's server side processing took, in milliseconds. |
error |
Object | Contains error information (type, message, details — all String). Only set if an error occurred. An error does not mean the whole request failed (e.g., batch requests). |
Note: You can use this information to plan your requests. Unused tokens will expire after 60 minutes.
Possible HTTP Status Codes#
| Code | Status | Meaning |
|---|---|---|
200 | OK | The request was successfully executed. |
400 | Bad Request | The request was malformed or could not be successfully executed. |
402 | Payment Required | The used API key does not grant access. |
405 | Method Not Allowed | A request parameter is out of the allowed range. |
429 | Too Many Requests | You are out of tokens. |
500 | Internal Server Error | An unexpected error occurred when executing this request. |
Note: For any status code other than 200, you will still receive the token bucket information in the error stream. The remainder of the response varies and is explained on the individual endpoint pages.
Examples#
A good first request is a minimal Product Request for a single ASIN: it costs a single token and — like every API response — returns your current token status in the response envelope, so you can verify that your key works and see how many tokens you have left.
https://api.keepa.com/product?key=YOUR_API_KEY&domain=1&asin=B0F3GWXLTS
KeepaAPI api = new KeepaAPI("YOUR_API_KEY");
Request r = Request.getProductRequest(AmazonLocale.US, null, null, "B0F3GWXLTS");
api.sendRequest(r)
.done(result -> {
if (result.status == ResponseStatus.OK) {
System.out.println(result.products[0].title);
}
})
.fail(failure -> System.out.println(failure));<?php
use Keepa\KeepaAPI;
use Keepa\API\Request;
use Keepa\API\ResponseStatus;
use Keepa\objects\AmazonLocale;
$api = new KeepaAPI("YOUR_API_KEY");
$response = $api->sendRequestWithRetry(Request::getProductRequest(AmazonLocale::US, null, null, null, 1, true, ['B0F3GWXLTS']));
if ($response->status == ResponseStatus::OK) {
echo $response->products[0]->title;
}import requests
# A Session reuses the connection (keep-alive); requests decodes gzip transparently.
session = requests.Session()
params = {"key": "YOUR_API_KEY", "domain": 1, "asin": "B0F3GWXLTS"}
r = session.get("https://api.keepa.com/product", params=params)
data = r.json()
print("Tokens left:", data["tokensLeft"])
print("Title:", data["products"][0]["title"])// fetch keeps the connection alive and handles gzip automatically.
const res = await fetch("https://api.keepa.com/product?key=YOUR_API_KEY&domain=1&asin=B0F3GWXLTS");
const data = await res.json();
console.log("Tokens left:", data.tokensLeft);
console.log("Title:", data.products[0].title);curl -s --compressed "https://api.keepa.com/product?key=YOUR_API_KEY&domain=1&asin=B0F3GWXLTS"Example response#
Every response starts with the token bucket envelope described above; the endpoint's payload (here: the products array of the Product Request) follows alongside it. The error field is absent because no error occurred.
{
"processingTimeInMs": 32,
"tokensLeft": 279,
"refillIn": 41565,
"refillRate": 5,
"tokenFlowReduction": 0.0,
"tokensConsumed": 1,
"products": [
{
"asin": "B0F3GWXLTS",
"domainId": 1,
"title": "Example Product, 16 oz"
}
]
}
Response shortened for readability; illustrative values. See the Product Object for the full payload structure.
Example error response#
If a request fails (any status code other than 200), the token bucket information is still delivered in the error stream, together with the error object. In this example the token balance is exhausted, so the request is answered with HTTP 429 - Too Many Requests. Note that tokensLeft can be negative:
{
"processingTimeInMs": 2,
"tokensLeft": -3,
"refillIn": 12844,
"refillRate": 5,
"tokenFlowReduction": 0.0,
"tokensConsumed": 0,
"error": {
"type": "documentationExample",
"message": "You are out of tokens.",
"details": "Wait until your token bucket refills."
}
}
Illustrative values — the actual error type, message and details strings vary by endpoint and error condition.
Additional Information#
- Available Requests: View all available requests