Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To find Shopify, WordPress, and HubSpot signals in a list of domains, send each site to a technology lookup API, match its returned technologies against your target list, and save the result and its evidence for every input row. Python can automate the CSV handling and API requests. A missing match is not proof that a company does not use a platform: the lookup can only report signals its provider detects under the selected scan mode.

What this workflow can—and cannot—tell you

Technology lookup services inspect observable website signals. Wappalyzer’s detection specification includes HTML, JavaScript variables, response headers, DOM elements, scripts, and metadata as possible evidence (Wappalyzer detection specification). A returned match is evidence that a detector found a signal associated with a technology; it does not prove that the company uses that tool across every page, subdomain, or internal workflow.

For example, a company may use HubSpot internally without exposing a detectable signal on its public site. A match may also be limited to one subdomain or reflect an older configuration. The sources do not establish a universal recall rate for detecting Shopify, WordPress, or HubSpot, so no script can promise to find every actual user.

  • Detected: the provider returned a target technology under the scan mode you used.
  • No technology returned: the provider returned no target match. Treat this as inconclusive, not as proof the organization does not use it.
  • Lookup failed: the request did not complete successfully, so the domain was not evaluated.
  • Pending: the provider has started an asynchronous crawl but has not yet returned its technology results.

Choose a lookup service and scan mode

Wappalyzer documents an API for technology lookup and lead-list enrichment. Its default lookup accepts one to ten website URLs per request, with a maximum of ten URLs and a rate limit of ten requests per second. The documented default uses cached data; setting live=true requests a real-time scan. Recursive lookup follows internal links for broader coverage. See the Wappalyzer lookup API documentation and its API overview for current behavior and access details.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.

Cached results can be faster to use, but may not reflect the site’s current configuration. Live recursive scans may be more useful when current, broader coverage matters, but they can take longer and use more credits. The API documentation says a crawl can take up to 15 minutes; an initial response may indicate a crawl without listing technologies. Use a callback or check again later rather than marking such a row as a confirmed no-match.

Under the credit scheme described in Wappalyzer’s API documentation, a normal lookup costs one credit per URL. Combining live=true and recursive=true costs five credits per URL. These are documented values, not a guarantee about your account: verify current limits, pricing, and access before budgeting or deploying. Wappalyzer says older verification windows are more likely to include sites that no longer use a technology. Its denoise option excludes low-confidence detections by default; relaxing it can return more results but raises false-positive risk.

Rank #2
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.

BuiltWith offers different paths for different jobs: its Lists API is for discovering websites by technology, while its Domain API checks supplied domains, supports multi-domain lookups, and has a bulk job flow for larger batches. BuiltWith requires an API key. The documented features do not establish an apples-to-apples accuracy comparison with Wappalyzer, so choose based on input size, workflow, evidence fields, access, and cost rather than an unsupported accuracy ranking.

Design an auditable CSV workflow

  1. Read the input without losing row identity. Keep the original domain value and a stable row ID. If two input rows contain the same domain, preserve both unless your own matching rules explicitly call for deduplication.
  2. Normalize carefully. Trim surrounding whitespace, remove a trailing slash, and add a scheme where needed so the provider receives a URL. Do not collapse distinct subdomains into a registrable domain unless that is the matching rule you intend to use.
  3. Batch within the provider’s limits. Send normalized URLs in supported batches, respecting rate limits. Record the provider and scan mode with each request.
  4. Match the returned technologies. Compare technology names or provider slugs against Shopify, WordPress, and HubSpot. Keep the full technology list as well as the target labels so the decision can be reviewed later.
  5. Write an output for every input row. Include a status that distinguishes a detection, no returned technology, request failure, and pending crawl. Save a check timestamp and any provider confirmation timestamp available.
  6. Review consequential or uncertain rows. Manually inspect stale, ambiguous, or commercially important matches before using them to qualify a lead.

Python’s standard library includes csv for reading and writing CSV files and urllib.request for HTTP requests. See the Python CSV documentation and Python urllib.request documentation. You can use a maintained HTTP library instead, but an API key, provider access, or paid plan may be required; Python itself does not make the lookup service free.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

Example Python implementation

This example uses Wappalyzer’s documented lookup endpoint and its cached default mode. Set WAPPALYZER_API_KEY in your environment and confirm the current endpoint, authentication format, and account limits in the provider documentation before running it. The code preserves one output row for each input row and marks errors instead of treating them as no-matches.

import csv
import json
import os
import time
from datetime import datetime, timezone
from urllib.error import HTTPError, URLError
from urllib.parse import urlsplit, urlunsplit
from urllib.request import Request, urlopen

API_KEY = os.environ["WAPPALYZER_API_KEY"]
ENDPOINT = "https://api.wappalyzer.com/v2/lookup/"
TARGETS = {"shopify", "wordpress", "hubspot"}


def normalize_domain(value):
    original = value.strip()
    if not original:
        return ""
    candidate = original if "://" in original else "https://" + original
    parts = urlsplit(candidate)
    return urlunsplit((parts.scheme or "https", parts.netloc, parts.path.rstrip("/"), parts.query, ""))


def chunks(items, size=10):
    for start in range(0, len(items), size):
        yield items[start:start + size]


with open("leads.csv", newline="", encoding="utf-8") as source:
    leads = list(csv.DictReader(source))

results = []
for row_number, lead in enumerate(leads, start=1):
    results.append({
        "row_id": lead.get("id") or str(row_number),
        "original_domain": lead.get("domain", ""),
        "normalized_url": normalize_domain(lead.get("domain", "")),
        "provider": "Wappalyzer",
        "scan_mode": "cached default",
        "detected_technologies": "",
        "target_labels": "",
        "status": "not checked",
        "checked_at_utc": "",
        "provider_confirmed_at": "",
        "error_or_pending": "",
        "raw_response": "",
    })

# Keep a result index per normalized URL so duplicate input rows remain intact.
valid = [(i, r["normalized_url"]) for i, r in enumerate(results) if r["normalized_url"]]
for batch in chunks(valid):
    indices, urls = zip(*batch)
    checked_at = datetime.now(timezone.utc).isoformat()
    body = json.dumps({"urls": list(urls)}).encode("utf-8")
    request = Request(
        ENDPOINT,
        data=body,
        headers={
            "Content-Type": "application/json",
            "x-api-key": API_KEY,
        },
        method="POST",
    )
    try:
        with urlopen(request, timeout=60) as response:
            payload = json.loads(response.read().decode("utf-8"))
        # Provider response fields can vary; inspect the current API schema and
        # adjust this mapping before relying on it in production.
        by_url = {item.get("url", ""): item for item in payload}
        for index, url in zip(indices, urls):
            item = by_url.get(url)
            if item is None:
                results[index]["status"] = "no technology returned"
                results[index]["checked_at_utc"] = checked_at
                continue
            technologies = item.get("technologies", [])
            names = [t.get("name", "") for t in technologies if t.get("name")]
            slugs = [t.get("slug", "") for t in technologies if t.get("slug")]
            labels = sorted({name for name in names if name.casefold() in TARGETS})
            labels.extend(slug for slug in slugs if slug.casefold() in TARGETS and slug not in labels)
            results[index].update({
                "detected_technologies": "; ".join(names),
                "target_labels": "; ".join(labels),
                "status": "detected" if labels else "no technology returned",
                "checked_at_utc": checked_at,
                "provider_confirmed_at": str(item.get("verifiedAt", "")),
                "raw_response": json.dumps(item, ensure_ascii=False),
            })
    except HTTPError as exc:
        message = f"HTTP {exc.code}: {exc.reason}"
        for index in indices:
            results[index].update({"status": "lookup failed", "checked_at_utc": checked_at, "error_or_pending": message})
    except (URLError, TimeoutError, json.JSONDecodeError) as exc:
        message = str(exc)
        for index in indices:
            results[index].update({"status": "lookup failed", "checked_at_utc": checked_at, "error_or_pending": message})

    # Avoid running at the documented maximum request rate.
    time.sleep(0.11)

for result in results:
    if not result["normalized_url"]:
        result["status"] = "lookup failed"
        result["error_or_pending"] = "Empty domain value"

with open("enriched_leads.csv", "w", newline="", encoding="utf-8") as output:
    fields = list(results[0]) if results else ["row_id", "original_domain", "normalized_url", "status"]
    writer = csv.DictWriter(output, fieldnames=fields)
    writer.writeheader()
    writer.writerows(results)

Before using this as a production integration, validate the provider’s current response schema against its documentation. In particular, confirm how it identifies each URL, names its technology and verification-time fields, and represents crawl-pending results. The example’s response mapping is intentionally isolated so it can be adapted without changing the row-preservation and status logic.

Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Store enough evidence to audit each label

Keep the provider’s raw response or a stable reference to it where the provider’s terms permit. A useful output should include the original lead value, normalized URL, provider, scan mode, full detected-technology list, target labels, check time, provider confirmation time if returned, lookup status, and any error or pending information. This is a workflow recommendation, not a schema mandated by either vendor.

Keeping both the full response evidence and the target label helps explain how a row was classified and makes later rechecks possible. Avoid overwriting an old result without retaining its timestamp: a detection is tied to the time and mode of the lookup, not a permanent property of a company.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.