VendorStacks
← All posts
GTM6 min read

Tech Stack Lookup API: Building Vendor Migration Lists for Sales Outreach

A technical guide to identifying companies likely to switch vendors by detecting competitor usage, contract renewal patterns, and multi-vendor overlap with tech stack APIs.

Stax
VendorStacks research desk

Why Vendor Migration Timing Matters

Companies switching from one vendor to another represent high-intent sales opportunities. A company migrating from Salesforce to HubSpot, or evaluating alternatives to their current payment processor, is actively in-market and comparing options. The challenge is identifying these companies before your competitors do.

Traditional sales intelligence focuses on firmographics and intent signals from content consumption. Tech stack data adds a third dimension: what vendors a company currently uses, and whether that usage pattern suggests migration risk. A company using both Stripe and a legacy payment gateway, or running Google Analytics alongside a newer analytics platform, may be mid-evaluation or planning a transition.

This guide covers how to build vendor migration lists programmatically using tech stack lookup APIs, with concrete implementation patterns you can deploy in production.

The Three Migration Signals You Can Detect

1. Competitor Usage Detection

The most direct signal: a company already uses your competitor. If you sell an analytics platform, companies using Google Analytics (315 companies in our index) or similar tools are your addressable market. The vendor_stack field returns all detected vendors across 24 categories, making it straightforward to filter by category and identify targets.

import requests

API_KEY = "vr_live_..."
BASE_URL = "https://api.vendorstacks.com"

def check_competitor_usage(domain, competitor_vendor, category):
    response = requests.get(
        f"{BASE_URL}/v1/check",
        params={"url": domain},
        headers={"Authorization": f"Bearer {API_KEY}"}
    )
    data = response.json()
    
    if not data.get("found"):
        return None  # No public evidence found
    
    vendors_in_category = data["vendor_stack"].get(category, [])
    return competitor_vendor in vendors_in_category

# Example: Check if a prospect uses Google Analytics
uses_ga = check_competitor_usage(
    "example.com",
    "Google Analytics",
    "analytics_data"
)

You're charged 1 credit only if the lookup succeeds and returns vendor data. If no public evidence exists, found returns false and you're charged 0 credits.

2. Multi-Vendor Overlap

Companies running two vendors in the same category often indicate transition periods. A company with both Stripe (171 companies) and another payment processor may be migrating, running a parallel evaluation, or maintaining legacy systems during a gradual cutover.

This pattern is common in several categories:

  • payments: Dual Stripe + legacy gateway usage during migration
  • crm_sales: Overlapping Salesforce (76 companies) and HubSpot (107 companies) during CRM consolidation
  • cloud_infra: AWS (236 companies) + Google Cloud (104 companies) multi-cloud setups that may consolidate
  • ai_ml: Multiple AI vendors (OpenAI at 88 companies plus others) suggesting experimentation
def detect_category_overlap(domain, category, target_vendors):
    response = requests.get(
        f"{BASE_URL}/v1/check",
        params={"url": domain},
        headers={"Authorization": f"Bearer {API_KEY}"}
    )
    data = response.json()
    
    if not data.get("found"):
        return []
    
    vendors_found = data["vendor_stack"].get(category, [])
    overlaps = [v for v in vendors_found if v in target_vendors]
    
    return overlaps if len(overlaps) > 1 else []

# Check if company uses multiple payment processors
payment_vendors = ["Stripe", "Braintree", "Adyen", "Checkout.com"]
overlap = detect_category_overlap("prospect.com", "payments", payment_vendors)

if len(overlap) > 1:
    print(f"Migration signal: using {overlap}")

3. Evidence Freshness and Scan Timing

The scanned_at timestamp tells you when vendor data was last verified. Combining this with evidence fields like dns_evidence, html_evidence, js_evidence, and headers_evidence lets you assess signal strength.

A vendor detected from html_evidence (tags in page source) is typically more current than one found only in dns_evidence (historical DNS records). Multiple evidence types increase confidence that usage is active, not legacy.

def assess_vendor_confidence(check_response, vendor_name):
    if not check_response.get("found"):
        return None
    
    confidence = check_response.get("vendor_confidence", {}).get(vendor_name)
    evidence_types = []
    
    for evidence_field in ["html_evidence", "js_evidence", "headers_evidence", "dns_evidence"]:
        if vendor_name in str(check_response.get(evidence_field, {})):
            evidence_types.append(evidence_field.replace("_evidence", ""))
    
    return {
        "confidence": confidence,
        "evidence_types": evidence_types,
        "scanned_at": check_response.get("scanned_at")
    }

Building a Migration Target List

The reverse lookup endpoint GET /v1/prospect?vendor=X returns companies using a specific vendor, paginated at 10 results per page. Each result in the page costs 1 credit. An empty result set costs 0 credits.

This is the fastest path to building a migration list:

def build_migration_list(competitor_vendor, max_companies=100):
    prospects = []
    page = 1
    
    while len(prospects) < max_companies:
        response = requests.get(
            f"{BASE_URL}/v1/prospect",
            params={"vendor": competitor_vendor, "page": page},
            headers={"Authorization": f"Bearer {API_KEY}"}
        )
        data = response.json()
        
        results = data.get("results", [])
        if not results:
            break  # No more results
        
        prospects.extend(results)
        page += 1
    
    return prospects[:max_companies]

# Build list of companies using Google Analytics
ga_prospects = build_migration_list("Google Analytics", max_companies=50)
print(f"Found {len(ga_prospects)} companies using Google Analytics")
print(f"Credits used: {len(ga_prospects)}")

With 315 companies using Google Analytics in the index, a 50-company migration list costs exactly 50 credits ($0.45 at the 1,100-credit pack rate).

Filtering by Vendor Combinations

The most targeted outreach combines competitor usage with complementary vendor signals. Example: finding companies using Stripe who also use specific CRMs, suggesting they're at a certain company stage and likely to consider payment optimization.

def filter_by_vendor_combination(prospects, required_category, required_vendors):
    filtered = []
    
    for domain in prospects:
        response = requests.get(
            f"{BASE_URL}/v1/check",
            params={"url": domain},
            headers={"Authorization": f"Bearer {API_KEY}"}
        )
        data = response.json()
        
        if not data.get("found"):
            continue
        
        category_vendors = data["vendor_stack"].get(required_category, [])
        if any(v in category_vendors for v in required_vendors):
            filtered.append({
                "domain": domain,
                "vendor_stack": data["vendor_stack"],
                "scanned_at": data["scanned_at"]
            })
    
    return filtered

# Find Stripe users who also use HubSpot or Salesforce
stripe_prospects = build_migration_list("Stripe", max_companies=100)
crm_filtered = filter_by_vendor_combination(
    [p["domain"] for p in stripe_prospects],
    "crm_sales",
    ["HubSpot", "Salesforce"]
)

This pattern costs 1 credit per reverse lookup result plus 1 credit per successful vendor stack check. A 100-company Stripe list refined to 30 CRM users costs 130 credits total.

Cost Management for Large Lists

The GET /v1/company/{domain} endpoint returns cached data without triggering a scan. Use it to check if a company is already indexed before running a live scan with /v1/check:

def get_vendor_stack_cached(domain):
    # Check cached data first (no scan, no charge if not indexed)
    response = requests.get(
        f"{BASE_URL}/v1/company/{domain}",
        headers={"Authorization": f"Bearer {API_KEY}"}
    )
    
    if response.status_code == 200:
        return response.json()  # Cached data, minimal cost
    
    # Not cached - decide whether to trigger live scan
    return None

def get_or_scan(domain, force_scan=False):
    cached = get_vendor_stack_cached(domain)
    if cached and not force_scan:
        return cached
    
    # Trigger live scan (1 credit if successful)
    response = requests.get(
        f"{BASE_URL}/v1/check",
        params={"url": domain},
        headers={"Authorization": f"Bearer {API_KEY}"}
    )
    return response.json()

For batch processing, check the cache first. The 1,000 indexed companies return instantly at minimal cost. Unindexed domains trigger a 15-90 second live scan.

Real-World Implementation Pattern

Here's a complete workflow for generating a weekly migration target list:

import requests
import time
from datetime import datetime, timedelta

API_KEY = "vr_live_..."
BASE_URL = "https://api.vendorstacks.com"

def generate_weekly_migration_list(competitor, category, complementary_vendors=None):
    # Step 1: Get all companies using competitor
    print(f"Building list of companies using {competitor}...")
    prospects = build_migration_list(competitor, max_companies=200)
    
    # Step 2: Filter by recent scan date (last 30 days)
    recent_cutoff = datetime.now() - timedelta(days=30)
    recent_prospects = [
        p for p in prospects 
        if datetime.fromisoformat(p["scanned_at"].replace("Z", "+00:00")) > recent_cutoff
    ]
    
    # Step 3: Filter by complementary vendor if specified
    if complementary_vendors:
        print(f"Filtering by {category} vendors: {complementary_vendors}")
        final_list = filter_by_vendor_combination(
            [p["domain"] for p in recent_prospects],
            category,
            complementary_vendors
        )
    else:
        final_list = recent_prospects
    
    print(f"Final migration list: {len(final_list)} companies")
    return final_list

# Example: Weekly list of Google Analytics users who also use HubSpot
migration_targets = generate_weekly_migration_list(
    competitor="Google Analytics",
    category="crm_sales",
    complementary_vendors=["HubSpot"]
)

Tracking Your Credit Balance

Monitor spending with the balance endpoint:

def check_balance():
    response = requests.get(
        f"{BASE_URL}/v1/balance",
        headers={"Authorization": f"Bearer {API_KEY}"}
    )
    return response.json().get("credit_balance")

print(f"Current balance: {check_balance()} credits")

Every successful lookup response includes credits_used and credit_balance, so you can track spend per request without separate balance checks.

Building This Into Your Sales Workflow

Migration lists are most effective when integrated into existing outreach tools:

  1. Daily prospect enrichment: Run /v1/check on new leads entering your CRM, flagging competitor usage in a custom field
  2. Weekly target generation: Use /v1/prospect to pull fresh lists of competitor users, filtered by complementary vendors
  3. Automated scoring: Weight leads higher when vendor combinations suggest active migration (multi-vendor overlap, recent evidence)

The combination of competitor detection, multi-vendor signals, and evidence freshness gives sales teams migration timing intelligence that's impossible to obtain from firmographic data alone.

Getting Started

Generate an API key with POST /v1/keys to receive 25 free credits instantly:

curl -X POST https://api.vendorstacks.com/v1/keys

No registration required. The response includes your vr_live_... key and initial 25-credit balance. Start with reverse lookup to see the current index coverage for your competitor vendors, then build targeted migration lists from there.

Vendor migration lists work because they target companies with demonstrated need (they use a competitor) and verified budget (they're already paying for the category). Tech stack data makes both signals programmatically accessible.

About the author

Stax is the pangolin who fronts the VendorStacks research desk — a fitting mascot for a company that reads layered stacks for a living. Posts under this byline are written by the VendorStacks team.

VendorStacks is the subprocessor disclosure data API — structured vendor stacks with quoted evidence, 25 free credits to start.

Get an API key