VendorStacks
← All posts
Engineering6 min read

Tech Stack Lookup API Integration Guide: Adding Vendor Detection to Your Product

A practical guide for engineering teams integrating tech stack lookup into CRMs, sales tools, and data platforms, with code examples and architectural patterns.

Stax
VendorStacks research desk

Tech Stack Lookup API Integration Guide: Adding Vendor Detection to Your Product

If you're building a CRM, sales intelligence platform, or marketing tool, adding tech stack visibility can transform how your users qualify leads and prioritize outreach. This guide walks through integrating a tech stack lookup API into your product, with real code examples and architectural decisions based on what actually works.

Why Product Teams Add Tech Stack Lookup

Three common integration patterns:

Enrichment at record creation: When a user adds a company to your CRM, automatically populate vendor data fields. Your sales team sees "Uses Stripe, Salesforce, AWS" without leaving the interface.

Batch processing existing records: Run your existing company database through vendor detection to segment by technology. Sales can filter for "companies using HubSpot but not Salesforce" or "Stripe users in Series A."

Reverse lookup for prospecting: Let users search "show me companies using Snowflake" to build targeted lists. Especially valuable for vendors selling integrations or competitive replacements.

Basic Integration: Single Company Lookup

The simplest integration checks a company's vendor stack when needed. Here's the core request:

const response = await fetch(
  `https://api.vendorstacks.com/v1/check?url=${encodeURIComponent(domain)}`,
  {
    headers: {
      'Authorization': `Bearer ${process.env.VENDORSTACKS_API_KEY}`,
      'Content-Type': 'application/json'
    }
  }
);

const data = await response.json();

if (data.found) {
  console.log(`Credits used: ${data.credits_used}`);
  console.log(`Vendors found: ${Object.keys(data.vendor_stack).length}`);
  console.log(`Scanned at: ${data.scanned_at}`);
} else {
  console.log('No public vendor evidence located');
  console.log(`Credits used: ${data.credits_used}`); // Will be 0
}

Key behavior: found: false means no public evidence was detected, not that the company uses no vendors. This costs 0 credits. You only pay for successful lookups that return data.

The response includes 24 vendor categories, from ai_ml and payments to cloud_infra and crm_sales. Each category contains an array of vendor objects with confidence scores and evidence.

Handling Indexed vs Unindexed Companies

VendorStacks maintains an index of 1,000 companies covering 317 distinct vendors. Indexed lookups return in under 1 second. Unindexed domains trigger a live scan that takes 15-90 seconds.

Your integration needs to handle both:

import requests
import time

def get_vendor_stack(domain, max_wait=90):
    url = f"https://api.vendorstacks.com/v1/check?url={domain}"
    headers = {"Authorization": f"Bearer {api_key}"}
    
    response = requests.get(url, headers=headers)
    data = response.json()
    
    # Indexed company - instant result
    if data.get('scanned_at') or not data.get('found'):
        return data
    
    # Unindexed - scan triggered, wait for completion
    start = time.time()
    while time.time() - start < max_wait:
        time.sleep(5)
        response = requests.get(url, headers=headers)
        data = response.json()
        if data.get('scanned_at'):
            return data
    
    raise TimeoutError(f"Scan did not complete within {max_wait}s")

For background processing (nightly enrichment jobs), you can trigger scans and process results asynchronously. For user-facing features ("enrich this lead now"), either check if it's indexed first or show a loading state.

Batch Processing Architecture

Enriching thousands of existing company records requires different logic:

import asyncio
import aiohttp

async def check_domain(session, domain):
    url = f"https://api.vendorstacks.com/v1/check?url={domain}"
    headers = {"Authorization": f"Bearer {api_key}"}
    
    async with session.get(url, headers=headers) as response:
        data = await response.json()
        return {
            'domain': domain,
            'vendors': data.get('vendor_stack', {}),
            'found': data.get('found', False),
            'credits_used': data.get('credits_used', 0)
        }

async def batch_enrich(domains, concurrency=10):
    connector = aiohttp.TCPConnector(limit=concurrency)
    async with aiohttp.ClientSession(connector=connector) as session:
        tasks = [check_domain(session, d) for d in domains]
        return await asyncio.gather(*tasks)

# Usage
domains = ["stripe.com", "snowflake.com", "anthropic.com"]
results = asyncio.run(batch_enrich(domains))
total_credits = sum(r['credits_used'] for r in results)
print(f"Enriched {len(domains)} companies using {total_credits} credits")

This parallelizes requests while respecting rate limits. For very large batches (10,000+ companies), process in chunks and store results incrementally.

Reverse Lookup for Prospecting Features

The /v1/prospect endpoint lets users find companies using specific vendors. This is especially powerful for:

  • Integration partners finding potential customers
  • Competitive intelligence ("who uses our competitor?")
  • Account-based marketing list building
const searchCompaniesUsingVendor = async (vendorName, page = 1) => {
  const response = await fetch(
    `https://api.vendorstacks.com/v1/prospect?vendor=${encodeURIComponent(vendorName)}&page=${page}`,
    {
      headers: {
        'Authorization': `Bearer ${apiKey}`
      }
    }
  );
  
  const data = await response.json();
  
  return {
    companies: data.results,  // Up to 10 per page
    creditsUsed: data.credits_used,  // 1 credit per result returned
    hasMore: data.results.length === 10
  };
};

// Example: Find Snowflake users
const snowflakeUsers = await searchCompaniesUsingVendor('Snowflake');
console.log(`Found ${snowflakeUsers.companies.length} companies`);
console.log(`Cost: ${snowflakeUsers.creditsUsed} credits`);

Crucial pricing detail: you pay 1 credit per result returned, not per search. An empty search costs 0 credits. A search returning 10 companies costs 10 credits.

From the current index, searching for "Stripe" would return companies from the 146 indexed Stripe users, "Snowflake" from 88 users, "OpenAI" from 99 users. As the index grows, these counts increase.

Storing and Updating Vendor Data

Vendor stacks change as companies adopt new tools. Design your database schema to handle updates:

CREATE TABLE company_vendors (
  company_id INTEGER REFERENCES companies(id),
  vendor_category VARCHAR(50),
  vendor_name VARCHAR(255),
  confidence VARCHAR(20),
  evidence_url TEXT,
  detected_at TIMESTAMP,
  last_checked TIMESTAMP,
  PRIMARY KEY (company_id, vendor_category, vendor_name)
);

CREATE INDEX idx_vendor_lookup ON company_vendors(vendor_name);

This schema supports both "what does Company X use?" queries and reverse lookups ("who uses Vendor Y?"). The last_checked field lets you refresh stale data:

def needs_refresh(company):
    if not company.vendor_data:
        return True
    days_old = (datetime.now() - company.last_checked).days
    return days_old > 90  # Refresh quarterly

Cost Management Strategies

At 1 credit per successful lookup, costs scale with usage. Some teams enrich every company immediately. Others optimize:

Lazy enrichment: Only look up vendor data when a user views the company record. Cache the result. Most CRM records never get opened.

Tiered refresh: Check high-priority accounts (active deals, large customers) monthly. Check dormant records annually or never.

Selective categories: If you only care about payments and CRM vendors, filter the response and ignore other categories. You still pay the same per lookup, but storage and UI are simpler.

const paymentsAndCRM = ['payments', 'crm_sales'];
const relevantVendors = Object.entries(data.vendor_stack)
  .filter(([category, vendors]) => paymentsAndCRM.includes(category))
  .reduce((acc, [cat, vendors]) => ({ ...acc, [cat]: vendors }), {});

Handling Evidence and Confidence Scores

Each vendor detection includes:

  • vendor_confidence: high, medium, or low
  • {category}_evidence: URLs showing where the vendor was detected
  • subprocessor_urls: privacy policy references if applicable

For user-facing features, showing evidence builds trust:

<VendorBadge 
  name="Stripe"
  confidence="high"
  evidence="https://example.com/privacy"
  tooltip="Detected in privacy policy subprocessor list"
/>

Low-confidence detections might be candidates for manual review before using them in sales outreach.

API Key Management

Get started instantly:

curl -X POST https://api.vendorstacks.com/v1/keys \
  -H "Content-Type: application/json"

This returns a vr_live_ key with 25 free credits. No email required. For production, track usage:

def check_balance():
    response = requests.get(
        "https://api.vendorstacks.com/v1/balance",
        headers={"Authorization": f"Bearer {api_key}"}
    )
    data = response.json()
    if data['credit_balance'] < 100:
        notify_team("VendorStacks credits running low")
    return data['credit_balance']

Real Integration Example

Here's how a CRM might auto-populate vendor fields:

interface Company {
  domain: string;
  name: string;
  vendors?: VendorStack;
}

async function enrichCompanyOnCreate(company: Company) {
  try {
    const response = await fetch(
      `https://api.vendorstacks.com/v1/check?url=${company.domain}`,
      { headers: { 'Authorization': `Bearer ${apiKey}` } }
    );
    
    const data = await response.json();
    
    if (data.found) {
      await db.companies.update(company.id, {
        vendor_stack: data.vendor_stack,
        vendor_data_updated: new Date(data.scanned_at),
        enrichment_credits_used: data.credits_used
      });
      
      // Create vendor tags for filtering
      const allVendors = Object.values(data.vendor_stack)
        .flat()
        .map(v => v.name);
      
      await db.companyTags.createMany(
        allVendors.map(name => ({
          company_id: company.id,
          tag: `uses:${name.toLowerCase()}`
        }))
      );
    }
  } catch (error) {
    console.error('Vendor enrichment failed:', error);
    // Don't block company creation on enrichment failure
  }
}

Now sales reps can filter companies with "uses:stripe" or "uses:salesforce" tags, and deal records show the full tech stack without manual research.

What This Enables

With tech stack lookup integrated, your users can:

  • Qualify inbound leads faster ("they use Salesforce, route to enterprise team")
  • Build targeted outbound lists ("find Series B companies using Snowflake")
  • Track technology adoption trends across their customer base
  • Prioritize competitive displacement opportunities
  • Personalize outreach with specific integration mentions

The technical lift is minimal—a few API calls and database fields. The product value compounds as your user base grows and discovers new ways to segment by technology.

From the current VendorStacks index: 319 companies use Google Analytics, 257 use AWS, 146 use Stripe, 99 use OpenAI. As you integrate vendor detection, your users can instantly identify and act on these technology signals without manual research.

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