How to scrape Nike
Nike.com sits behind Akamai Bot Manager. Requests that look automated get an "Access Denied" page instead of products. StealthASF Ultra mode returns the real page: in our production test on 8 October 2026, Nike's men's shoes listing came back with HTTP 200 and its real title. This guide walks from that listing to product pages and the fields you usually need.
1. Use Ultra mode
Set engine to ultra. Ultra is our strongest mode, built for the hardest protected sites, and it passed Nike in our tests. It renders the page and supports extraction but does not run browser steps such as clicks or scrolling. Nike listing pages and product pages each have their own URL, so you can collect everything by requesting URLs.
2. Request a listing page with curl
Create an API key in your dashboard after verifying your email and set STEALTHASF_API_KEY. The request below fetches the men's shoes listing with links extraction, so data holds every link on the page with its text, product links included.
curl --max-time 600 "https://stealthasf.com/v1/scrape" \
-H "x-api-key: $STEALTHASF_API_KEY" \
-H "content-type: application/json" \
--data-raw '{"url":"https://www.nike.com/w/mens-shoes-nik1zy7ok","engine":"ultra","extract":"links"}'Replace the URL with any Nike listing or search page from your browser. Keep the 600-second client timeout; a rendered protected page takes longer than a plain request.
3. What comes back
The response carries the target's status, the rendered html, the engine used and credits_charged. With links extraction, data is a table of text and url. Nike product pages have /t/ in the path, followed by the product name and style code, so filtering on /t/ gives you the products. If the text shows "Access Denied", you were blocked. A detected block returns HTTP 422 and is never charged.
4. Collect products in Python
This example uses only the Python standard library and stops on an API error, so a failed request never turns into a record.
import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen
payload = {
"url": "https://www.nike.com/w/mens-shoes-nik1zy7ok",
"engine": "ultra",
"extract": "links"
}
request = Request(
"https://stealthasf.com/v1/scrape",
data=json.dumps(payload).encode("utf-8"),
headers={
"x-api-key": os.environ["STEALTHASF_API_KEY"],
"content-type": "application/json",
},
method="POST",
)
try:
with urlopen(request, timeout=600) as response:
result = json.load(response)
except HTTPError as error:
detail = error.read().decode("utf-8")
raise SystemExit(f"API error {error.code}: {detail}")
print("Target status:", result["status"])
print("Credits:", result["credits_charged"])
print(result.get("data"))Then keep the product links and request each one with the same payload. The helper reads JSON-LD from a product page's HTML. Product pages that embed schema.org Product data give you the name, price and availability as JSON, with no CSS selectors to maintain.
import re
# result is the listing-page response from the example above.
detail_urls = sorted({url for _text, url in result["data"]["rows"] if "/t/" in url})
print(len(detail_urls), "detail pages")
def json_ld(html):
"""Every JSON-LD block on the page that parses as JSON."""
blocks = re.findall(r"<script[^>]*application/ld\+json[^>]*>(.*?)</script>", html, re.S | re.I)
found = []
for block in blocks:
try:
found.append(json.loads(block))
except json.JSONDecodeError:
pass
return found
# Send each detail URL with the same payload, then read its fields:
# for item in json_ld(detail["html"]): print(item.get("@type"), item.get("name"))5. Fields and cost
Typical Nike fields are product name, style code (the last part of the product URL), category, full price, sale price, colorways and sizes in stock. Use the style code as the record key so repeated runs update the same product. Check each record for a name and a price before saving it.
An Ultra request costs 50 credits including the first 1 MB of transfer. Each additional MB adds 10 credits, rounded up to a whole credit, and a solved CAPTCHA adds 25. Image-heavy pages can transfer more than 1 MB, so read credits_charged on a few responses before you set a budget. At the base rate, a listing page and 40 product pages cost 2,050 credits, and the Pro plan covers 5,000 Ultra requests a month. Blocked requests are never charged.
6. Run it on a schedule
Collect product URLs from listings once, then request only those products on each run to track price and stock. Deduplicate URLs first, since the same shoe can appear in several listings. Store the URL, style code, engine, target status, job ID and credits with each record. Raise concurrency step by step within your plan limit, follow Retry-After on a 429 and cap retries. If a URL keeps returning 422, send support the job ID. If prices depend on the region, add a country field on Pro or Scale. More on this protection in the Akamai guide, and every field is in the API reference.