Extract Commercial Listing Titles, Addresses, and URLs

Site apirealestate.comTask extract-commercial-listing-urls-and-paginationVersion v2Updated Nov 6, 2026Category real-estate

Extract commercial listing titles, addresses, and detail URLs from the first page of 24 listings in APi Real Estate's expanded listings view. This skill was captured from a live agent session on apirealestate.com and is published here as a reusable recipe for agents.

NoteSelectors and URL schemes drift as sites change. A skill is a snapshot of what worked when it was captured, not a contract — agents re-learn it when it stops working.

Collect commercial listing titles, addresses, and detail URLs from the first page of 24 listings in APi Real Estate's expanded 10,000+ property results view. Pagination is not currently working; report this as a partial first page, not the full catalogue.

Use Cases

  • Collect commercial listing titles, addresses, and detail URLs from the first page of 24 listings.
  • Expand the commercial landing page into its full listings results view.
  • Re-run extraction after lazy-loaded listing cards appear.

Automation Flow

  1. Navigate to https://www.apirealestate.com/commercial-listings with a residential proxy.
  2. Solve the reCAPTCHA if presented, then read the href of a[aria-label="More Listings"] and goto it (/listing?searchId=…; never invent the searchId). Clicking the link does not navigate. On the /listing page, wait for a[href*="/listing-detail/"] with a 45000 ms timeout, then wait another 3000 ms: the results render slowly.
  3. Run this evaluator on the expanded results view:
(async () => {
  for (let i = 0; i < 12; i++) {
    const before = document.querySelectorAll('a[href*="/listing-detail/"]').length;
    window.scrollTo(0, document.body.scrollHeight);
    await new Promise(r => setTimeout(r, 1000));
    const after = document.querySelectorAll('a[href*="/listing-detail/"]').length;
    if (after === before) break;
  }
  const seen = new Set();
  const listings = [...document.querySelectorAll('a[href*="/listing-detail/"]')].map(a => {
    const url = new URL(a.getAttribute('href'), location.href);
    const key = url.origin + url.pathname;
    if (seen.has(key)) return null;
    seen.add(key);
    const article = a.closest('article');
    const heading = article?.querySelector('h3, h2, [role="heading"]');
    const title = (heading?.innerText || a.innerText || '').trim().replace(/\s+/g, ' ');
    const address = (a.innerText || article?.querySelector('h3, h2, [role="heading"]')?.innerText || '').trim().replace(/\s+/g, ' ');
    return { title, address, detailUrl: key };
  }).filter(x => x && x.detailUrl);
  const paginationLinks = [...document.querySelectorAll('a[rel="next"], a[rel="prev"], a[aria-label*="next" i], a[aria-label*="previous" i], .pagination a, [data-page-next] a, [data-page-next]')]
    .map(a => { try { return new URL(a.getAttribute('href'), location.href).href; } catch { return null; } })
    .filter(u => u && new URL(u).origin === location.origin && !u.includes('/listing-detail/'))
    .filter((u, i, all) => all.indexOf(u) === i);
  return { listings, paginationLinks };
})();
  1. Follow each returned genuine pagination URL and repeat the evaluator until no new result-page URL remains; if paginationLinks is empty, do not invent page parameters.

Params

ParamWhat it doesExample value
commercial listings URLLanding page containing the full-results expansion linkhttps://www.apirealestate.com/commercial-listings
expansion selectorOpens the 10,000+ property results viewa[aria-label="More Listings"]
lazy-load waitDelay between result-list scrolls1000 ms

Possible Friction Points

  • Non-www host returns a network error → use https://www.apirealestate.com/commercial-listings with a residential proxy.
  • reCAPTCHA appears → solve it before clicking More Listings.
  • Commercial landing page shows 3 featured cards with /listing-detail/ links → they satisfy the wait selector, so confirm the URL path is /listing before extracting; otherwise only the featured cards come back.
  • Navigation links are mistaken for pagination → accept only explicit next/previous/pagination controls and do not guess page URLs when none are present.
  • The results pager is li.page-num items with no href (24 listings per page, ~417 pages). Clicking them did not advance the page under automation, so paginationLinks is empty and the recipe returns the first 24 listings only → report the result as a partial first page, not the full catalogue.
  • Listing cards carry only an address → title repeats address; detail links carry a ~1,000-character tracking query string, so detailUrl is returned as origin + path.
  • Listing cards are lazy-loaded → use the included bounded scroll loop before extraction.

Example Result

{
  "listings": [
    {
      "title": "Example Title",
      "address": "Example Address",
      "detailUrl": "https://example.com/listing-detail/example"
    }
  ],
  "paginationLinks": [
    "https://example.com/page/2"
  ]
}

Call it

GET https://production-sfo.browserless.io/skills?token=TOKEN-HERE&domain=apirealestate.com&task=extract-commercial-listing-urls-and-pagination