Sometimes. The Amazon search API geo_location parameter sets the delivery postcode for one /amazon-search call on the SellerMagnet API, and page one is loaded for that address. It is best effort: when Amazon refuses or loses the postcode, the call still returns 200 and costs one credit, with results from another location, and nothing in the response says so. Below: what the parameter does, when it is ignored, and a script that measures whether a postcode changes page one.
Key Takeaways
geo_locationtakes a ZIP code or postcode for one search; leave it out and the API asks Amazon for the marketplace's default postcode, 20001 on amazon.com.- It is best effort: if Amazon refuses or loses the postcode, the search still runs and answers a billed 200 with results from another location.
- No field in the response says which location was used, and the API does not check the postcode, so a typo is never an error.
- An empty
geo_location=is not the default: it skips the postcode step completely. - To see whether a postcode changes page one, search it at least twice next to the default location and count only products that stay in or stay out.
What does geo_location do in the Amazon search API?
Amazon decides part of what a shopper sees by the delivery address; its US help pages say shoppers "may see different Featured Offers when shopping from different shipping locations". geo_location lets one search ask for a specific address. You pass a ZIP code or postcode as text. It always goes to Amazon as a postcode; there is no country or city option. The API hands it to its scraper unchanged, the scraper sets it as the delivery postcode on Amazon, then loads the results page for your keyword and returns page one as JSON. The other parameters, q, marketplaceId and count (default 30, at most 50), are in the API documentation.
The scraper tries up to three times to set the postcode. It counts the postcode as set when Amazon confirms it, and also when Amazon only calls the address valid without saying which postcode it set; that second case is not checked any further. Here is a response for geo_location=99501. Look for the location in it: there is none.
Response of /amazon-search for q=usb c cable, amazon.com, geo_location=99501 (illustrative products; keys and shape as the API returns them)
{
"data": {
"searchResults": [
{
"asin": "B0GEOCAB01",
"discount_info": "",
"link": "https://www.amazon.com/dp/B0GEOCAB01",
"listingPrice": {
"price": {
"currency_code": "USD",
"currency_name": "United States Dollar",
"currency_symbol": "$",
"total": "12.99"
}
},
"mainImage": "https://m.media-amazon.com/images/I/B0GEOCAB01.jpg",
"on_sale": false,
"position": 1,
"productTitle": "USB C Cable 6 ft, 2-Pack",
"reviewAmount": 1037,
"reviewRating": 4.1,
"sponsored": true
},
{
"asin": "B0GEOCAB02",
"discount_info": "",
"link": "https://www.amazon.com/dp/B0GEOCAB02",
"listingPrice": {
"price": {
"currency_code": "USD",
"currency_name": "United States Dollar",
"currency_symbol": "$",
"total": "8.49"
}
},
"mainImage": "https://m.media-amazon.com/images/I/B0GEOCAB02.jpg",
"on_sale": false,
"position": 2,
"productTitle": "USB C to USB C Cable 3 ft",
"reviewAmount": 1074,
"reviewRating": 4.2,
"sponsored": false
}
]
},
"success": true
}
When does the API ignore your postcode?
In every case below the API answers HTTP 200 with the same keys and charges one credit. Only the place the results come from changes, and the response does not show it. We ran each case through the API's own search code against a simulated Amazon to confirm the table.
| Request | What happens | Results come from |
|---|---|---|
No geo_location | The marketplace's default postcode is set | The default postcode (20001 on amazon.com) |
| A postcode Amazon accepts | Set; Amazon confirms it or calls the address valid | Your postcode |
| A postcode Amazon refuses, such as a typo | 3 tries, then the search runs anyway | The location the session started with |
| The postcode change is blocked | 3 tries, then the search runs anyway | The location the session started with |
| Amazon sets a different postcode | 3 tries, then the search runs anyway | That other location |
| Amazon blocks the first page load | A backup fetch returns page one without the postcode step | The location the session started with |
| Robot checks after the postcode was set, and no retry gets through | Last resort: a new session fetches the page | A new session's location, not your postcode |
geo_location= with no value | No postcode step, not even the default | The location the session started with |
"The location the session started with" is the delivery location Amazon gives a new visitor. Amazon places it by the connection, so it is neither your postcode nor necessarily the marketplace's default postcode; it can be another city or even another country. If Amazon refuses a postcode such as 9950, the search does not fall back to 20001 either. The default postcode goes through the same three tries, so a search without geo_location can end up at the start location too.

An empty geo_location is not the default
geo_location= with no value skips the postcode step, so the search runs wherever the session started. The default postcode is used only when the parameter is left out.
Which default postcode does each marketplace use?
Leave geo_location out and the API asks Amazon for the marketplace's default postcode: the geo_location value that /amazon-get-marketplaces returns for each marketplace, all 23 in one call for one credit. amazon.ae has no default, so a search there without the parameter sets no delivery postcode at all. What each marketplace ID means is in the marketplace IDs list.
| Marketplace | Marketplace ID | Default postcode |
|---|---|---|
| amazon.com | ATVPDKIKX0DER | 20001 |
| amazon.ca | A2EUQ1WTGCTBG2 | K1A 0A1 |
| amazon.com.mx | A1AM78C64UM0Y8 | 06000 |
| amazon.com.br | A2Q3Y263D00KWC | 70040-904 |
| amazon.co.uk | A1F83G8C2ARO7P | SW1A 1AA |
| amazon.ie | A28R8C7NBKEWEA | D02 |
| amazon.de | A1PA6795UKMFR9 | 10115 |
| amazon.fr | A13V1IB3VIYZZH | 75001 |
| amazon.it | APJ6JRA9NG5V4 | 00184 |
| amazon.es | A1RKKUPIHCS9HS | 28001 |
| amazon.nl | A1805IZSGTT6HS | 1012 |
| amazon.com.be | AMEN7PMS3EDWL | 1000 |
| amazon.se | A2NODRKZP88ZB9 | 111 52 |
| amazon.pl | A1C3SOZRARQ6R3 | 00-001 |
| amazon.com.tr | A33AVAJ2PDY3EV | 06690 |
| amazon.ae | A2VIGQ35RCS4UG | none |
| amazon.sa | A17E79C6D8DWNP | 12271 |
| amazon.eg | ARBP9OOSHTCHU | 11511 |
| amazon.co.za | AE08WJ6YKNBMC | 0083 |
| amazon.co.jp | A1VC38T7YXB528 | 100-0001 |
| amazon.in | A21TJRUUN4KGV | 110001 |
| amazon.sg | A19VAU5U5O7RUS | 179431 |
| amazon.com.au | A39IBJ37TRP1C6 | 2601 |
Measure whether a postcode changes page one
A refused postcode looks exactly like an accepted one, so one search per postcode proves nothing. Even two searches for the same location can differ, because page one moves between loads. The script below uses the default location as a control. It searches the default and every postcode twice, in alternating rounds so that a change over time hits every location alike, and it leaves ads out. A product counts as a difference only when it is in both runs at one location and in neither run at the other. A postcode is reported as different only when it has more such products than flip between the two default runs on their own.

get_with_retry comes from retry.py, and ApiError and new_session from sellermagnet.py, both published in the Python quickstart. The script checks each postcode before paying for it: where the default postcode is all digits, as on amazon.com, it sends only postcodes with the same number of digits, because the API passes any value to Amazon and a typo comes back as a normal billed search. A 401 or 403 (a key or credits problem, never billed) stops the run, and so do three failed calls.
Install requests, set the key, run the check
pip install requests
export SELLERMAGNET_API_KEY="your-key-here"
python3 geo_check.py
geo_check.py: page one at the default location and at each postcode, two runs each
"""Does geo_location change Amazon search results? Page one at several postcodes, checked against a control.
geo_location is best-effort: when Amazon refuses or loses the postcode, /amazon-search still answers 200 (billed)
with the results of another location, and nothing in the response says which location was used. So one call proves
nothing. The script loads the default location and every postcode RUNS times, counts a product as a difference only
when it is in every run at one location and in no run at the other, and calls a postcode different only when it has
more such products than the default runs flip between themselves.
Helpers: sellermagnet.py and retry.py from https://sellermagnet-api.com/blog/amazon-product-api-python
"""
import requests
from retry import get_with_retry
from sellermagnet import ApiError, new_session
KEYWORD = "usb c cable"
MARKET = "ATVPDKIKX0DER" # amazon.com
POSTCODES = ["10001", "33101", "99501", "9950"] # New York, Miami, Anchorage and a typo
RUNS = 2 # per location: one run cannot tell a change from noise
COUNT = 50 # page one, priced products only; 50 is the maximum
MAX_FAILED = 3 # 502/503 are billed on every retry: stop after 3 failed calls
def page_one(session, postcode):
"""Organic ASINs on page one: ads are left out."""
params = {"q": KEYWORD, "marketplaceId": MARKET,
"count": COUNT}
if postcode: # None: the marketplace default
params["geo_location"] = postcode
data = get_with_retry(session, "amazon-search", **params)
rows = data.get("searchResults") or []
return {r["asin"] for r in rows if not r.get("sponsored")}
def sendable(postcode, default):
"""The API does not check geo_location: a typo is a billed call at some other location. Check it first."""
if default.isdigit(): # amazon.com 20001: digits only, same length
return postcode.isdigit() and len(postcode) == len(default)
return bool(postcode)
def collect(session, locations):
"""RUNS answers per location, interleaved so that a change over time hits every location alike."""
runs, failed = {loc: [] for loc in locations}, 0
for _ in range(RUNS):
for loc in locations:
try:
runs[loc].append(page_one(session, loc) or "empty") # an empty page is billed, proves nothing
except ApiError as err:
if err.status in (401, 403): # key or credits: never billed, stop
raise
runs[loc].append(f"error {err.status}")
failed += 1
except requests.RequestException as err:
runs[loc].append(type(err).__name__)
failed += 1
if failed >= MAX_FAILED:
return runs, f"stopped after {failed} failed calls"
return runs, ""
def complete(runs):
return len(runs) == RUNS and all(isinstance(r, set) for r in runs) # no failed, empty or missing run
def compare(base, other):
"""(only here, only at the default): sorted ASINs in every run of one location and in no run of the other."""
only_here = set.intersection(*other) - set.union(*base)
only_base = set.intersection(*base) - set.union(*other)
return sorted(only_here), sorted(only_base)
def flips(runs):
"""Products in some but not all runs of one location: how much page one moves on its own."""
return len(set.union(*runs) - set.intersection(*runs))
def show(asins):
return ", ".join(asins[:3]) + (f" (+{len(asins) - 3})" if len(asins) > 3 else "")
def main():
if RUNS < 2 or not 1 <= COUNT <= 50 or not KEYWORD.strip():
raise SystemExit("set KEYWORD, RUNS >= 2 and COUNT 1 to 50 (a bad count is a billed 400)")
session = new_session()
market = get_with_retry(session, "amazon-get-marketplaces").get(MARKET) # 1 credit; cache it in a real job
if market is None:
raise SystemExit(f"unknown marketplace ID {MARKET}: every search would be a free 400")
default = market["geo_location"]
codes = list(dict.fromkeys(p.strip() for p in POSTCODES))
locations = [None] + [p for p in codes if sendable(p, default)]
runs, stopped = collect(session, locations)
noise = flips(runs[None]) if complete(runs[None]) else None
print(f'"{KEYWORD}" on {market["marketplace_domain"]}: organic products on page one, {RUNS} runs per location')
print(f"{'location':<17}{'organic':<14}{'only here':>9}{'only default':>14} verdict")
notes = []
for loc in locations:
sizes = ", ".join(str(len(r)) if isinstance(r, set) else r for r in runs[loc]) or "-"
here = base = "-"
if loc is None:
name, verdict = f"default ({default or 'none'})", "baseline"
if noise is not None:
verdict = f"baseline: {noise} flip between runs"
elif noise is None or not complete(runs[loc]):
name, verdict = loc, "not compared"
else:
name, (only_here, only_base) = loc, compare(runs[None], runs[loc])
here, base = len(only_here), len(only_base)
if not here and not base:
verdict = "no consistent difference"
elif here + base <= noise:
verdict = "within the noise"
else:
verdict = "differs from the default"
notes += [f" {loc} only here: {show(only_here)}"] if here else []
notes += [f" {loc} only at the default: {show(only_base)}"] if base else []
print(f"{name:<17}{sizes:<14}{here:>9}{base:>14} {verdict}")
for code in codes:
if not sendable(code, default):
why = f"not a {len(default)}-digit postcode" if code else "blank"
print(f"{code or '(blank)':<17}{'-':<14}{'-':>9}{'-':>14} not sent: {why}")
for line in notes + ([stopped] if stopped else []):
print(line)
if __name__ == "__main__":
try:
main()
except (ApiError, requests.RequestException) as err: # the marketplace list failed, or a 401/403
raise SystemExit(f"stopped: {err}")
Output of geo_check.py (illustrative data)
"usb c cable" on amazon.com: organic products on page one, 2 runs per location
location organic only here only default verdict
default (20001) 42, 42 - - baseline: 2 flip between runs
10001 42, 42 0 0 no consistent difference
33101 41, 41 1 1 within the noise
99501 43, 43 5 3 differs from the default
9950 - - - not sent: not a 5-digit postcode
99501 only here: B0LOC00001, B0LOC00002, B0LOC00003 (+2)
99501 only at the default: B0CAB00005, B0CAB00017, B0CAB00033
In this illustrative run the two default searches differ by two products, which sets the noise floor. 10001 has no product that is in both of its runs and in no default run, or the other way round, and 33101 has one product each way, no more than the default runs flip on their own. Only 99501 differs: five products are in both of its runs and in no default run, and three that are in both default runs are missing. 9950 was never sent, because it has four digits and the amazon.com default postcode has five.
Read the verdicts carefully. "Differs from the default" shows that page one came back different for that postcode; it does not prove that Amazon used exactly 99501, because a postcode that falls back to another location changes the results too. "No consistent difference" means one of two things the API cannot tell apart: the postcode does not matter for this keyword, or it was not applied. On a page that moves a lot, a small real difference can also stay within the noise. With RUNS = 3 a counted product has to hold in three runs per location, for 1.5 times the search credits (12 searches instead of 8).
Credit math for a geo_location check
Each billed call costs one credit, and the run above made 9 billed requests: 1 for the marketplace list and 8 searches (4 locations, 2 runs each). The typo cost nothing because it was never sent. A search whose postcode Amazon ignored is billed like any other 200. A missing or blank q and a missing or unknown marketplace ID are refused for free before billing; a count above 50 is a billed 400, which is why the script checks it first. When the scraper fails, the API answers 502, and when the service is unavailable, 503 with a Retry-After header; both are billed on every attempt, and get_with_retry makes up to 4. A 500 without Retry-After is refunded and retried once, and 429, 401 and 403 are free. MAX_FAILED = 3 stops the run after 3 failed calls, at most 12 billed attempts. Plans and credit prices are on the pricing page.
What the response cannot tell you
- Which location was used: there is no location field, and the status is 200 either way.
- Whether Amazon took your postcode: an address Amazon only calls valid counts as set, without a check of which postcode Amazon applied.
- Anything after page one: results come from the first results page, capped at
count, and products without a displayed price are skipped. - Delivery dates or shipping costs: a search result carries the listed price, no delivery fields.
- Positions over time: for daily rank tracking per keyword, see the keyword rank tracking guide; every field of a result is explained in the search results API guide.
Frequently Asked Questions
What format does geo_location take?
A ZIP code or postcode as text, for example 10001 on amazon.com or SW1A 1AA on amazon.co.uk. The API passes it on unchanged and does not check it, so check the format yourself: a typo comes back as a normal, billed search from another location.
What happens if I leave geo_location out?
The API asks for the marketplace's default postcode, the geo_location value that /amazon-get-marketplaces lists, for example 20001 on amazon.com and 10115 on amazon.de, best effort like any postcode. amazon.ae has no default postcode, so no delivery postcode is set there.
How do I know whether my postcode was used?
Not from the response: it has no location field, and a refused or lost postcode still returns 200. Compare repeated searches with the default location instead, as the script in this guide does.
Does a search with an ignored postcode cost a credit?
Yes. Setting the postcode is part of a normal search, so the call is a billed 200 whether Amazon took the postcode or not. Requests refused before billing, such as a missing q or an unknown marketplace ID, are free.
Is geo_location= with an empty value the same as leaving it out?
No. The default postcode is used only when the parameter is absent. An empty value skips the postcode step completely, so the search runs wherever the session started. Leave the parameter out instead.
Bottom line: geo_location asks Amazon for a delivery postcode, and the search API answers either way. Leave it out for the marketplace default, never send it empty, check the format before you pay, and judge a postcode only by repeated runs next to the default location. A free account comes with 150 credits after e-mail verification.