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.
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:
- Daily prospect enrichment: Run
/v1/checkon new leads entering your CRM, flagging competitor usage in a custom field - Weekly target generation: Use
/v1/prospectto pull fresh lists of competitor users, filtered by complementary vendors - 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.