To extract the ASIN from an Amazon URL, match the 10-character code that follows /dp/, /gp/product/ or /gp/aw/d/ in the path, and read the marketplace from the domain: amazon.de is A1PA6795UKMFR9, amazon.com is ATVPDKIKX0DER. Short links such as amzn.to have to be resolved first. This guide gives a regex tested against the common URL shapes in Python, JavaScript and bash, and shows why validating before the API call saves credits.
Key Takeaways
- The ASIN is the 10-character segment after /dp/, /gp/product/, /gp/aw/d/, /gp/offer-listing/ or /product-reviews/.
- Print books with an ISBN-10 reuse it as the ASIN; everything else, Kindle books included, starts with B.
- The domain decides the marketplace: the same ASIN on amazon.de and amazon.fr needs two different marketplaceId values.
- Short links (amzn.to, a.co, amzn.eu) carry no ASIN; read the Location header of the redirect.
- Product lookup does not check the ASIN format, so a malformed code is sent and billed - validate first.
Where is the ASIN in an Amazon URL?
Amazon puts the ASIN in the path, after a fixed prefix. The words before /dp/ are a slug for search engines and can be anything; tracking parameters after ? do not matter either. The same product can be linked in several shapes, so a parser has to know all of them.
| Pattern | Where you see it | Example |
|---|---|---|
/dp/{ASIN} | Product page | amazon.com/Title/dp/B0CL61F39H |
/gp/product/{ASIN} | Older product link | amazon.it/gp/product/B0CLTBHXWQ |
/gp/aw/d/{ASIN} | Mobile product page | amazon.de/gp/aw/d/B0CLTBHXWQ |
/gp/offer-listing/{ASIN} | All offers | amazon.com/gp/offer-listing/B0CL61F39H |
/product-reviews/{ASIN} | Review page | amazon.com/product-reviews/B0CL61F39H |
?asin={ASIN} | Query parameter | amazon.com/…?asin=B0CL61F39H |
amzn.to · a.co · amzn.eu | Short link | follow the redirect first |

How do I extract the ASIN in Python?
Parse the URL, map the host to a marketplace ID, then look for the ASIN after one of the known prefixes, falling back to an asin= or ASIN.1= (add-to-cart links) query parameter. The validation pattern accepts product ASINs (B plus nine letters or digits) and ISBN-10 book ASINs, so a truncated or mistyped code never reaches the API. Every marketplace ID is in the marketplace IDs list.
asin_url.py - ASIN and marketplace from any Amazon product URL
import re
from urllib.parse import parse_qs, urlparse
MARKETPLACES = {
"amazon.com": "ATVPDKIKX0DER", "amazon.ca": "A2EUQ1WTGCTBG2", "amazon.com.mx": "A1AM78C64UM0Y8",
"amazon.com.br": "A2Q3Y263D00KWC", "amazon.co.uk": "A1F83G8C2ARO7P", "amazon.ie": "A28R8C7NBKEWEA",
"amazon.de": "A1PA6795UKMFR9", "amazon.fr": "A13V1IB3VIYZZH", "amazon.it": "APJ6JRA9NG5V4",
"amazon.es": "A1RKKUPIHCS9HS", "amazon.nl": "A1805IZSGTT6HS", "amazon.com.be": "AMEN7PMS3EDWL",
"amazon.se": "A2NODRKZP88ZB9", "amazon.pl": "A1C3SOZRARQ6R3", "amazon.com.tr": "A33AVAJ2PDY3EV",
"amazon.ae": "A2VIGQ35RCS4UG", "amazon.sa": "A17E79C6D8DWNP", "amazon.eg": "ARBP9OOSHTCHU",
"amazon.co.za": "AE08WJ6YKNBMC", "amazon.co.jp": "A1VC38T7YXB528", "amazon.in": "A21TJRUUN4KGV",
"amazon.sg": "A19VAU5U5O7RUS", "amazon.com.au": "A39IBJ37TRP1C6",
}
ASIN = re.compile(r"^(?:B[0-9A-Z]{9}|[0-9]{9}[0-9X])$") # product ASIN or ISBN-10
PATH = re.compile(
r"/(?:dp|gp/product|gp/aw/d|gp/offer-listing|product-reviews|exec/obidos/ASIN)/([0-9A-Za-z]{10})(?=[/?#]|$)"
)
def parse_amazon_url(url: str) -> tuple[str, str] | None:
"""Return (asin, marketplaceId) for an Amazon product URL, or None."""
url = url.strip()
parts = urlparse(url if "://" in url else "https://" + url)
host = (parts.hostname or "").lower()
for prefix in ("www.", "smile.", "m."):
host = host.removeprefix(prefix)
marketplace_id = MARKETPLACES.get(host)
if marketplace_id is None:
return None
query = parse_qs(parts.query)
for candidate in PATH.findall(parts.path) + query.get("asin", []) + query.get("ASIN.1", []):
if ASIN.match(candidate.upper()):
return candidate.upper(), marketplace_id
return None
assert parse_amazon_url("https://www.amazon.com/PS5-Slim/dp/B0CL61F39H/ref=sr_1_1?th=1") == ("B0CL61F39H", "ATVPDKIKX0DER")
assert parse_amazon_url("amazon.co.uk/dp/0141439513") == ("0141439513", "A1F83G8C2ARO7P")
assert parse_amazon_url("https://www.amazon.com/s?k=ps5") is None # a search page has no ASIN
Validate before you look it up
/api/amazon-product-lookup does not check the ASIN format: a malformed code is sent upstream and billed like any other request, usually as a 404. Five lines of regex are cheaper than a credit per typo.
How do I resolve amzn.to and a.co short links?
A short link is a redirect with no ASIN in it. Send a GET without following redirects - a.co, amzn.eu and amzn.asia answer a HEAD request with 404 - and read the Location header; repeat until the URL parses, then use it. Reading the header is enough - there is no need to download Amazon's product page itself.
short_links.py - follow the redirect chain via Location headers
from urllib.parse import urljoin, urlparse
import requests
from asin_url import parse_amazon_url
SHORT_HOSTS = {"amzn.to", "a.co", "amzn.eu", "amzn.asia", "amzn.com"}
def resolve(url: str, max_hops: int = 5) -> tuple[str, str] | None:
url = url.strip()
url = url if "://" in url else "https://" + url
for _ in range(max_hops):
if found := parse_amazon_url(url):
return found
host = (urlparse(url).hostname or "").lower().removeprefix("www.")
if host not in SHORT_HOSTS:
return None # no ASIN and nothing to follow
# GET, not HEAD: a.co, amzn.eu and amzn.asia answer HEAD with a 404
resp = requests.get(url, allow_redirects=False, timeout=10)
location = resp.headers.get("Location")
if not location:
return None
url = urljoin(url, location) # Location may be relative
return None
Can I do the same in JavaScript?
Yes. The function below uses only regular expressions, so it runs in browsers, Node.js, Deno and edge runtimes alike. Keep the domain map in your code, or load it once from /api/amazon-get-marketplaces - that call costs one credit, so cache the result.
asinUrl.js - no dependencies, no URL class needed
const MARKETPLACES = {
"amazon.com": "ATVPDKIKX0DER", "amazon.ca": "A2EUQ1WTGCTBG2", "amazon.com.mx": "A1AM78C64UM0Y8",
"amazon.com.br": "A2Q3Y263D00KWC", "amazon.co.uk": "A1F83G8C2ARO7P", "amazon.ie": "A28R8C7NBKEWEA",
"amazon.de": "A1PA6795UKMFR9", "amazon.fr": "A13V1IB3VIYZZH", "amazon.it": "APJ6JRA9NG5V4",
"amazon.es": "A1RKKUPIHCS9HS", "amazon.nl": "A1805IZSGTT6HS", "amazon.com.be": "AMEN7PMS3EDWL",
"amazon.se": "A2NODRKZP88ZB9", "amazon.pl": "A1C3SOZRARQ6R3", "amazon.com.tr": "A33AVAJ2PDY3EV",
"amazon.ae": "A2VIGQ35RCS4UG", "amazon.sa": "A17E79C6D8DWNP", "amazon.eg": "ARBP9OOSHTCHU",
"amazon.co.za": "AE08WJ6YKNBMC", "amazon.co.jp": "A1VC38T7YXB528", "amazon.in": "A21TJRUUN4KGV",
"amazon.sg": "A19VAU5U5O7RUS", "amazon.com.au": "A39IBJ37TRP1C6",
};
const ASIN = /^(?:B[0-9A-Z]{9}|[0-9]{9}[0-9X])$/;
const PATH = /\/(?:dp|gp\/product|gp\/aw\/d|gp\/offer-listing|product-reviews|exec\/obidos\/ASIN)\/([0-9A-Za-z]{10})(?=[\/?#]|$)/g;
export function parseAmazonUrl(url) {
const m = url.trim().match(/^(?:https?:\/\/)?([^\/?#]+)([^?#]*)(?:\?([^#]*))?/i);
if (!m) return null;
const host = m[1].toLowerCase().replace(/^(?:www|smile|m)\./, "");
const marketplaceId = MARKETPLACES[host];
if (!marketplaceId) return null;
const candidates = [...m[2].matchAll(PATH)].map((x) => x[1]);
const query = (m[3] || "").match(/(?:^|&)(?:asin|ASIN\.1)=([0-9A-Za-z]{10})(?:&|$)/);
if (query) candidates.push(query[1]);
const asin = candidates.map((c) => c.toUpperCase()).find((c) => ASIN.test(c));
return asin ? { asin, marketplaceId } : null;
}
// parseAmazonUrl("https://www.amazon.de/gp/aw/d/B0CLTBHXWQ/") -> { asin: "B0CLTBHXWQ", marketplaceId: "A1PA6795UKMFR9" }
How do I pull ASINs out of a list of links in the shell?
For a one-off clean-up of a spreadsheet column or a log file, grep -oE does the job. It extracts the ASIN only; add the domain to the pattern if the file mixes marketplaces.
Unique ASINs from a file of Amazon links
grep -oiE '/(dp|gp/product|gp/aw/d|gp/offer-listing|product-reviews)/[0-9a-z]{10}([^0-9a-z]|$)' links.txt \
| sed -E 's#^.*/([0-9A-Za-z]{10}).?$#\1#' \
| tr '[:lower:]' '[:upper:]' \
| grep -E '^(B[0-9A-Z]{9}|[0-9]{9}[0-9X])$' \
| sort -u > asins.txt
wc -l asins.txt

What should I do with the ASIN next?
Pass both values to a lookup: besides your API key, asin and marketplaceId are the only parameters /api/amazon-product-lookup needs, and the answer carries the title, price, images and ranks. To store a clean link, rebuild it as https://www.<domain>/dp/<ASIN>. The Python quickstart has a client with retries; for barcodes instead of links, the UPC and EAN to ASIN guide covers the converter.
Frequently Asked Questions
How long is an ASIN?
Always 10 characters, letters and digits. Print books with an ISBN-10 reuse it as the ASIN; everything else, Kindle books included, starts with B.
Does the same ASIN work on every Amazon marketplace?
Often, but not always. The ASIN can be missing or different in another country, and a lookup there returns a billed 404.
Can I get the ASIN from an amzn.to link without opening it?
You need one request to the short-link host. Read the Location header of its redirect; the target URL contains the ASIN.
Why does my regex also match non-product pages?
Anchor it to the known prefixes such as /dp/ and /gp/product/, and validate the 10 characters. Search and store pages carry no ASIN.
Does the lookup endpoint reject an invalid ASIN for free?
No. Product lookup does not validate the format, so an invalid ASIN is billed. Check it locally first.
Bottom line: read the marketplace from the domain, the ASIN from the segment after /dp/ or its siblings, resolve short links through their redirect, and validate before spending a credit. Every lookup field is on the product lookup page, and a free account includes 150 credits.