Home / Blog / Amazon Search API geo_location: Does It Change Results?

Amazon Search API geo_location: Does It Change Results?

The Amazon search API geo_location parameter sets the delivery postcode for one search, but only as far as Amazon accepts it: a refused or lost postcode still returns a billed 200 from another location, and the response never says which. What the parameter does, when it is ignored, every default postcode, and a Python script that measures whether a postcode changes page one.

October 25, 2026
9 min read
SellerMagnet Team
Share & Bookmark
Terminal window with the Python function that sends geo_location to the Amazon search API, the geo_location line marked best effort

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_location takes 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.

Where the results of one /amazon-search call come from (October 2026).
RequestWhat happensResults come from
No geo_locationThe marketplace's default postcode is setThe default postcode (20001 on amazon.com)
A postcode Amazon acceptsSet; Amazon confirms it or calls the address validYour postcode
A postcode Amazon refuses, such as a typo3 tries, then the search runs anywayThe location the session started with
The postcode change is blocked3 tries, then the search runs anywayThe location the session started with
Amazon sets a different postcode3 tries, then the search runs anywayThat other location
Amazon blocks the first page loadA backup fetch returns page one without the postcode stepThe location the session started with
Robot checks after the postcode was set, and no retry gets throughLast resort: a new session fetches the pageA new session's location, not your postcode
geo_location= with no valueNo postcode step, not even the defaultThe 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.

Six-step log of one search: the postcode change is refused, the search runs again anyway and returns 200 without a location field
A refused, blocked or swapped postcode does not stop the search: the API answers 200 from wherever the session is.

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.

Default postcode per marketplace, as /amazon-get-marketplaces returns it (October 2026).
MarketplaceMarketplace IDDefault postcode
amazon.comATVPDKIKX0DER20001
amazon.caA2EUQ1WTGCTBG2K1A 0A1
amazon.com.mxA1AM78C64UM0Y806000
amazon.com.brA2Q3Y263D00KWC70040-904
amazon.co.ukA1F83G8C2ARO7PSW1A 1AA
amazon.ieA28R8C7NBKEWEAD02
amazon.deA1PA6795UKMFR910115
amazon.frA13V1IB3VIYZZH75001
amazon.itAPJ6JRA9NG5V400184
amazon.esA1RKKUPIHCS9HS28001
amazon.nlA1805IZSGTT6HS1012
amazon.com.beAMEN7PMS3EDWL1000
amazon.seA2NODRKZP88ZB9111 52
amazon.plA1C3SOZRARQ6R300-001
amazon.com.trA33AVAJ2PDY3EV06690
amazon.aeA2VIGQ35RCS4UGnone
amazon.saA17E79C6D8DWNP12271
amazon.egARBP9OOSHTCHU11511
amazon.co.zaAE08WJ6YKNBMC0083
amazon.co.jpA1VC38T7YXB528100-0001
amazon.inA21TJRUUN4KGV110001
amazon.sgA19VAU5U5O7RUS179431
amazon.com.auA39IBJ37TRP1C62601

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.

Table of eight example products across two default runs and two runs for postcode 99501, showing which ones count as a difference
B, C and F count: each is in both runs at one location and in no run at the other. D and H flip between the default runs, so the noise floor is 2.

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.

Ready to Extract Amazon Data at Scale?

Start building with SellerMagnet API today. Real-time product data, competitive pricing, and review analytics at your fingertips.

150 free API credits • No credit card required • Cancel anytime