# Company Tech Stack API (`spool/company-tech-stack-api`) Actor

Detect any company's tech stack and hiring footprint from its public ATS job board. 80+ tools across cloud, data, AI and business systems.

- **URL**: https://apify.com/spool/company-tech-stack-api.md
- **Developed by:** [Spool](https://apify.com/spool) (community)
- **Categories:** Lead generation, Developer tools
- **Stats:** 2 total users, 1 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

## Company Tech Stack API — Detected from Job Postings

**Find companies by the technology they run. Not a lookup — a search.**

Ask for *"YC fintech companies that are hiring and use Salesforce, NetSuite or
Snowflake"* and get the list back, with each company's full stack, hiring
breakdown and expansion signals.

```
Scanned 60 · resolved 29 · matched 5

ramp      142 jobs   Salesforce · NetSuite · Snowflake · Looker · dbt   62 of 142 roles are Sales
atob       10 jobs   Salesforce · Stripe · Shopify · Ruby · PostgreSQL
alpaca     59 jobs   Spark · GCP · Kubernetes · gRPC · PostgreSQL
```

Companies announce their stack in public every day and nobody reads it. A firm
hiring three Kubernetes engineers and a Snowflake analyst has told you what it
runs and what it's spending on, months before any vendor database catches up.

***

### Why nothing else does this

Every other technographic tool is an **enrichment** tool: you bring a list, it
annotates it. That is not a choice they made — it is a wall. No applicant
tracking system publishes a company directory or a cross-board search. Every
such endpoint returns 404 or 401:

```
Greenhouse board list   → 404      Lever directory     → 404
Ashby directory         → 401      Cross-board search  → 404
```

So a tech stack can only be looked up for a company you can already name.

**Unless you bring your own directory.** Two are public and complete enough:

| Directory | Size | What it adds |
|---|---|---|
| **Y Combinator** | 6,202 companies | 59 industries, 335 tags, 50 batches — each its own endpoint |
| **Hacker News** *Who is hiring?* | ~240 companies/month | **~95% are not YC** — the half of the market YC cannot reach |

YC companies run overwhelmingly on Greenhouse and Ashby, exactly the boards this
reads. HN posters often link their ATS board directly. Joining these to the board
APIs turns lookup into search.

That query — *find companies by the tools they're hiring for* — is not answered
by BuiltWith, Datanyze or any ATS scraper, because it is built from **hiring
intent** rather than a website scan or a purchased list.

***

### Why hiring beats a website scan

Most "tech stack" tools read a website's HTML and find the marketing pixels —
Google Analytics, HubSpot, Intercom. Useful, shallow, identical across thousands
of companies.

Job postings reveal the **internal** stack: the data warehouse, the cloud, the
ERP, the orchestration layer, the ML framework. None of that appears in a
`<script>` tag. And a job posting is a *budgeted* signal — somebody approved
headcount for it.

***

### Quick start

**Search mode** — pick a directory, filter it, and name the technologies you care
about:

```json
{
  "discoverFrom": "ycombinator",
  "ycIndustries": ["fintech"],
  "requireTech": ["Salesforce", "NetSuite", "Snowflake"],
  "maxCompanies": 100
}
```

Industries and tags **combine with AND**, so `b2b` + tag `ai` gives B2B companies
also tagged AI — 611 of them, 212 hiring. Only the endpoints you ask for are
downloaded, so a narrow search costs a few hundred KB rather than the 10 MB dump.

**Lookup mode** — name the companies yourself. Names, domains and board URLs all
work, mixed freely:

| You type | What happens |
|---|---|
| `Stripe` | Name matched against live boards |
| `ramp.com` | Domain |
| `https://boards.greenhouse.io/oklo` | Board URL — always resolves |

Both can run together.

***

### What you get back

```json
{
  "company": "ramp",
  "atsProvider": "ashby",
  "openJobs": 142,

  "ycBatch": "Winter 2019",
  "ycIndustry": "Fintech",
  "ycTeamSize": 1000,

  "techStack": ["Python", "Salesforce", "NetSuite", "LLM", "AWS", "Anthropic"],
  "techByCategory": {
    "business": ["Salesforce", "NetSuite", "Workday", "SAP", "HubSpot"],
    "data":     ["Snowflake", "Looker", "dbt", "BigQuery", "Airflow", "Kafka"],
    "ai":       ["LLM", "Anthropic", "RAG"]
  },
  "techDetail": [
    { "name": "Salesforce", "category": "business", "jobCount": 23 },
    { "name": "NetSuite",   "category": "business", "jobCount": 22 }
  ],

  "signals": {
    "hiringFocus": "Sales",
    "hiringScale": "scale-up",
    "salesLed": true,
    "aiAdoption": "committed",
    "dataMaturity": "advanced",
    "primaryCloud": "AWS",
    "seniorityProfile": "balanced",
    "remotePosture": "hybrid"
  },

  "departments": [{ "name": "Sales", "count": 62 }, { "name": "Engineering", "count": 30 }]
}
```

**`techDetail` carries the evidence**, not just the claim. A tool named in 1 job
of 600 is a mention; named in 40, it's a commitment.

**`signals` carries the reading of it.** `salesLed: true` means the sales org is
outgrowing engineering — the classic moment a company is expanding go-to-market
and buying tools. Every threshold is published in `signals.thresholds`, so you
can disagree with an interpretation without losing the numbers underneath.

Six dataset views ship with it: **Overview**, **Discovery**, **Signals**,
**Tech stack**, **Hiring footprint** and **Failures**.

***

### Recipes — copy, paste, run

**Companies outside YC entirely — from Hacker News**

```json
{
  "discoverFrom": "hackernews",
  "hnMonths": 6,
  "requireTech": ["Kubernetes", "Terraform"],
  "maxCompanies": 400
}
```

**Every YC company running a modern data stack**

```json
{
  "discoverFrom": "ycombinator",
  "requireTech": ["Snowflake", "dbt", "Looker"],
  "requireTechMode": "all",
  "maxCompanies": 400
}
```

**Companies expanding their sales org — the moment they buy**

```json
{
  "discoverFrom": "ycombinator",
  "ycIndustries": ["B2B"],
  "requireSignals": ["salesLed:true", "hiringScale:scale-up"],
  "maxCompanies": 300
}
```

**Who actually committed to AI, rather than talking about it**

```json
{
  "discoverFrom": "ycombinator",
  "requireSignals": ["aiAdoption:committed"],
  "ycStages": ["Growth"],
  "maxCompanies": 300
}
```

**Enrich an account list you already have**

```json
{
  "companies": ["ramp.com", "brex.com", "mercury.com"],
  "techCategories": ["business"],
  "minJobMentions": 3
}
```

***

### Every option

| Option | Default | What it does |
|---|---|---|
| `discoverFrom` | `none` | `ycombinator`, `hackernews`, or `both` — search a directory instead of naming companies |
| `ycIndustries` | *(all)* | 59 available: `b2b` (3,158), `consumer` (884), `healthcare` (697), `fintech` (658), `healthcare-it` (141), `sales` (135)… |
| `ycTags` | *(all)* | 335 available: `saas` (1,097), `artificial-intelligence` (998), `developer-tools` (548), `generative-ai` (255), `payments` (146)… Combines with industries using AND |
| `hnMonths` | `3` | How many monthly *Who is hiring?* threads to read. Repeats across months are merged |
| `ycStages` | *(all)* | Early (5,116) or Growth (1,086) |
| `ycRegions` | *(all)* | US, Europe, Remote, Asia, Latin America… |
| `ycBatches` | *(all)* | `winter-2024`, `summer-2023` — 50 batches |
| `ycOnlyHiring` | `true` | Only companies with open roles. 1,477 of 6,202 |
| `ycOnlyTopCompanies` | `false` | YC's own breakout-company flag |
| `ycMinTeamSize` / `ycMaxTeamSize` | — | Headcount bounds |
| `maxCompanies` | `100` | Cap on discovered companies scanned per run |
| `companies` | — | Names, domains or board URLs, mixed freely |
| **`requireTech`** | *(none)* | **Only keep companies using these.** The filter that makes it a lead list |
| `requireTechMode` | `any` | `all` finds specific combinations, like Snowflake **and** dbt **and** Looker |
| `requireSignals` | *(none)* | `salesLed:true`, `aiAdoption:committed`, `dataMaturity:advanced`… |
| `onlyProvider` | `any` | Greenhouse, Ashby, Lever or SmartRecruiters |
| `minJobMentions` | `1` | Evidence threshold per technology |
| `techCategories` | *(all)* | `business`, `data`, `ai`, `security`, `cloud`, `devops`, `database`, `language`, `frontend`, `backend`, `observability` |
| `detailLimit` | `60` | SmartRecruiters only — descriptions to fetch |
| `includeJobs` | `false` | Add every open role with its own tech and seniority |
| `maxConcurrency` | `5` | Companies looked up in parallel |

***

### What it covers, honestly

**Greenhouse, Ashby, Lever and SmartRecruiters.** The first three cover startup
and scale-up hiring; SmartRecruiters covers the enterprise end (Bosch alone
lists 4,811 roles). **Workday and iCIMS are not covered.**

**Name resolution, measured on 110 real companies:**

| | Resolved from the name alone |
|---|---|
| Companies that use a supported ATS | **89%** |
| An arbitrary list of companies | **58%** |

The gap is coverage, not guessing. Roughly half of companies run on an ATS this
does not read, or a custom careers page. In a discovery run expect **about half
the scanned companies to resolve** — that is normal and the run summary states
it plainly. Misses return `board_not_found` with the candidates that were tried,
so a miss is always visible rather than silent.

**Depth differs by provider.** Greenhouse, Ashby and Lever return every
description in one request, so detection reads the whole board. SmartRecruiters
returns none — each description is a separate call — so descriptions are read
for the first `detailLimit` roles and the rest still contribute titles,
departments and locations. Those rows carry `descriptionsRead` and
`totalFoundOnBoard`, so the sampling is visible rather than implied.

**190 technologies** across 11 categories. Each has an explicit match pattern —
ambiguous names like Go, R and Ray require surrounding context, so "going to the
algorithm" never registers as Go.

**No proxy, no key, no blocking.** These are public JSON endpoints published by
the ATS vendors themselves. Greenhouse's robots.txt permits the board API.

***

### What it costs

Companies that **match your filters and are returned** are the billable unit.
Companies scanned and filtered out are charged at a fraction of that, because
looking them up still did work but gave you nothing to act on. A company that
isn't on a supported ATS costs nothing at all.

That means a wide search with a narrow filter stays cheap: scanning 300
companies to find the 12 running NetSuite bills 12 at the full rate.

***

### When a lookup fails

| `error` | Meaning |
|---|---|
| `board_not_found` | Not on a supported ATS, or the name didn't match. `triedLookups` shows what was attempted — paste the board URL instead |
| `no_open_jobs` | Board exists but is currently empty |
| `network_error` | Timeout or connection failure |
| `invalid_input` | Couldn't read a name, domain or URL from that value |

A full breakdown is saved to the key-value store as `RUN_SUMMARY`.

***

### FAQ

**Can I search for companies, or must I know them already?**
Search. Two directories are built in: Y Combinator (filterable by 59 industries,
335 tags, 50 batches, stage, region, team size and hiring status) and the monthly
Hacker News *Who is hiring?* thread, which is about 95% non-YC companies. Both
can run at once, duplicates merged. You can also just name companies yourself.

What no tool can do is search the *whole* market — no ATS publishes a directory,
so coverage is bounded by whichever directories you point it at.

**How reliable is the Hacker News parsing?**
Posts follow a pipe convention — `Company | Role | City | REMOTE | url` — and only
top-level comments are read, since replies are candidates asking questions. Only
the first field and the first URL are trusted; everything after varies too much
between posters to be worth parsing. On a recent thread that gave 228 companies
from 240 posts, with replies correctly rejected.

**How is this different from BuiltWith or Wappalyzer?**
Those scan a website's HTML and see front-end and marketing tools. This reads
hiring intent and sees the internal stack — warehouse, cloud, ERP, ML framework
— which never appears in page source.

**Can I find every company using Salesforce or NetSuite?**
Every company *in the directory that is hiring and on a supported ATS*, yes —
set `requireTech`. Not every company on earth; no honest tool can claim that.

**How many technologies can it detect?**
190, across languages, frontend, backend, cloud, DevOps, databases, data, AI/ML,
business systems, security and observability.

**Does it work for companies posting in other languages?**
Yes. Departments are read from the company's own ATS labels first and only fall
back to title matching, so a board posting in German, Spanish and Portuguese
still classifies correctly. On Bosch that took unclassified roles from 51% to 3%.

**Is this legal?**
It reads public job postings that companies publish deliberately to be found,
plus a public startup directory. Everything returned is **company-level** —
technologies, counts, departments, locations. No candidate data, no personal
contacts, no names. That is a deliberate design choice, not an oversight.

***

### Support

Open an issue on the **Issues** tab and you'll get a reply, usually the same day.
Requests for extra technologies in the taxonomy are especially welcome — tell me
what you need detected and I'll add it.

# Actor input Schema

## `discoverFrom` (type: `string`):

**This is what makes it a search rather than a lookup.**

No applicant tracking system publishes a directory, so normally you can only look up companies you can already name. Two public directories get around that:

• **Y Combinator** — 6,202 companies across 59 industries, 335 tags and 50 batches. Heavily Greenhouse and Ashby.
• **Hacker News** — the monthly *Ask HN: Who is hiring?* thread. About 240 companies each month, and **~95% of them are not YC**, which is the half of the market YC cannot reach.

Pick a source, filter it, and get back only the companies whose tech stack matches.

## `ycIndustries` (type: `array`):

Leave empty for all 59. Type one per line — the big ones, with company counts:

`b2b` (3,158) · `consumer` (884) · `healthcare` (697) · `fintech` (658) · `engineering-product-and-design` (620) · `industrials` (458) · `infrastructure` (321) · `productivity` (230) · `marketing` (169) · `real-estate-and-construction` (162) · `operations` (152) · `manufacturing-and-robotics` (145) · `healthcare-it` (141) · `finance-and-accounting` (138) · `supply-chain-and-logistics` (138) · `sales` (135)

Several industries are combined with OR. An unknown name is reported in the log rather than silently ignored.

## `ycTags` (type: `array`):

Far finer than industries — 335 available. Type one per line:

`saas` (1,097) · `artificial-intelligence` (998) · `ai` (889) · `fintech` (705) · `developer-tools` (548) · `marketplace` (307) · `generative-ai` (255) · `machine-learning` (236) · `analytics` (186) · `health-tech` (173) · `open-source` (170) · `productivity` (168) · `education` (164) · `ai-assistant` (161) · `hardware` (160) · `biotech` (148) · `payments` (146)

**Tags and industries combine with AND.** Industry `b2b` plus tag `ai` gives B2B companies that are also tagged AI — 611 of them, 212 hiring.

## `ycBatches` (type: `array`):

One per line, as `winter-2024`, `summer-2023`, `winter-2022`. 50 batches are available. Leave empty for all.

## `ycStages` (type: `array`):

Early (5,116 companies) or Growth (1,086). Leave empty for both.

## `ycRegions` (type: `array`):

Leave empty for everywhere. A company can sit in several — `Remote` and `United States of America` often both apply.

## `ycOnlyHiring` (type: `boolean`):

Strongly recommended, and on by default. A company with no open roles has no job board to read, so scanning it spends a lookup to learn nothing. 1,477 of the 6,200 are hiring.

## `ycOnlyTopCompanies` (type: `boolean`):

YC's own `top_company` flag — the breakout names. Narrow, but every one is a serious buyer.

## `ycMinTeamSize` (type: `integer`):

Filter out companies below this headcount. Useful when a two-person startup is not worth a sales call.

## `ycMaxTeamSize` (type: `integer`):

Filter out companies above this headcount.

## `hnMonths` (type: `integer`):

How far back to read *Ask HN: Who is hiring?*. Each thread carries roughly 240 companies, and repeats across months are merged, so 3 months yields several hundred distinct companies — most of them not in any startup directory.

## `maxCompanies` (type: `integer`):

Cap on discovered companies per run, applied **after** duplicates are merged, so it counts companies actually scanned. Each takes about a second. Start at 100 to see the shape of the results, then raise it.

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

Company names, domains, or ATS board URLs — mix them freely. Works on its own, or alongside discovery.

• Name: `Stripe`, `Two Chairs`
• Domain: `ramp.com`
• Board URL: `https://boards.greenhouse.io/oklo` (always resolves)

About 89% of companies that use a supported ATS resolve from the name alone.

## `requireTech` (type: `array`):

**The filter that turns a scan into a lead list.** Type technology names exactly as they appear in the output — `Salesforce`, `NetSuite`, `Snowflake`, `Kubernetes`, `Anthropic`, `SOC 2`.

Leave empty to keep every company. 190 technologies are detected in total.

## `requireTechMode` (type: `string`):

`any` casts a wide net; `all` finds companies running a specific combination — for example Snowflake **and** dbt **and** Looker, which is a modern-data-stack shop.

## `requireSignals` (type: `array`):

Written as `key:value`, one per line. Examples:

• `salesLed:true` — sales org outgrowing engineering, the classic expansion moment
• `aiAdoption:committed`
• `dataMaturity:advanced`
• `hiringScale:scale-up`
• `remotePosture:remote-first`

Every signal in the output can be filtered this way.

## `onlyProvider` (type: `string`):

Restrict the lookup to one ATS. Leave on **Any** to try Greenhouse, then Ashby, then Lever, then SmartRecruiters — which is what you want unless you already know.

## `minJobMentions` (type: `integer`):

How much evidence before a technology counts. `1` keeps every mention; raise it to keep only tools the company is clearly committed to. A tool named in 1 job of 600 is a mention — named in 40, it's a commitment.

## `techCategories` (type: `array`):

Leave empty for everything. Pick categories to narrow the output — `business` alone gives you Salesforce, HubSpot, NetSuite, SAP and Workday, which is usually what a sales team wants.

## `detailLimit` (type: `integer`):

Only affects SmartRecruiters, the one ATS that needs a separate request per job to get its description. Bosch alone lists 4,811 roles, so descriptions are read for this many and the rest still contribute titles, departments and locations. The other three return descriptions in one request and ignore this.

## `includeJobs` (type: `boolean`):

Adds every open role to the row, each with its own detected tech, department and seniority. Off by default so the output stays one clean line per company.

## `includeJobDescriptions` (type: `boolean`):

Only applies when the jobs above are included. Descriptions run several thousand characters each, so this makes the dataset much larger.

## `maxConcurrency` (type: `integer`):

Companies looked up in parallel. These are plain public JSON endpoints with no proxy involved, so the only reason to keep this modest is politeness.

## Actor input object example

```json
{
  "discoverFrom": "none",
  "ycIndustries": [],
  "ycTags": [],
  "ycBatches": [],
  "ycStages": [],
  "ycRegions": [],
  "ycOnlyHiring": true,
  "ycOnlyTopCompanies": false,
  "hnMonths": 3,
  "maxCompanies": 100,
  "companies": [
    "Stripe",
    "ramp.com",
    "assemblyai"
  ],
  "requireTech": [],
  "requireTechMode": "any",
  "requireSignals": [],
  "onlyProvider": "any",
  "minJobMentions": 1,
  "techCategories": [],
  "detailLimit": 60,
  "includeJobs": false,
  "includeJobDescriptions": false,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `companies` (type: `string`):

Every company record, with the full detected tech stack and hiring footprint.

## `techStackOnly` (type: `string`):

Just company, ATS and the detected technologies grouped by category — the lead-list view.

## `hiringFootprint` (type: `string`):

Open roles by department, seniority and location, with the remote share.

## `runSummary` (type: `string`):

How many companies resolved, how many failed, and a breakdown of failures by type.

# 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": [
        "Stripe",
        "ramp.com",
        "assemblyai"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("spool/company-tech-stack-api").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": [
        "Stripe",
        "ramp.com",
        "assemblyai",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("spool/company-tech-stack-api").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": [
    "Stripe",
    "ramp.com",
    "assemblyai"
  ]
}' |
apify call spool/company-tech-stack-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,spool/company-tech-stack-api"
        }
    }
}

```

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/2NneuywewkG9i17dw/builds/XAek8yBqp9hH2pyuV/openapi.json
