NewLaunching String Web Access APIRead the manifesto →
← Blog

How to scrape Walmart product data in 2026

Bruce Magness · October 23, 2026
Part of Best E-commerce Scraping APIs in 2026: 8 APIs Compared

Last updated: October 23, 2026. Benchmark figures come from the September 16, 2026 run of the Web Data Frontier Benchmark. Every script on this page ran on October 23, 2026, and the output shown is what it printed.

To scrape Walmart product data in 2026, fetch the product page at https://www.walmart.com/ip/<item id> or a category page under /browse/, and read the JSON that Walmart puts in the page's __NEXT_DATA__ script tag. Name, price, stock, rating and review count are all in it. You do not need to parse the HTML. Getting the page is the hard part. On October 23, 2026, plain Python requests got Walmart's "Robot or human?" page on 43 of 43 URLs, each with HTTP 200. The String Web Access API returned all 72 pages we sent it with a price.

This page is part of our comparison of the best e-commerce scraping APIs.

On this page: TL;DR · Where String fits · What people run into · What we measured · What 16 APIs did · Step 1: fetch a product page · Step 2: read the product JSON in Python · Step 3: list a category in TypeScript · The typed Walmart integration · The failure mode · How to avoid getting blocked · How String gets Walmart pages · What it costs · FAQ

TL;DR

  • Walmart blocks with a page, not an error. A blocked request gets a 307 redirect to walmart.com/blocked, then HTTP 200 and a 15 KB page titled "Robot or human?". A scraper that checks only the status code stores that page.
  • On October 23, 2026, from a US home connection, default Python requests got that page on 43 of 43 product, search and category URLs.
  • curl_cffi impersonating Chrome got 12 of 12 category pages and 0 of 38 product and search pages, in the same session, minutes apart. The wall depends on the page type.
  • Through String, 72 of 72 Walmart requests came back with a price: 42 product pages over two runs, 20 search pages and 10 category pages. Every one billed as a standard fetch: $0.30 per 1,000 on Starter.
  • In the September 16, 2026 benchmark, 12 of 16 scraping APIs returned walmart.com on all five attempts. Only 23 of the 100 sites scored higher.
  • Every anonymous product page we fetched was priced for one store: ZIP 95829, the Sacramento Gerber Rd Supercenter. Store that location with every price.

Where String fits

String is a fit for one job on Walmart: fetching public product and category pages on a schedule, without managing the "Robot or human?" wall.

  • For: developers and pricing teams that track Walmart prices, stock or listings daily, from servers, at one API call per page.
  • Not for: pages behind a Walmart login, checkout, or jobs that Walmart's own Affiliates or Marketplace APIs already cover for your account.
  • Price: you pay only when content comes back. Every Walmart page we logged billed as a standard fetch: $0.30 per 1,000 on Starter ($20 a month) and $0.20 per 1,000 on Growth ($100 a month). The first 5,000 standard requests are free (pricing).
  • Integrations: a REST API for Python, TypeScript or any language, a typed Walmart integration that returns rows as JSON, and an MCP server for agents.
  • Limits: 60 requests a second per account on Starter and Growth with a 3,600-request burst, and no cap on concurrency (pricing). String fetches pages; you bring the schedule.

What people who scrape Walmart run into

The block shows up even for real browsers driven by code. In July 2026, a developer filed a detailed bug against an open-source Walmart MCP server. Headless Chromium was redirected to walmart.com/blocked with the title "Robot or human?". The developer then logged in by hand and reused that browser profile. It was still blocked: "a valid, cookied, logged-in session is still blocked purely for running headless" (GitHub, markswendsen-code/mcp-walmart issue #1, July 31, 2026).

The press-and-hold check is not new. In 2021 a Stack Overflow user driving Chrome with Selenium described it: "I press and hold the button until teh captcha is cleared, then appears this symbol ✓ and then appears the captcha again" (Stack Overflow, testgen, November 22, 2021). The one answer suggested a Chrome flag. A later comment on it reads: "This doesn't prevent being detected and blocked for me."

Not everyone sees the wall on every page. A curl_cffi tutorial published by Bright Data says that impersonate="chrome" makes Walmart "return the standard web page" for a search URL. We tested that claim. It held for category pages and not for product or search pages, as the next section shows.

What we measured on October 23, 2026

We sent the same Walmart URLs through three clients, one request at a time, three seconds apart. The connection was a US home connection in New York.

The URLs: 21 product pages (20 items from two Walmart searches, plus the benchmark's own product), 10 search pages and 10 category pages. A page counts as real when its __NEXT_DATA__ JSON carries a product name and a price.

Client, October 23, 2026 Product pages Search pages Category pages
Python requests, defaults 0 of 23 0 of 10 0 of 10
curl_cffi, impersonate="chrome" 0 of 26 0 of 12 12 of 12
String /v1/fetch, countryCode: "US" 42 of 42 20 of 20 10 of 10

Counts above 21 and 10 include a recheck. After curl_cffi passed the category pages, we sent it five product pages, two search pages and two category pages again, and plain requests two product pages. The result was the same split. String's counts include a second run of the 31 product and search pages, about two minutes after the first.

Every blocked response looked the same. Walmart answered with a 307 redirect to https://www.walmart.com/blocked?url=..., then HTTP 200 and a page of 15,195 bytes (15,566 for curl_cffi) titled "Robot or human?". The page loads HUMAN's px-captcha challenge. No block came back as a 403 or a 429.

The real pages are large. A product page through String was 595 KB at the median and a search page 1.5 MB. A category page carried 41 to 50 products, and a search page 50 to 72. String took 1.6 seconds per product or search page at the median on the first run, and the product prices were identical on both runs.

Walmart's robots.txt disallows /search, so we fetched each search page once per client for this test only. The scripts below use /browse/ category pages. Two limits on this test: it is one connection on one day, and Walmart scores IP reputation too. A different home IP may get through on more pages. The pattern to plan for is the split by page type.

What 16 scraping APIs did with walmart.com

The Web Data Frontier Benchmark sends the same 100 URLs to 16 scraping APIs, five attempts each. The Walmart URL is a Nike T-shirt product page, and a pass needs the text "Short Sleeve Just Do It Swoosh Graphic Active" in the body. The harness and targets are open source, and so is the raw result file for the September 16 run. The benchmark lists Walmart's anti-bot vendor as PerimeterX, now part of HUMAN.

API walmart.com, 5 attempts Average time on walmart.com Overall success, 100 sites
String 100% 7.8 s 97.0%
Scrapfly 100% 4.1 s 86.2%
ScraperAPI 100% 2.8 s 84.0%
Apify 100% 5.2 s 77.4%
Bright Data 100% 6.3 s 74.6%
ScrapingBee 100% 3.5 s 73.0%
Context.dev 100% 4.2 s 72.0%
Oxylabs 100% 6.9 s 69.0%
Nimble 100% 12.7 s 68.6%
Zyte 100% 1.6 s 68.0%
Decodo 100% 25.6 s 50.6%
ZenRows 100% 7.2 s 41.2%
ScrapingAnt 60% 3.2 s 36.4%
Firecrawl 40% 16.1 s 80.2%
Scrapingdog 0% 7.8 s 45.6%
Browserbase 0% 7.8 s 41.4%

Twelve of 16 APIs returned walmart.com on all five attempts: 65 passes out of 80 requests. The average API passed 81.3% on Walmart, against 66.6% across all 100 sites and 62.1% across the 17 retail sites. Only 23 of the 100 sites scored higher.

The four partial scores fail in four ways, from the raw file:

  • Browserbase got HTTP 307 on all five attempts. That is the redirect our blocked clients got before the "Robot or human?" page.
  • Scrapingdog returned a body without the product text on all five, in about 0.06 seconds each.
  • Firecrawl returned HTTP 500 from its own API on three attempts and passed two.
  • ScrapingAnt returned HTTP 423 from its own API on two attempts and passed three.

Scrapfly, Firecrawl, ScraperAPI, Zyte, ScrapingAnt, ScrapingBee, Scrapingdog and Nimble ran on harness settings below their current anti-bot mode, because the harness changed on September 6, 2026. That applies to three of the four partial scores above.

The benchmark loads one product page five times. It does not test search or category pages, and it does not check which store the price is for.

Try it on your own item ids: pip install requests, set STRING_API_KEY, and run the script in Step 2. The first 5,000 standard requests are free.

Step 1: fetch a product page

The fetch is one POST to String's fetch endpoint with the Walmart URL.

import os
import requests

def fetch(url: str) -> str:
    response = requests.post(
        "https://request.usestring.ai/v1/fetch",
        headers={"Authorization": f"Bearer {os.environ['STRING_API_KEY']}"},
        json={"url": url, "countryCode": "US"},
        timeout=90,
    )
    response.raise_for_status()
    page = response.json()
    if page["statusCode"] != 200:
        raise RuntimeError(f"walmart.com returned {page['statusCode']} for {url}")
    return page["data"]

statusCode is Walmart's own status, not String's. A product URL works with only the item id: https://www.walmart.com/ip/8886020987 returned the same page as the long URL with the product name in it.

Step 2: read the product JSON in Python

Walmart is a Next.js site, so the product record ships as JSON in <script id="__NEXT_DATA__">. The fields are at props.pageProps.initialData.data.product, checked on October 23, 2026:

Field Path under product Example from the run
Name name Scott Paper Towels, Choose-A-Sheet, 6 Double Rolls
Price priceInfo.currentPrice.price 6.84
Stock availabilityStatus IN_STOCK
Rating, reviews averageRating, numberOfReviews 4.6, 70,230
Seller sellerDisplayName Walmart.com
Location the price is for location.postalCode, location.storeIds 95829, 3081

Here is the whole script:

"""Scrape Walmart product pages by item id with the String Web Access API.

    pip install requests
    export STRING_API_KEY=...
    python walmart_products.py 8886020987 15621274542
"""
import json
import os
import re
import sys

import requests

API = "https://request.usestring.ai/v1/fetch"


def fetch(url: str) -> str:
    response = requests.post(
        API,
        headers={"Authorization": f"Bearer {os.environ['STRING_API_KEY']}"},
        json={"url": url, "countryCode": "US"},
        timeout=90,
    )
    response.raise_for_status()
    page = response.json()
    if page["statusCode"] != 200:
        raise RuntimeError(f"walmart.com returned {page['statusCode']} for {url}")
    if "/blocked" in (page.get("finalUrl") or "") or "Robot or human" in page["data"][:20000]:
        raise RuntimeError(f"{url}: Walmart served its 'Robot or human?' page")
    return page["data"]


def next_data(html: str) -> dict:
    # Walmart is a Next.js site: the whole product record ships as JSON in this script tag.
    m = re.search(r'<script id="__NEXT_DATA__"[^>]*>(.*?)</script>', html, re.S)
    if not m:
        raise RuntimeError("HTTP 200 but no __NEXT_DATA__: treat this as blocked")
    return json.loads(m.group(1))


def product(item_id: str) -> dict:
    data = next_data(fetch(f"https://www.walmart.com/ip/{item_id}"))
    p = data["props"]["pageProps"]["initialData"]["data"]["product"]
    price = (p.get("priceInfo") or {}).get("currentPrice") or {}
    location = p.get("location") or {}
    return {
        "item_id": p["usItemId"],
        "name": p["name"],
        "price": price.get("price"),  # None means the page loaded without a price
        "availability": p.get("availabilityStatus"),
        "rating": p.get("averageRating"),
        "reviews": p.get("numberOfReviews"),
        "seller": p.get("sellerDisplayName") or p.get("sellerName"),
        # Walmart prices the page for a location. Store it with the price.
        "zip": location.get("postalCode"),
        "store": (location.get("storeIds") or [None])[0],
    }


if __name__ == "__main__":
    ids = sys.argv[1:] or ["8886020987", "15621274542", "2501897203"]
    for item_id in ids:
        p = product(item_id)
        print(f'{p["item_id"]} ${p["price"]} | {p["availability"]} | {p["rating"]} stars, {p["reviews"]:,} reviews | {p["seller"]} | zip {p["zip"]}, store {p["store"]}')
        print(f'   {p["name"][:80]}')

What it printed on October 23, 2026, in 5.2 seconds for three products:

8886020987 $6.84 | IN_STOCK | 4.6 stars, 70,230 reviews | Walmart.com | zip 95829, store 3081
   Scott Paper Towels, Choose-A-Sheet, 6 Double Rolls
15621274542 $39.95 | IN_STOCK | 4.2 stars, 1,234 reviews | Walmart.com | zip 95829, store 3081
   JBL Vibe Buds 2 - True Wireless Noise Cancelling Earbuds - Black
2501897203 $21.15 | IN_STOCK | 4 stars, 9 reviews | DesignerBrandsForLess | zip 95829, store 3081
   Nike Men's Short Sleeve Just Do It Swoosh Graphic Active T-Shirt Navy L

The third item is the benchmark's T-shirt. Its seller is a marketplace seller, not Walmart, so keep the seller field when you compare prices. All three prices are for ZIP 95829, which the page names as "Pickup, today at Sacramento Gerber Rd Supercenter".

Step 3: list a category in TypeScript

A category page holds about 40 to 50 listings in the same __NEXT_DATA__ JSON, under searchResult.itemStacks. Use /browse/ category URLs: Walmart's robots.txt allows /browse and disallows /search for all crawlers. No dependencies. Node 22.6 or later runs TypeScript directly (we ran it on Node 22), and so does Bun.

// Scrape one Walmart category (browse) page of product listings with the String Web Access API.
//   export STRING_API_KEY=...
//   node walmart_category.ts household-essentials/paper-towels/1115193_1073264_1149383
//   (Node 22.6+; or: bun walmart_category.ts <path>)

type FetchEnvelope = { statusCode: number; data: string; finalUrl?: string };

async function fetchPage(url: string): Promise<string> {
  const response = await fetch("https://request.usestring.ai/v1/fetch", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.STRING_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ url, countryCode: "US" }),
  });
  if (!response.ok) throw new Error(`String returned ${response.status}: ${await response.text()}`);
  const page = (await response.json()) as FetchEnvelope;
  if (page.statusCode !== 200) throw new Error(`walmart.com returned ${page.statusCode}`);
  if (page.finalUrl?.includes("/blocked") || page.data.slice(0, 20000).includes("Robot or human")) {
    throw new Error("Walmart served its 'Robot or human?' page: treat this as blocked");
  }
  return page.data;
}

// The path after /browse/ in a category URL. Walmart's robots.txt allows /browse and disallows /search.
const category = process.argv[2] ?? "household-essentials/paper-towels/1115193_1073264_1149383";
const html = await fetchPage(`https://www.walmart.com/browse/${category}`);
const match = html.match(/<script id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/);
if (!match) throw new Error("HTTP 200 but no __NEXT_DATA__: treat this as blocked");
const searchResult = JSON.parse(match[1]).props.pageProps.initialData.searchResult;

// Results sit in itemStacks; ads and editorial tiles share the list, so keep __typename "Product" only.
const products = searchResult.itemStacks
  .flatMap((stack: any) => stack.items ?? [])
  .filter((item: any) => item.__typename === "Product")
  .map((item: any) => ({
    id: item.usItemId as string,
    name: item.name as string,
    price: item.price as number | undefined,
    rating: item.averageRating as number | undefined,
    reviews: item.numberOfReviews as number | undefined,
    sponsored: Boolean(item.isSponsoredFlag),
  }));
if (products.length === 0) throw new Error("HTTP 200 but no products in the page: treat this as blocked");

const organic = products.filter((p) => !p.sponsored);
console.log(`${category.split("/").at(-2)}: ${products.length} products, ${products.filter((p) => p.price).length} with a price, ${products.length - organic.length} sponsored`);
for (const p of organic.slice(0, 5)) {
  console.log(`${p.id.padEnd(12)} $${String(p.price ?? "-").padEnd(6)} ${p.rating ?? "-"} stars (${p.reviews ?? 0})  ${p.name.slice(0, 55)}`);
}

We ran it on October 23, 2026, on Node 22. It returned in 2.6 seconds:

paper-towels: 50 products, 47 with a price, 10 sponsored
14580219389  $17.82  4.7 stars (108487)  Bounty Paper Towels Select-A-Size White, 6 Triple Rolls
8886020987   $6.84   4.6 stars (70230)  Scott Paper Towels, Choose-A-Sheet, 6 Double Rolls
3945859864   $16.48  4.7 stars (94494)  Great Value Ultra Strong Paper Towels, 12 Double Rolls
14580319069  $20.94  4.7 stars (38039)  Bounty Paper Towels Select-A-Size White, 6 Mega Rolls, 
150941272    $3.77   4.6 stars (130978)  Sparkle Tear-A-Square Paper Towels, 2 Double Rolls, 110

On Bun, the same script returned 49 earbuds listings, 48 with a price, in 4.3 seconds. A listing can come back without a price: 3 of the 50 paper-towel tiles had none. Fetch those items' product pages with the Python script when you need the price.

The listing price matched the product page on every item we checked: 20 of 20 items from our sample carried the same price on the Walmart search page as on their own product page.

The typed Walmart integration

If you want rows instead of pages, String also has a Walmart site integration with two actions:

  • search reads walmart.com search results by keyword or category, 40 results a page, priced and stocked at one store, chosen by store number or by the nearest store to a ZIP code.
  • products reads product records by item id or URL, up to 300 per call: name, brand, description, images, rating, review count and order limit. With a store number or ZIP code, it adds each item's price, stock and fulfillment at that store.
curl https://request.usestring.ai/v1/integrations/walmart/search \
  -H "Authorization: Bearer $STRING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "paper towels", "zipcode": "10001", "pages": 1}'

On October 23, 2026, that call returned 40 rows priced at the Secaucus Supercenter, the nearest store to ZIP 10001, and made two standard requests. Each response lists every request it made and the rate it billed at, in a requests array. Read the warnings array too: it says when a page did not confirm the store you asked for. The integration adds no charge of its own (billing).

The failure mode: HTTP 200 and "Robot or human?"

Walmart's block has no error code, so a status check cannot catch it.

  • A blocked request gets a 307 to https://www.walmart.com/blocked?url=<the page you asked for, base64>. HTTP clients follow it by default.
  • The redirect lands on HTTP 200: a page of about 15 KB titled "Robot or human?", with a press-and-hold button. A real product page is about 40 times larger.
  • A scraper that checks only for 200 then writes empty rows and reports no error.

Three checks catch it, and both scripts run them:

  • Reject any response whose final URL contains /blocked.
  • Treat "Robot or human?" in the first 20 KB, or a body under 50 KB, as a block.
  • Raise an error when __NEXT_DATA__ is missing or the product has no name.

The second failure is quieter: a real price for the wrong store. All 42 product pages we fetched through String were priced for ZIP 95829 and store 3081, in Sacramento. That is the location Walmart showed an anonymous visitor in our runs. If you compare Walmart with a local competitor, or report prices for a region, that is the wrong number. Store location.postalCode and location.storeIds with each price, or use the integration's zipcode and storeId fields.

How to avoid getting blocked on Walmart

In our run, what got through depended on the client and on the page type.

  • Category pages are the easy part. In our run, curl_cffi with Chrome impersonation got 12 of 12 /browse/ pages from a home connection. Plain requests got none.
  • Product pages are stricter. The same curl_cffi session got 0 of 26 product pages, minutes apart from its category passes.
  • A headless browser is not enough. The July 2026 report above shows a logged-in headless session redirected to /blocked.
  • /ip/ and /browse/ are allowed in robots.txt. /search, /api/ and /account/ are disallowed.
  • One request every few seconds is enough for most price checks, and it is polite.
  • Run the three checks above on every page, so a block stops the run instead of filling your table.

When your product pages start to come back as "Robot or human?", send the same URL through a fetch API instead: curl -X POST https://request.usestring.ai/v1/fetch -H "Authorization: Bearer $STRING_API_KEY" -H "Content-Type: application/json" -d '{"url":"https://www.walmart.com/ip/8886020987","countryCode":"US"}'.

How String gets Walmart pages

String is the most reliable web scraping API, bypassing anti-bot, and you only pay when content comes back. Here is what that means on Walmart, one fact per line:

  • String returned every Walmart page we sent it on October 23, 2026. 72 of 72 product, search and category pages came back with __NEXT_DATA__ and a price, while default Python requests got "Robot or human?" on 43 of 43.
  • String returned walmart.com on every benchmark attempt. In the September 16, 2026 run, String passed 5 of 5 on Walmart and 97.0% across all 100 sites, the highest of 16 APIs (benchmark).
  • Walmart pages bill at the lowest rate. All 72 Walmart fetches returned the x-billed-request-type header request_standard: $0.30 per 1,000 on Starter and $0.20 on Growth.
  • Walmart's own status comes back separately. statusCode in the response body is Walmart's status, so a removed item's 404 stays a 404 (fetch docs).
  • Walmart rows come back as JSON without a parser. The Walmart integration returns search results and product records priced for a store or ZIP code you choose.
  • Agents get the same fetch as a tool. String's hosted MCP server gives Claude and other MCP clients fetch and search with the same anti-bot handling.

What it costs

Every Walmart fetch we logged on October 23, 2026, 72 in all, billed as a standard fetch. From String's pricing, that is $0.30 per 1,000 requests on Starter ($20 a month) and $0.20 per 1,000 on Growth ($100 a month). A request that fails is not billed, and the first 5,000 standard requests are free.

Worked through from the runs above:

  • For price monitoring, you make one request per item. Checking 10,000 items once a day is about 300,000 requests a month: $90 in usage on Starter or $60 on Growth.
  • For category tracking, one request returns about 40 to 50 listings. Checking 500 categories a day is about 15,000 requests a month: $4.50 on Starter or $3.00 on Growth.

The typed integration bills only the requests it makes, at the same rates. On October 23, 2026, one search call for 40 rows made 2 standard requests, and one products call for 21 items at a store made 23.

Next step: run the Step 2 script on ten of your own Walmart item ids with a free key. The first 5,000 standard requests cost nothing.

Is it legal to scrape Walmart?

Prices and product details on public pages are facts, and collecting them is the best-supported case in scraping law. The limits are contract terms, personal data and how hard you hit the site. Our page on whether it is legal to scrape competitor prices goes through the cases. Stay on public pages, skip reviewer names, keep your rate low, and check with your counsel. This is not legal advice.

FAQ

How to scrape Walmart product listings?

Fetch a category page under https://www.walmart.com/browse/ and read props.pageProps.initialData.searchResult.itemStacks from its __NEXT_DATA__ JSON. Keep items whose __typename is "Product". Each has usItemId, name, price, averageRating, numberOfReviews and isSponsoredFlag. The TypeScript script on this page returned 50 paper-towel listings on October 23, 2026.

Can you scrape Walmart?

Yes, but plain HTTP clients rarely get past Walmart's wall. On October 23, 2026, default Python requests got the "Robot or human?" page on 43 of 43 URLs. curl_cffi with Chrome impersonation got category pages and not product pages. The String Web Access API returned 72 of 72 with prices.

Is there an API for Walmart?

Walmart runs an Affiliates API on Walmart I/O, with product lookup, search and store endpoints, for affiliate publishers who register a signing key. Sellers use the Walmart Marketplace APIs for their own items. If neither fits your account, the public pages are the source.

Why does Walmart keep asking "Robot or human?"

It is Walmart's bot check, run by HUMAN (formerly PerimeterX). It redirects a request it does not trust to walmart.com/blocked and shows a press-and-hold button. For a scraper it arrives as HTTP 200, so check the final URL and the page title, not the status.

How do I scrape Walmart with Python?

Send the product URL to a fetch API with requests, find <script id="__NEXT_DATA__"> in the HTML, and load it with json.loads. The price is at props.pageProps.initialData.data.product.priceInfo.currentPrice.price. The Python script on this page returned three products with price, stock, rating, reviews and seller in 5.2 seconds on October 23, 2026.

How to scrape Walmart product data for free?

For a small job, try curl_cffi with impersonate="chrome" on category pages first: it got 12 of 12 in our test. Product pages are stricter. String's first 5,000 standard requests are free, and every Walmart page in our test billed as standard.

Why is the Walmart price I scraped different from the one I see?

Walmart prices each page for a store. Every anonymous product page we fetched on October 23, 2026 was priced for ZIP 95829, the Sacramento Gerber Rd Supercenter, store 3081. Read location.postalCode from the page with the price, or ask String's Walmart integration for a specific zipcode or storeId.

Does Walmart block headless browsers?

Often, yes. A July 2026 GitHub report describes headless Chromium sent to walmart.com/blocked, even with a logged-in profile. In the September 16, 2026 benchmark, Browserbase, a hosted browser, got HTTP 307 on all five Walmart attempts. A plain fetch of the page through a scraping API avoids running a browser at all.

Sources

Cheers,
String team

Get your API key →Explore the Web Access API