# Company Tech Stack Scraper — BuiltWith Alternative (`devilscrapes/builtwith-alternative-scraper`) Actor

A BuiltWith & Wappalyzer alternative: detect any company's real tech stack (Postgres, Django, AWS, Kubernetes, Snowflake…) from Greenhouse, Lever & Ashby job posts. 110+ frameworks, databases and cloud tools, export to JSON or CSV. B2B sales & recruiter intel — pay-per-result, no SaaS bill.

- **URL**: https://apify.com/devilscrapes/builtwith-alternative-scraper.md
- **Developed by:** [DevilScrapes](https://apify.com/devilscrapes) (community)
- **Categories:** Jobs
- **Stats:** 3 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

<img src="https://apify.com/.actor/icon.svg" width="160" alt="ATS Tech Stack Detector" />

## Company Tech Stack Scraper — a BuiltWith & Wappalyzer Alternative

▶️ **[Full tutorial on YouTube](https://youtu.be/C5hMiLWpE3Q)**

▶️ **[45-second demo on YouTube](https://youtube.com/shorts/F89DtLqomYk)**

**$5.20 / 1 000 rows**  ·  pay only for results  ·  no credit card to try

*We do the dirty work so your dataset stays clean.* 😈

**A free BuiltWith / Wappalyzer alternative — for the back-end stack.** Point this company tech stack scraper at any company's Greenhouse, Lever, or Ashby job board and get one structured row per active job, including a deduplicated list of canonical tech names (Postgres, Django, AWS, Kubernetes, Snowflake…) detected directly from the job description.

BuiltWith, Wappalyzer, and TheirStack charge a subscription and sniff **front-end** signals — JavaScript libraries, tracking pixels, CDN headers. They are blind to the **back-end**: the databases, queues, cloud, and infra that engineering teams declare in their own job posts. This scraper reads exactly that — the place where companies tell you what they really run — and bills per result instead of per seat.

Sales reps qualifying accounts, recruiters sourcing by skill, and competitive-intelligence analysts mapping vendor footprint all need this signal. Use it as a standalone technographics source, or alongside a front-end tool for full-stack coverage.

### ⚖️ BuiltWith & Wappalyzer alternative — how it compares

A quick, honest comparison. These tools don't fully overlap — they read different halves of the stack — but if you're paying a subscription for back-end signal, this is the cheaper, more accurate source.

| | This Actor | BuiltWith | Wappalyzer | TheirStack |
|---|---|---|---|---|
| **What it reads** | Back-end stack from job descriptions | Front-end JS / pixels / headers | Front-end JS / pixels / headers | Job-post & web signals |
| **Sees Postgres, Kafka, Snowflake, K8s?** | ✅ Yes | ❌ No | ❌ No | ⚠️ Partial |
| **Sees HubSpot, Segment, Cloudflare?** | ❌ No (use a front-end tool) | ✅ Yes | ✅ Yes | ⚠️ Partial |
| **Pricing** | Pay-per-result (~$0.30 / 50-job run) | Subscription | Subscription | Subscription |
| **Login / account required** | ❌ No | Account | Account | Account |
| **Export** | JSON / CSV (Apify dataset) | CSV (paid tiers) | CSV (paid tiers) | CSV / API |
| **Best for** | Back-end technographics, hiring-signal intel | Marketing-stack detection | Marketing-stack detection | Broad firmographics |

**TL;DR:** if you want to know whether a company runs **Django + Postgres + AWS** (and you don't want a SaaS seat to find out), this is your tool. If you want their **marketing stack**, pair it with a front-end sniffer.

### 🎯 What this scrapes

Three ATS platforms, one unified schema:

1. **Greenhouse** — `boards-api.greenhouse.io/v1/boards/{token}/jobs?content=true`
2. **Lever** — `api.lever.co/v0/postings/{token}`
3. **Ashby** — `api.ashbyhq.com/posting-api/job-board/{token}` (token is **case-sensitive** — `Ramp`, not `ramp`)

For each active job posting the Actor pulls the title, location, department, full description, and public URL — then runs a case-insensitive word-boundary regex against a curated vocabulary of ~110 canonical tech names. The result is a single `detected_techs` column you can pivot or filter without any per-company string normalisation.

### 🔥 Features

- **Three ATSs, one schema** — Greenhouse, Lever, and Ashby normalised to an identical row shape.
- **Curated tech vocabulary** — ~110 canonical names spanning languages (Python, Go, Rust), frameworks (Django, React, FastAPI), databases (Postgres, MongoDB, Redis, Snowflake), cloud (AWS, GCP, Azure, Vercel), infra (Kubernetes, Terraform, Docker), CI/CD, observability, and ML/data tools.
- **Per-company fault isolation** — one bad token (404 from Lever, typo on Ashby) does not abort the run; the other companies still produce data.
- **Pydantic v2 validation** — every input and every output row is model-validated before landing in the dataset.
- **Filter knobs** — `maxJobsPerCompany` cap and `minTechsDetected` floor let you drop generic non-engineering postings.
- **Exponential backoff** — 408 / 429 / 503 retries with `Retry-After` honoured; up to 5 attempts per request.
- **Deterministic detection** — regex + vocabulary, not LLM. High precision, zero hallucinated tools.

### 💡 Use cases

- **B2B sales qualification** — enrich every Salesforce or HubSpot account with the company's live back-end stack: "they hire Django + Postgres + AWS" tells you whether to pitch your Postgres-tuning SaaS.
- **Recruiter sourcing** — pull every senior backend role from your target accounts and filter by `detected_techs` to find teams that match your candidate's stack.
- **Competitive intelligence** — track which competitors are hiring for `Kubernetes` or `Snowflake` and infer their roadmap before it's announced.
- **CRM enrichment** — replace TheirStack / BuiltWith / Wappalyzer subscriptions for the segment of buyers who publish their stack in job descriptions anyway.
- **Investment research** — map private-company tech footprint over time without a Crunchbase Pro seat.
- **Open-source stack maps** — feed a public page that ranks the most-hired-for technologies among YC-backed startups.

### ⚙️ How to use it

1. Open the Actor input form on the Apify Store page.
2. Add one or more `{companyToken, atsType}` pairs under **Companies to scrape** (the form prefills with `airtable` on Greenhouse and `Ramp` on Ashby).
3. (Optional) Set **Max jobs per company** — leave empty for no cap.
4. (Optional) Set **Minimum detected techs** — `2` or `3` drops generic non-engineering postings.
5. Configure **Apify Proxy** — leave the default residential proxy group on; we rotate sessions automatically if the ATS endpoint rate-limits.
6. Click **Start**. Rows stream into the default dataset as they are produced.

#### Quick examples

**Two engineering-heavy companies, default settings:**

```json
{
  "companies": [
    { "companyToken": "airtable", "atsType": "greenhouse" },
    { "companyToken": "Ramp", "atsType": "ashby" }
  ]
}
```

**Single Lever company, drop sales/marketing roles:**

```json
{
  "companies": [
    { "companyToken": "palantir", "atsType": "lever" }
  ],
  "minTechsDetected": 3
}
```

**Three companies across all three ATSs, capped at 50 jobs each:**

```json
{
  "companies": [
    { "companyToken": "airtable", "atsType": "greenhouse" },
    { "companyToken": "palantir", "atsType": "lever" },
    { "companyToken": "Ramp", "atsType": "ashby" }
  ],
  "maxJobsPerCompany": 50,
  "minTechsDetected": 2
}
```

### 📥 Input

| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| `companies` | array | Yes | — | List of `{companyToken, atsType}` objects. `atsType` must be `greenhouse`, `lever`, or `ashby`. |
| `maxJobsPerCompany` | integer | No | unlimited | Hard cap on jobs fetched per company. |
| `minTechsDetected` | integer | No | `0` | Skip rows where `detected_techs` has fewer items than this value. |
| `proxyConfiguration` | object | No | Apify Proxy | Proxy settings. Residential group recommended for rate-limited targets. |

**How to find a company's ATS token:**

- **Greenhouse**: the path segment in `boards.greenhouse.io/{token}` or `{token}.applytojob.com`. Examples: `airtable`, `figma`, `stripe`.
- **Lever**: the path segment in `jobs.lever.co/{token}`. Examples: `palantir`, `netflix`, `eventbrite`.
- **Ashby**: the path segment in `jobs.ashbyhq.com/{Token}`. Case-sensitive — `Ramp`, `PostHog`, `Linear` work; lowercase variants return zero jobs.

### 📤 Output

One row per active job posting. Rows are pushed to the default Apify dataset as they are produced — long runs surface results immediately.

| Field | Type | Description |
|---|---|---|
| `ats` | string | `greenhouse`, `lever`, or `ashby` |
| `company_token` | string | Board slug passed to the ATS |
| `job_id` | string | ATS-canonical job identifier |
| `title` | string | Job posting title |
| `location` | string | null | Location string (Remote, NYC, Berlin…) |
| `department` | string | null | Department or team |
| `url` | string | Public job-post URL |
| `description_text` | string | Plain-text description (HTML stripped) |
| `detected_techs` | string\[] | Sorted, deduplicated canonical tech names |
| `posted_at` | string | null | ISO 8601 UTC publication timestamp |
| `scraped_at` | string | ISO 8601 UTC row-creation timestamp |

**Sample output row:**

```json
{
  "ats": "greenhouse",
  "company_token": "airtable",
  "job_id": "4812345",
  "title": "Senior Backend Engineer",
  "location": "Remote",
  "department": "Engineering",
  "url": "https://boards.greenhouse.io/airtable/jobs/4812345",
  "description_text": "We use Python, Django, PostgreSQL, and AWS...",
  "detected_techs": ["AWS", "Django", "Postgres", "Python"],
  "posted_at": "2026-05-01T09:00:00Z",
  "scraped_at": "2026-06-01T12:00:00Z"
}
```

### 🔌 Integrations

The dataset is plain JSON/CSV, so it drops into the usual destinations with zero glue code:

- **Google Sheets / Airtable** — export the dataset and import, or push rows via the Apify integration.
- **Make / Zapier / n8n** — trigger on run finish, then fan rows into your CRM or warehouse.
- **CRM enrichment (Salesforce / HubSpot)** — match on `company_token` and write `detected_techs` to a custom field for tech-stack-based lead scoring.
- **Warehouses (Postgres / BigQuery / Snowflake)** — one flat row per job; `detected_techs` stores cleanly as a JSON array or unnested table.
- **API / MCP** — pull results programmatically via the Apify API, or wire the Actor into an AI agent over MCP.

### 🍳 Sample queries & recipes

- **"Find companies hiring for Snowflake"** — run a batch of target accounts, then filter rows where `detected_techs` contains `Snowflake`. Instant account list for a data-platform pitch.
- **Build a technographic lead list** — feed your TAM's ATS tokens, set `minTechsDetected: 3`, and aggregate `detected_techs` per `company_token` into one stack profile per account.
- **Weekly competitor watch** — schedule a run over competitor tokens and diff `detected_techs` week over week to catch new infra (a `Kubernetes` or `Kafka` appearing signals a roadmap shift).
- **Recruiter shortlist by stack** — pull senior backend roles, filter to companies whose stack matches your candidate's, and pitch the warm fit.

### 💰 Pricing

| Event | Price | When it fires |
|---|---|---|
| `actor-start` | $0.20 | Once per run (covers warm-up) |
| `result-row` | $0.005 | Per job row emitted |

A typical 50-job run costs `$0.05 + 50 × $0.005 = $0.30` on pay-per-result pricing — no monthly subscription, and the back-end stack is read straight from each job description.

**You pay only for rows that land.** If every token is misspelled and no jobs are returned, you pay only the $0.05 start fee.

### 🚧 Limitations

- **Tech detection is regex-based, not LLM-based** — it is deterministic and high-precision, but will miss tools not in the curated ~110-name vocabulary. Submit a feedback request to add more terms.
- **~85% recall** — job descriptions that omit or abbreviate tool names will not surface every dependency in a company's stack. This is an inherent limit of job-description parsing, not an Actor bug.
- **Ashby tokens are case-sensitive** — `Ramp` works; `ramp` returns zero jobs. Double-check the exact token from the company's Ashby board URL.
- **Greenhouse double-encoding** — Greenhouse wraps its `content` field in double HTML-encoding (`&lt;div&gt;` for `<div>`). The parser unescapes twice before stripping tags; unusual encoding in edge-case boards may still slip through.
- **Lever `descriptionPlain` gaps** — Lever sometimes omits the "Requirements" bullet list from `descriptionPlain`. The parser concatenates every `lists[].content` chunk to recover skills listed there; however, lists in non-standard formats may be missed.
- **Default dataset retention** — Apify FREE-tier default storage retains datasets for 7 days. Use `Actor.open_dataset(name="…")` or export immediately if you need to outlive that window.
- **No personal-data extraction** — this Actor reads public job posts and company-aggregated tech signals only. It does not extract candidate data or applicant details from any ATS.

### ❓ FAQ

**Q: What makes this different from BuiltWith or Wappalyzer?**
A: BuiltWith and Wappalyzer sniff front-end signals — JavaScript libraries, tracking pixels, and Cloudflare headers. They are excellent for marketing-stack detection (HubSpot, Marketo, Segment) but blind to back-end infrastructure (Postgres, Kafka, Kubernetes, Snowflake). This ATS tech stack detector reads job descriptions, which is where engineering teams declare their actual data platform and server-side stack. They complement each other; they don't overlap.

**Q: Is scraping these ATS job boards allowed?**
A: Greenhouse, Lever, and Ashby all publish their job-board APIs as official public endpoints — the same ones embedded in company career pages. We read public job post text and return company-level tech signals. No personal data, no applicant details, no authenticated endpoints.

**Q: How do I find the token for a company on Greenhouse / Lever / Ashby?**
A: Visit the company's public jobs page. The token is the path segment: `jobs.lever.co/{token}`, `boards.greenhouse.io/{token}`, or `jobs.ashbyhq.com/{Token}`. Ashby tokens are case-sensitive — copy them exactly as they appear in the URL.

**Q: Can I scrape hundreds of companies in one run?**
A: Yes. Add as many `{companyToken, atsType}` pairs as you need. One bad token (404 or empty board) does not abort the run — the others still produce data. Use `maxJobsPerCompany` to cap the volume and keep costs predictable.

**Q: What happens when a company rate-limits my requests?**
A: We handle it. The Actor retries with exponential backoff, rotates proxy sessions on 429s and 503s, and honours `Retry-After` headers. If a company hits a hard block mid-run, you'll see a `set_status_message` with a partial count — we never silently return an empty dataset.

**Q: Why does the Actor fail loud instead of returning an empty dataset?**
A: A silent empty result is a lie. If every token is wrong, the run exits non-zero with a clear error message — so your pipeline knows to investigate rather than assuming there were just no open roles.

### 🗣 Your feedback

If a tech name you care about is missing from the vocabulary, open a feedback ticket on the Actor's Apify Store page — we add new terms in batches.

Found a bug or have a feature request? Use the Store feedback form or contact **DevilScrapes** at <https://apify.com/DevilScrapes>.

***

**Related Actors:**

- **LLM Pricing Monitor** — track live API pricing across OpenAI, Anthropic, Google, Mistral, Groq, Together AI, and DeepSeek in one normalised schema.
- **GitHub Org Scraper** — pair tech-stack signals from job descriptions with actual code-repo activity per org.
- **Y Combinator Companies Scraper** — combine YC funding signal with ATS-derived tech stack for a complete startup profile.

# Actor input Schema

## `companies` (type: `array`):

List of <code>{companyToken, atsType}</code> pairs. <code>atsType</code> is one of <code>greenhouse</code>, <code>lever</code>, <code>ashby</code>. <code>companyToken</code> is the board slug in the ATS URL (e.g. <code>airtable</code> for Greenhouse, <code>palantir</code> for Lever, <code>Ramp</code> for Ashby — Ashby is case-sensitive).

## `maxJobsPerCompany` (type: `integer`):

Hard cap on rows emitted per company. Leave empty for no cap.

## `minTechsDetected` (type: `integer`):

Drop jobs whose <code>detected\_techs</code> list has fewer than this many entries. <code>0</code> keeps every job.

## `useProxy` (type: `boolean`):

Route requests via Apify Proxy (<code>BUYPROXIES94952</code>). All three ATS APIs are public — only enable if you observe rate-limiting.

## Actor input object example

```json
{
  "companies": [
    {
      "companyToken": "airtable",
      "atsType": "greenhouse"
    },
    {
      "companyToken": "Ramp",
      "atsType": "ashby"
    }
  ],
  "minTechsDetected": 0,
  "useProxy": false
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

All dataset items as JSON.

## `datasetItemsCsv` (type: `string`):

Same data exported to CSV.

## `datasetView` (type: `string`):

Open the run dataset in the Console.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "companies": [
        {
            "companyToken": "airtable",
            "atsType": "greenhouse"
        },
        {
            "companyToken": "Ramp",
            "atsType": "ashby"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/builtwith-alternative-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "companies": [
        {
            "companyToken": "airtable",
            "atsType": "greenhouse",
        },
        {
            "companyToken": "Ramp",
            "atsType": "ashby",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/builtwith-alternative-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "companies": [
    {
      "companyToken": "airtable",
      "atsType": "greenhouse"
    },
    {
      "companyToken": "Ramp",
      "atsType": "ashby"
    }
  ]
}' |
apify call devilscrapes/builtwith-alternative-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devilscrapes/builtwith-alternative-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/10kpfiQ1b5t3Hiu7e/builds/PFqM3JqmmOZK0PUru/openapi.json
