JSON Web Scraping: How to Find, Extract and Parse the JSON Behind Any Website

Developer guide Β· Last reviewed 28 September 2026 Β· 12 min read

JSON web scraping means collecting data from the JSON that websites already use, instead of parsing it out of HTML. Most modern sites load their products, listings and search results from JSON endpoints, and many embed structured JSON in the page itself. This guide shows where that JSON lives, how to find it with your browser’s developer tools, how to request and parse it in Python, and how to run JSON scraping reliably at scale with residential proxies.

The short version

Before you parse HTML, look for the JSON behind the page: it is cleaner, faster to fetch and changes less often. Open DevTools, filter the Network tab by Fetch/XHR, reload, and look for responses that contain your data. Also check the page source for application/ld+json blocks and embedded state. Fetch the endpoint with the same parameters, parse it with Python’s json module, and page through the results.

Why JSON Web Scraping Beats HTML Parsing

Structured data, straight from the source

A web page is a presentation layer. Somewhere behind it, the site has the same data in a structured form, and on most modern sites that form is JSON: JavaScript Object Notation, the lightweight text format defined in RFC 8259. When a page loads products after you scroll, filters results without reloading, or shows a price that appears a moment after the rest of the page, it is almost always fetching JSON.

  • Cleaner data. Prices arrive as numbers, dates in standard formats, and IDs are included, with no text to clean from HTML.
  • Less fragile. Redesigns change HTML classes all the time; data endpoints change far less often.
  • Faster and lighter. One small JSON response can replace a full page with scripts, styles and images, which also cuts proxy bandwidth.
  • More complete. Endpoints often return fields the page does not display, such as stock counts or product IDs.

The best HTML parser is the one you don’t need: find the JSON first.

Where JSON Hides on a Website

Four places to look

1. XHR and fetch requests

Single-page applications and infinite-scroll listings call JSON endpoints from the browser, for example a search or products endpoint with query parameters for page, filters and sorting.

2. Embedded state in the HTML

To render quickly, many sites send the initial data inside the HTML as a JSON object in a <script> tag, which the JavaScript framework then picks up. Search the page source for "price" or a product name and you will often find it.

3. JSON-LD structured data

Sites add JSON-LD blocks, <script type="application/ld+json">, to describe products, articles, events, organisations and reviews to search engines using schema.org vocabulary. Google’s documentation recommends JSON-LD as the format for structured data. For scrapers it is a gift: name, price, currency, availability and ratings in a documented structure.

4. Public APIs

Some sites offer documented APIs. If one exists for the data you need, use it: it is the most stable and clearly permitted route.

How to Find JSON Endpoints with DevTools

Five minutes in the Network tab
  1. Open the page in Chrome or another browser, press F12 and select the Network tab.
  2. Filter by Fetch/XHR and reload the page, then scroll, filter or click “load more” to trigger more requests.
  3. Click each request and open its Preview or Response tab. Look for your products, prices or listings.
  4. When you find it, check the Headers tab: the request URL, the method (GET or POST), query parameters and the request body.
  5. Right-click the request and choose Copy as cURL to reproduce it exactly, then trim headers until you know which ones are needed.

Use the search box in the Network panel to search all responses for a value you can see on the page, such as a specific price. It finds the right request faster than clicking through dozens of them.

Before you write any code, test the request on its own. Paste the copied cURL command into a terminal, confirm it returns the same JSON, and then change one parameter at a time, such as the page number or category, to learn how the endpoint behaves. Ten minutes spent here usually saves hours of debugging later.

Request and Parse JSON in Python

requests does most of the work
Fetch a JSON endpoint and read the fields you need
import requests

PROXY = "http://USERNAME:PASSWORD@v2.proxyempire.io:5000"
proxies = {"http": PROXY, "https": PROXY}
headers = {"User-Agent": "Mozilla/5.0", "Accept": "application/json",
           "Accept-Language": "en-US"}

url = "https://www.example.com/api/products"
params = {"category": "laptops", "page": 1, "sort": "price_asc"}

r = requests.get(url, params=params, headers=headers, proxies=proxies, timeout=30)
r.raise_for_status()
data = r.json()                       # parsed into Python dicts and lists

for item in data.get("products", []):
    print(item.get("id"), item.get("name"), item.get("price"))

Check the response’s content type before calling .json(). If a site answers with an HTML error or CAPTCHA page, .json() raises an exception, which is better than silently storing bad data. Handle that case explicitly and log the URL.

Reading embedded state from the HTML

Extract a JSON object from a script tag
import json
from bs4 import BeautifulSoup

html = requests.get(page_url, headers=headers, proxies=proxies, timeout=30).text
soup = BeautifulSoup(html, "html.parser")
tag = soup.find("script", id="initial-state")      # the id or type differs per site
state = json.loads(tag.string)
print(state.keys())

Common pitfalls with JSON endpoints

  • Required headers. Some endpoints expect an Accept header, a Referer or a custom header the page sends. If a request works in the browser but not in your script, add the browser’s headers back one at a time until you find the one that matters.
  • Short-lived tokens. Endpoints that need a token issued to the page usually expect a fresh token per session. Load the page first in the same session to get it, and never reuse tokens that belong to someone else.
  • Different data per country. The same endpoint can return other prices, currencies or products depending on the visitor’s IP. Record the country of every request next to the data.
  • Silent schema changes. Sites rename or move fields without notice. Check that the fields you rely on exist in every response, and alert when they disappear.

How to Extract JSON-LD Structured Data

schema.org data in every product page
Read all JSON-LD blocks and pick the Product
import json

def json_ld(soup):
    for tag in soup.find_all("script", type="application/ld+json"):
        try:
            block = json.loads(tag.string or "")
        except json.JSONDecodeError:
            continue
        for obj in (block if isinstance(block, list) else [block]):
            yield obj

for obj in json_ld(soup):
    if obj.get("@type") == "Product":
        offer = obj.get("offers", {})
        print(obj.get("name"), offer.get("price"), offer.get("priceCurrency"))

JSON-LD can be a single object, a list, or a graph under @graph, and offers can be one offer or a list. Write your extraction to handle each shape.

Pagination, Cursors and GraphQL

Getting every record, once
  • Page numbers: increase page until the list comes back empty or you pass the total count the response reports.
  • Offsets: add the page size to offset each time.
  • Cursors: many APIs return a next token or URL; pass it to the next request and stop when it is missing.
  • GraphQL: some sites use a single endpoint that accepts a POST with a query and variables. The GraphQL documentation describes how queries are sent over HTTP. Copy the query from DevTools and change only the variables, such as the cursor or category.
Cursor-based pagination
items, cursor = [], None
while True:
    params = {"limit": 50, **({"cursor": cursor} if cursor else {})}
    page = requests.get(url, params=params, headers=headers,
                        proxies=proxies, timeout=30).json()
    items += page["results"]
    cursor = page.get("next_cursor")
    if not cursor:
        break
print(len(items), "records")

Working with Nested JSON

Flatten what you need, keep the rest

Real responses are deeply nested: a product contains variants, each with prices by region and stock by warehouse. Three habits keep this manageable:

  • Read defensively. Use .get() with defaults so a missing field does not crash the run, and count how often each field is missing.
  • Use a query language such as JMESPath for complex selections, for example every variant price in one expression, instead of nested loops.
  • Store raw and flat. Save the raw response as JSON Lines, one JSON object per line, and write a separate flat table with the fields you analyse. When you need a new field later, re-parse the raw file instead of scraping again.

Key takeaways

  • Look for XHR/fetch endpoints, embedded state and JSON-LD before parsing HTML.
  • Reproduce requests with “Copy as cURL”, then keep only the headers and parameters you need.
  • Handle every pagination style and every JSON shape explicitly.
  • Keep raw responses as JSON Lines so you can re-parse without re-scraping.

Scaling JSON Scraping with Proxies

Endpoints have rate limits too

JSON endpoints are cheap to call, which is exactly why sites rate-limit them per IP. Hundreds of calls a minute from one server will soon get 429 “too many requests” answers or an HTML challenge page instead of JSON. Rotating residential proxies spread the calls over many household IPs, so each one sends only a few requests.

  • Rotation: a new IP per request suits independent calls such as product IDs; a sticky session suits paginated sequences where the cursor or cookies are tied to one visitor.
  • Location: prices, currencies, availability and catalogues in JSON responses often depend on the visitor’s country. Country, city and ZIP targeting return the local version.
  • Bandwidth: JSON responses are small, so JSON scraping is one of the most cost-efficient ways to use pay-per-GB proxies.

For the Python side of proxies, including sessions, SOCKS5 and retries, see our Python web scraping guide. For pages that only produce their data inside a browser, see headless browsers.

Rules and Good Practice

An endpoint is still someone’s server
  • Public data only. Do not use endpoints behind logins you are not entitled to, and do not reuse other people’s tokens or session cookies.
  • Official APIs first. If the site offers a documented API, use it and follow its limits.
  • Moderate the rate. Limit concurrency per site and back off on 429 responses.
  • Avoid personal data unless you have a lawful basis, and check each site’s terms.

ProxyEmpire proxies are for lawful data collection. Banking, payment and government websites are blocked by default on all products. See our web scraping basics for more on responsible scraping.

JSON Web Scraping FAQ

Straight answers
What is JSON web scraping?

Collecting data from the JSON that a website uses, either from the endpoints its pages call or from JSON embedded in the HTML, instead of extracting values from the rendered HTML.

How do I find the JSON API behind a website?

Open DevTools, go to the Network tab, filter by Fetch/XHR and reload or interact with the page. Look at each response’s Preview for your data, and use the search box to find a value you can see on the page.

Is scraping JSON faster than scraping HTML?

Usually, yes. A JSON response is smaller than the full page, needs no HTML parsing, and does not require a browser to run JavaScript.

What is JSON-LD and why is it useful for scraping?

JSON-LD is a way to embed structured data in a page using schema.org types such as Product or Article. It gives you clean fields like name, price, currency and availability in a documented structure.

Why does my JSON request work in the browser but not in Python?

The browser sends headers, cookies or tokens your script does not. Copy the request as cURL from DevTools and compare. Also check that your IP and country match what the site expects; a proxy in the right country often fixes it.

How should I store scraped JSON?

Keep the raw responses as JSON Lines, one object per line, and write the fields you need to a flat table or database. That lets you re-parse old data when requirements change.

Do I need proxies for JSON scraping?

For small, occasional jobs, no. For hundreds of calls per minute, or for data that depends on the visitor’s country, rotating residential proxies prevent rate limits and return the right local version.

References

Primary documentation
  1. RFC 8259: The JSON Data Interchange FormatThe JSON standard
  2. Google Search Central: Introduction to structured dataJSON-LD, schema.org and Google’s recommended format
  3. JSON-LDJSON for Linked Data
  4. Chrome DevTools: Network features referenceFiltering requests, previewing responses, copying as cURL
  5. GraphQL: Serving over HTTPHow GraphQL queries are sent with GET and POST
  6. JMESPathA query language for JSON
  7. JSON LinesOne JSON value per line for storing records
  8. Python: json moduleEncoding and decoding JSON

Scale your JSON scrapers

More than 30 million residential IPs behind one endpoint, a new IP per request or sticky sessions, targeting by country, city, ZIP, ISP and ASN at no extra cost, unused bandwidth that rolls over, and 24/7 support from real people. Try it for $1.97.

ProxyEmpire proxy types and prices

ProxyEmpire

Proxies for data collection at scale.

Residential, mobile, ISP and datacenter proxies for scraping, ad verification, SEO monitoring and multi-account work. Choose from 30M+ ethically sourced residential IPs and 4M+ 4G/5G mobile IPs in 170+ countries, target by country, city, ZIP, ISP or carrier, and connect over HTTP(S) or SOCKS5 with 99.9% uptime. Try residential and mobile proxies for $1.97.

🏘️ Rotating Residential Proxies
  • 30M+ Premium Residential IPs
  • Β 170+ Countries
    Every residential IP in our network corresponds to an actual desktop device with a precise geographical location. Our residential proxies are fast and reliable, with 99.9% uptime, and work for a wide range of use cases. You can use country, region, city, ZIP, ISP, ASN and OS fingerprint targeting for our rotating residential proxies, at no extra charge.

See our Rotating Residential Proxies

πŸ“ Static Residential Proxies
  • 24 Countries
    Buy a dedicated static residential IP from one of the 24 countries that we offer proxies in. Keep the same IP for a month or longer, while benefiting from their fast speed and stability.

See our Static Residential Proxies

πŸ“³ Rotating Mobile Proxies
  • 4M+ Premium Mobile IPs
  • Β 170+ Countries
    Access millions of clean mobile IPs with precise targeting by country, region, city, ZIP, mobile carrier, ASN and OS fingerprint, at no extra charge. Get far fewer IP blocks and CAPTCHAs with our 4G and 5G proxies.

See our Mobile Proxies

πŸ“± Dedicated Mobile Proxies
  • 4 Countries
  • 50+ Locations
    Get your own dedicated mobile proxy in one of our supported locations, with unlimited bandwidth and unlimited IP changes on demand. A great choice when you need a small number of mobile IPs and a lot of proxy bandwidth.

See our 4G & 5G Proxies

🌐 Rotating Datacenter Proxies
  • 197,000+ IPs Premium IPs
  • Β 62 Countries
    On a budget and need to do some simple scraping tasks? Our datacenter proxies are the perfect fit! Get started with as little as $2

See our Datacenter Proxies

♾️ Unlimited Residential Proxies
  • 1M+ Daily Residential IPs
  • 85+ Countries
    Our unlimited residential proxies bill by connection speed, not gigabytes: 100 to 1000 Mbit with unlimited bandwidth, on daily, weekly or monthly plans. Ask us for a 1-hour trial at 50 Mbit before you pick a plan.

See our Unlimited Residential Proxies

proxy locations

30M+ rotating IPs

99.9% uptime - high speed

99.9% uptime.

dedicated support team

24/7 Dedicated Support.

fair price

Fair Pricing.

🏠 Residential Proxies Rotating / Static / Unlimited
πŸ“± Mobile Proxies Rotating and Dedicated
πŸ–₯️ Datacenter Proxies Rotating
🌍 IP Pool 30M+ residential + 4M+ mobile IPs
πŸ“Ά Uptime 99.9% Β· Live status
πŸ’³ Payment Card Β· PayPal Β· Crypto Β· Bank transfer
πŸ’¬ Support 24/7 live chat Β· sales@proxyempire.io