Farfetch is one of the largest luxury fashion marketplaces, connecting shoppers with boutiques and high-end brands across thousands of clothing, shoe, and accessory listings. Its public product pages carry the kind of data that fashion analysts, price trackers, and ecommerce teams care about: which brands are listed, what the items are, how they are priced, and where each one links. Watching that catalog over time tells you how luxury goods are priced across brands and categories and which labels are trending.

This guide shows you how to scrape Farfetch retail data with JavaScript and Node.js using cheerio. You build a small, runnable scraper that fetches a Farfetch listing page through the Crawling API, parses the brand, product name, price, and link for each item, handles pagination, and exports the result as JSON and CSV. The whole walkthrough stays scoped to public product-listing data, and the legality section near the end is not boilerplate, so read it before you point this at any real volume.

What you will build

A Node.js script that takes a public Farfetch category URL, retrieves the rendered HTML through the Crawling API, and extracts a structured record for every product card on the listing. We use a men's shoes category as the running example and pull these fields per item:

  • Brand the luxury brand name shown on the card, for example "Gucci".
  • Description the short product blurb that identifies the item, like "Screener leather sneakers".
  • Price the price as shown on the card, including the currency symbol.
  • Discount the discount label when the item is on sale, otherwise "N/A".
  • Link the absolute URL to the individual product page on farfetch.com.

Why a plain request fails on Farfetch

If you request a Farfetch category URL with a bare HTTP client, you rarely get the product grid back. Farfetch renders its listing cards in the browser with JavaScript, so the initial HTML is a near-empty shell until the page's scripts run. On top of that, Farfetch watches for automated traffic: datacenter IPs and request patterns that do not look like a real browser get challenged, rate-limited, or blocked before they reach the rendered product data.

So a working Farfetch scraper needs two things in one request: a browser that actually renders the page, and an IP the platform reads as a real visitor. You can assemble that yourself with a headless browser plus a pool of rotating residential proxies, but stitching those together and keeping them healthy is most of the work. The Crawling API folds both into a single call: you send it the URL, it renders the page behind a trusted IP, and it returns finished HTML for you to parse with cheerio.

Wait for the content

Because Farfetch loads its grid client-side, you tell the Crawling API to wait for the JavaScript to settle before it captures the HTML. Set ajax_wait: 'true' and a page_wait of a few thousand milliseconds so the product cards are present in the markup you get back. Rendering uses the JavaScript request type, which is why these listings need the JS token rather than a plain request.

Prerequisites

You need a few things in place before writing any code. None of them take long.

Basic JavaScript and Node.js. You should be comfortable writing and running a Node script and installing packages with npm. If you are new to Node, the official docs and any beginner course will get you to the level this tutorial assumes. The guide to building a web scraper with Node.js is a good warm-up if you want one.

Node.js 16 or later. Confirm your version with node --version. If you do not have it, install it from the Node.js website or through a version manager like nvm.

A Crawlbase account and token. Sign up, open your dashboard, and copy your token. The free tier gives you 1,000 requests with no card. Farfetch needs rendering, so use your JavaScript token for these requests. Treat the token like a password: it authenticates your requests, so keep it out of version control.

Set up the project

Create a project folder, initialize it, and install the two libraries the scraper needs.

bash
node --version

mkdir farfetch-scraper && cd farfetch-scraper
npm init -y

npm install crawlbase cheerio

Two dependencies do the work: crawlbase is the official Node client for the Crawling API, and cheerio parses the returned HTML with a jQuery-style API so you can pull out individual fields by CSS selector. Create a file named farfetch-scraper.js in this folder and add the code from the steps below.

Step 1: Fetch the rendered listing page

Start by getting the finished page. Import the CrawlingAPI class, initialize it with your token, and request the category URL with the wait options so the cards are rendered before the HTML is captured. Checking the status code before you parse keeps failures loud instead of silent.

javascript
const { CrawlingAPI } = require('crawlbase');

const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' });

const options = {
  ajax_wait: 'true',    // wait for the JavaScript grid to load
  page_wait: '5000'     // give the page 5 seconds to settle
};

const listingURL =
  'https://www.farfetch.com/shopping/men/shoes-2/items.aspx';

api
  .get(listingURL, options)
  .then((response) => {
    if (response.statusCode === 200) {
      console.log(response.body.slice(0, 500));
    }
  })
  .catch((error) => console.error('API request error:', error));

Run the script with node farfetch-scraper.js and you should see real Farfetch product markup near the top of the body, not a stripped-down shell. That confirms rendering works before you write a single selector. The ajax_wait and page_wait options are what give the client-side grid time to populate; you can read more about the available request parameters in the Crawling API docs.

Crawlbase Crawling API

That first request just returned a fully rendered Farfetch listing without a headless browser or a proxy on your side. The Crawling API runs the page in a real browser, waits for the JavaScript grid to load with ajax_wait and page_wait, rotates through residential IPs server-side, and handles the challenges Farfetch throws at scrapers, so you get finished HTML from one call. Point it at a men's shoes category on the free tier first.

Step 2: Parse each product card with cheerio

With rendered HTML in hand, load it into cheerio and walk the product cards. Farfetch lays each item out as a list element inside the catalog grid, so you select every card, then read the brand, description, price, discount, and link from inside it. Reading each field defensively keeps one missing value from crashing the run.

javascript
const cheerio = require('cheerio');

function parseListings(html) {
  const $ = cheerio.load(html);
  const products = [];

  const cards = $(
    'ul#catalog-grid > li[data-testid="productCard"]'
  );

  cards.each((index, element) => {
    const card = $(element);

    const brand = card
      .find('p[data-component="ProductCardBrandName"]')
      .text()
      .trim() || 'N/A';

    const description = card
      .find('p[data-component="ProductCardDescription"]')
      .text()
      .trim() || 'N/A';

    const price = card
      .find('p[data-component="Price"], p[data-component="PriceFinal"]')
      .first()
      .text()
      .trim() || 'N/A';

    const discount = card
      .find('p[data-component="PriceDiscount"]')
      .text()
      .trim() || 'N/A';

    const href = card.find('a').first().attr('href');
    const link = href
      ? new URL(href, 'https://www.farfetch.com').href
      : 'N/A';

    products.push({ brand, description, price, discount, link });
  });

  return products;
}

The selectors come straight from the page. Each card is a li[data-testid="productCard"] inside ul#catalog-grid. The brand sits in a p tagged data-component="ProductCardBrandName", the description in ProductCardDescription, and the price in Price or PriceFinal, so the selector matches either and takes the first. When an item is on sale, the discount label lives in PriceDiscount. The product link is the card's anchor href, which Farfetch serves as a relative path, so it is resolved against https://www.farfetch.com to give an absolute URL that works outside the page.

Selectors drift

Farfetch's data-component and data-testid attributes are stable handles, but markup changes over time. Treat the selectors above as a starting template, not a contract. When a field comes back as "N/A" across the board, re-inspect the live page in your browser's dev tools and update the selector. Periodic selector maintenance is normal for any production scraper, not a sign something is broken.

Step 3: Handle pagination

Farfetch spreads its listings across many pages. To collect a full category, walk the pages by appending a page parameter to the URL and run the same fetch-and-parse on each one, then gather everything into a single list.

javascript
async function crawl(pageUrl) {
  const response = await api.get(pageUrl, options);
  if (response.statusCode === 200) return response.body;
  console.error(`Request failed: ${response.statusCode}`);
  return null;
}

async function scrapeMultiplePages(baseUrl, totalPages) {
  const allProducts = [];

  for (let page = 1; page <= totalPages; page++) {
    const paginatedUrl = `${baseUrl}?page=${page}`;
    console.log(`Scraping page: ${page}`);
    const html = await crawl(paginatedUrl);
    if (!html) continue;
    allProducts.push(...parseListings(html));
  }

  return allProducts;
}

This loops from page 1 through totalPages, building each page URL as ?page=N, fetching it through the Crawling API with the same wait options, parsing the cards, and spreading the results into one combined array. Because every category page shares the same card structure, the parseListings function you wrote in Step 2 works on all of them without changes.

Step 4: Assemble the full script with JSON and CSV export

Now wire the fetch, the parse, and the pagination into one runnable script, then write the records to disk as both JSON and CSV.

javascript
const fs = require('fs');
const { CrawlingAPI } = require('crawlbase');
const cheerio = require('cheerio');

const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' });

const options = { ajax_wait: 'true', page_wait: '5000' };

async function crawl(pageUrl) {
  const response = await api.get(pageUrl, options);
  if (response.statusCode === 200) return response.body;
  console.error(`Request failed: ${response.statusCode}`);
  return null;
}

function parseListings(html) {
  const $ = cheerio.load(html);
  const products = [];

  $('ul#catalog-grid > li[data-testid="productCard"]').each((i, el) => {
    const card = $(el);
    const brand = card.find('p[data-component="ProductCardBrandName"]').text().trim() || 'N/A';
    const description = card.find('p[data-component="ProductCardDescription"]').text().trim() || 'N/A';
    const price = card.find('p[data-component="Price"], p[data-component="PriceFinal"]').first().text().trim() || 'N/A';
    const discount = card.find('p[data-component="PriceDiscount"]').text().trim() || 'N/A';
    const href = card.find('a').first().attr('href');
    const link = href ? new URL(href, 'https://www.farfetch.com').href : 'N/A';

    products.push({ brand, description, price, discount, link });
  });

  return products;
}

async function scrapeMultiplePages(baseUrl, totalPages) {
  const allProducts = [];
  for (let page = 1; page <= totalPages; page++) {
    console.log(`Scraping page: ${page}`);
    const html = await crawl(`${baseUrl}?page=${page}`);
    if (html) allProducts.push(...parseListings(html));
  }
  return allProducts;
}

function toCsv(rows) {
  const headers = ['brand', 'description', 'price', 'discount', 'link'];
  const escape = (value) => `"${String(value).replace(/"/g, '""')}"`;
  const lines = [headers.join(',')];
  for (const row of rows) {
    lines.push(headers.map((h) => escape(row[h])).join(','));
  }
  return lines.join('\n');
}

async function main() {
  const baseUrl = 'https://www.farfetch.com/shopping/men/shoes-2/items.aspx';
  const products = await scrapeMultiplePages(baseUrl, 5);
  if (!products.length) return;

  fs.writeFileSync('farfetch_listings.json', JSON.stringify(products, null, 2));
  fs.writeFileSync('farfetch_listings.csv', toCsv(products));
  console.log(`Saved ${products.length} products to JSON and CSV`);
}

main();

Run it with node farfetch-scraper.js and you get two files: farfetch_listings.json with the full structured records and farfetch_listings.csv ready to open in a spreadsheet. The toCsv helper quotes every field and doubles any embedded quotes, which matters here because brand names and product descriptions frequently contain commas.

What the output looks like

The JSON file holds one object per product card, each with the brand, description, price, discount, and link.

json
[
  {
    "brand": "Gucci",
    "description": "Screener leather sneakers",
    "price": "$890",
    "discount": "N/A",
    "link": "https://www.farfetch.com/shopping/men/gucci-screener-sneakers-item-27582236.aspx"
  },
  {
    "brand": "Common Projects",
    "description": "Original Achilles low-top sneakers",
    "price": "$425",
    "discount": "30% off",
    "link": "https://www.farfetch.com/shopping/men/common-projects-original-achilles-item-12345678.aspx"
  }
]

The CSV mirrors the same rows with a header line, so it drops straight into Excel, Google Sheets, or any data pipeline that reads delimited files.

csv
brand,description,price,discount,link
"Gucci","Screener leather sneakers","$890","N/A","https://www.farfetch.com/shopping/men/gucci-screener-sneakers-item-27582236.aspx"
"Common Projects","Original Achilles low-top sneakers","$425","30% off","https://www.farfetch.com/shopping/men/common-projects-original-achilles-item-12345678.aspx"

Scale across categories

One category is a demo; a real job walks several Farfetch departments. Each category lives under its own listing path, for example men's shoes, women's bags, or kids' clothing, so you can build a list of category URLs, run scrapeMultiplePages on each, and tag every row with its category before you export. Because every listing page shares the same card structure, the parser you already wrote works across all of them without changes.

This pattern carries straight into pricing and product research. For the broader picture of pulling structured data out of online stores, see the guide to ecommerce web scraping, and for turning these prices into decisions, see how to use web scraping for price intelligence. If you want the same approach applied to another fashion retailer, the walkthrough on how to create a Zalando scraper covers a similar JavaScript-heavy catalog.

Staying unblocked

Even with rendering handled, Farfetch watches for scraper-shaped traffic. A few habits keep a run healthy, and they apply to any hard commercial target.

  • Pace your requests. Introduce a delay between page fetches rather than hammering the catalog in a tight loop. Spreading requests out is the single biggest factor in staying under a site's rate limits.
  • Lean on rotation. A pool of residential IPs spreads requests across many real-user addresses so no single one trips a limit or a challenge. The Crawling API handles this for you; if you roll your own stack, this is the part to get right.
  • Read the status codes. A run that starts returning challenges or non-200 responses is telling you the current rate or IP tier is no longer enough. Treat that as signal to back off, not noise to ignore.

For the broader playbook on this, see how to scrape websites without getting blocked, and for the mechanics of why these catalogs need a real browser in the first place, the guide on how to crawl JavaScript websites goes deeper.

Whether scraping Farfetch is allowed depends on Farfetch's terms of service, your jurisdiction, and what you do with the data. Farfetch's terms restrict automated access, so scraping can run against those terms regardless of how careful your tooling is. None of the code here changes that; it just makes the technical part work. Read Farfetch's Terms and Conditions and its robots.txt, and treat both as the boundary for what you collect.

A few lines worth holding to. Collect only public product data: the brand, description, price, discount, and product link that anyone can see on a category page without an account. Respect Farfetch's stated rate expectations and keep your request volume low enough that you are not straining its servers. Avoid personal data, including anything tied to identifiable reviewers beyond the public text shown on the page. Do not redistribute Farfetch's copyrighted media, such as product photography, as if it were your own. If you plan to reuse the data commercially, get permission or an official agreement rather than assuming silence is consent.

For volume or commercial use, the right move is to look for a sanctioned channel. Farfetch runs partner and affiliate programs through its boutique and brand relationships, and those are the correct route when you need large volumes, guaranteed structure, or commercial rights. This guide is deliberately scoped to public listing data because that is the line that keeps the work defensible. It does not cover anything behind a login, customer or boutique personal data, order history, or any attempt to bypass authentication or a challenge you were not meant to pass. If your project needs more than public listings, an official partnership or a data agreement is the correct path, not a cleverer scraper.

Recap

Key takeaways

  • Farfetch renders listings client-side and blocks hard. A plain request returns an empty shell, so you must render the page behind a trusted IP, with ajax_wait and page_wait set, before you parse it.
  • The Crawling API does the heavy lifting in one call. It renders the page, waits for the JavaScript grid, rotates residential IPs, and handles challenges, returning finished HTML you parse with cheerio.
  • cheerio extracts the fields. Select every li[data-testid="productCard"], then read brand, description, price, discount, and the absolute product link from inside each card.
  • Paginate and export. Walk pages with ?page=N, gather the records, and write them to JSON and CSV, quoting CSV fields so comma-heavy brand names and descriptions stay intact.
  • Stay on public data. Respect Farfetch's terms and robots.txt, pace your requests, and prefer an official partnership or affiliate channel for volume or commercial use.

Frequently Asked Questions (FAQs)

How do I handle JavaScript content when scraping Farfetch?

Farfetch loads its product grid client-side, so the raw HTML is nearly empty until the page's scripts run. Route the request through the Crawling API with ajax_wait: 'true' and a page_wait of a few thousand milliseconds, which gives the JavaScript time to render before the HTML is captured. Use your JavaScript token for these requests, since rendering is a JS request type.

Can I scrape product details from multiple pages on Farfetch?

Yes. Farfetch paginates its categories, so you append a page parameter to the listing URL, for example ?page=2, and loop through the pages you want. Run the same fetch-and-parse on each page and combine the results into one array, as the scrapeMultiplePages function in this guide does. Routing every page through the Crawling API keeps the rotation and challenge handling consistent across the run.

Which fields does this scraper extract?

For each product card it pulls the brand from ProductCardBrandName, the description from ProductCardDescription, the price from Price or PriceFinal, the discount label from PriceDiscount, and the product link from the card's anchor, resolved to an absolute farfetch.com URL. Missing values fall back to "N/A" so a partial card never breaks the run.

My selectors return "N/A" for every card. What changed?

Almost certainly Farfetch's markup. Its data-component and data-testid handles are fairly stable but can change, and a layout update can move a field. Re-inspect a live category page in your browser's dev tools and update the selectors to match. Periodic selector maintenance is normal for any production scraper.

How do I store the scraped data?

Save it in a structured format like JSON or CSV. This guide writes both: farfetch_listings.json for downstream code and farfetch_listings.csv for spreadsheets and BI tools. The CSV writer quotes each field and escapes embedded quotes so commas inside brand names or descriptions do not shift the columns.

Can I scrape personal data from Farfetch?

No, and this guide does not cover it. Customer account details, order history, and anything behind a login are not public data. Scraping login-walled content, personal data about reviewers beyond the public text shown on the page, or bypassing authentication is out of scope here and runs against Farfetch's terms. For sanctioned access the correct route is an official partnership or data agreement.

Start Building

Crawl any site at scale, without fighting infrastructure.

Crawlbase handles proxies, fingerprints, and CAPTCHAs so your team ships data pipelines instead of maintaining crawl plumbing. 1,000 requests free, no card required.

Self-serve · No sales call required · Enterprise crawl volumes available