# Tankha — Salary Data from Greenhouse, Lever, Ashby & More (`sayuj/tankha`) Actor

Extract salary ranges from public Greenhouse, Lever, Ashby and SmartRecruiters job boards. Structured pay data first, strict text parsing second, India LPA/lakh/crore support, and change tracking that flags new jobs, pay changes and removed jobs.

- **URL**: https://apify.com/sayuj/tankha.md
- **Developed by:** [Sayuj Pillai](https://apify.com/sayuj) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 jobs

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

![Tankha — Salary Data from Public Job Boards](https://api.apify.com/v2/key-value-stores/Rb5YdEZ3JbsTQUcig/records/tankha-banner.png)

### What is Tankha?

**Tankha** ("salary" in Hindi) pulls **salary data from public Greenhouse, Lever, Ashby and SmartRecruiters job boards**. Give it a list of company boards. You get one row per job with the pay range, currency, pay period and the exact text the number came from. Run it again later and it tells you which jobs are new, which changed pay and which were removed.

### Why salary data?

Pay-transparency laws in the US, Canada, the EU and the UK mean thousands of companies now publish pay ranges. Recruiters, compensation analysts, job seekers and market researchers use this data to benchmark offers, follow hiring trends and spot pay changes. Tankha reads the companies' own public job APIs, so the data is first-hand.

### Supported boards

| ATS | Write it as | Or paste the URL |
|---|---|---|
| Greenhouse | `greenhouse:airbnb` | `boards.greenhouse.io/airbnb`, `job-boards.greenhouse.io/figma` |
| Lever | `lever:palantir` | `jobs.lever.co/spotify` |
| Ashby | `ashby:openai` | `jobs.ashbyhq.com/ramp` |
| SmartRecruiters | `smartrecruiters:BoschGroup` | `jobs.smartrecruiters.com/BoschGroup` |

#### How do I find a board name?

Open the company's careers page and click any job. If the address is on one of the domains above, the board name is the first part of the path, for example `jobs.lever.co/`**`spotify`**`/…`. Tankha reports an unknown or misspelled board in the summary and still runs the other boards.

### How it extracts salaries

1. **Structured fields first:** Greenhouse `pay_input_ranges`, Ashby compensation tiers (including "Offers Equity") and Lever `salaryRange`.
2. **Text parser second:** a deterministic parser reads the job description ("Salary: $199,222 - $235,200/yr", "€94,000-€105,000 per year", "$32 - $35/hour").
3. **Never guess:** a range is reported only when there is a currency, a pay context, a clear pay period and plausible numbers. Tankha rejects revenue figures ("$250 - $1B revenue"), years, percentages, 401(k) and bonus amounts, funding and headcounts. Open ranges such as "up to $120k" keep the missing bound as `null`. A job with several regional ranges lists all of them in `salaryRanges`, and the first one is the primary.

#### India: LPA, lakh and crore

`12-18 LPA`, `₹12,00,000 – ₹18,00,000`, `INR 15 lakh`, `1.2 crore` and `12L-18L` all become yearly INR numbers (for example 1200000-1800000).

### Input example

```json
{
  "boards": ["greenhouse:airbnb", "ashby:ramp", "lever:palantir"],
  "maxJobsPerBoard": 200,
  "onlyWithSalary": false,
  "trackChanges": true
}
```

### Output example (real row, ashby:ramp)

```json
{
  "source": "ashby", "board": "ramp", "company": "ramp",
  "title": "Security Engineer, Cloud",
  "url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "location": "New York, NY (HQ)", "department": "Engineering", "remote": true,
  "postedAt": "2026-04-07T17:12:35.753+00:00",
  "salaryDisclosed": true, "salaryStatus": "disclosed",
  "salary": { "min": 211400, "max": 290600, "currency": "USD", "period": "year",
    "source": "structured", "evidence": "$211.4K – $290.6K • Offers Equity", "equity": true },
  "changeType": "new", "previousSalary": null
}
```

The **SUMMARY** record in the key-value store has, for each board: job count, jobs with salary, disclosure %, the median, minimum and maximum annual salary per currency, change counts and any errors. In our test runs, Ramp disclosed pay for 96% of its jobs, Airbnb 84%, OpenAI 79% and Palantir 77%.

### Change tracking

With `trackChanges` on (the default), Tankha saves a snapshot of each board in a named key-value store, `tankha-snapshots`, in your account. On the next run each job is marked:

- `new`: not seen last time
- `salary_changed`: the pay range differs; `previousSalary` holds the old range
- `unchanged`
- `removed`: the job is gone. These rows are free, and they are only reported when Tankha fetched the board's complete job list, so a failed or partial fetch never produces false removals.

Schedule the Actor daily or weekly to monitor competitors' hiring and pay.

### Pricing

You pay **per job analysed** (the `job-analyzed` event). That is one event per job row delivered; `removed` rows are free. See the **Pricing** tab for the current price. To cap cost, use `maxJobsPerBoard`, or `onlyWithSalary` to receive and pay for only the jobs with pay data.

### Limitations

- Tankha reads only the ATS's public API. Some companies show pay only on their own careers site. Stripe, for example, renders many US ranges on stripe.com, and those are absent from its Greenhouse feed. Such jobs come back as `not_disclosed`.
- A bare `$` is read as USD unless the text names another dollar currency (CAD, AUD, SGD and so on).
- The text parser understands English plus common German pay phrases. Pay written only in other languages may be missed, never invented.
- On SmartRecruiters, each job needs one extra request for its description, so `maxJobsPerBoard` also limits requests there.

### FAQ

**Is this legal?** Tankha reads the same public job APIs that power the companies' career pages. It does not log in or collect personal data.

**Why is `equity` sometimes `null`?** `equity` is `true` when the posting mentions equity or stock as part of pay. It is never set to `false` on a guess.

**How are annual figures computed?** Hourly × 2080, daily × 260, monthly × 12. They appear only in SUMMARY. Rows keep the original period.

**Can I track hundreds of boards?** Yes. Add up to 200 boards per run. Each board has its own snapshot.

# Changelog

This Actor's version history is a separate document: https://apify.com/sayuj/tankha/changelog.md

# Actor input Schema

## `boards` (type: `array`):

Each entry is either <code>source:board</code> (for example <code>greenhouse:airbnb</code>, <code>lever:palantir</code>, <code>ashby:openai</code>, <code>smartrecruiters:BoschGroup</code>) or the board's URL (<code>boards.greenhouse.io/airbnb</code>, <code>job-boards.greenhouse.io/figma</code>, <code>jobs.lever.co/spotify</code>, <code>jobs.ashbyhq.com/ramp</code>, <code>jobs.smartrecruiters.com/BoschGroup</code>). The board name is the part of the careers URL right after the domain. A board that cannot be found is reported in SUMMARY and the other boards still run.

## `maxJobsPerBoard` (type: `integer`):

Analyse at most this many jobs from each board (in the order the board lists them). Each analysed job that you receive as a row is one billable event, so this also caps cost.

## `onlyWithSalary` (type: `boolean`):

Output only jobs where a salary was found. Jobs without a disclosed salary are still counted in SUMMARY and still tracked for changes, but you receive (and pay for) only the jobs with pay data.

## `trackChanges` (type: `boolean`):

Saves a snapshot of every board in the named key-value store <code>tankha-snapshots</code> in your account. The next run marks each job <code>new</code>, <code>salary_changed</code> (with <code>previousSalary</code>) or <code>unchanged</code>, and adds free rows with <code>changeType: removed</code> for jobs that disappeared. Removals are only reported when the board's complete job list was fetched.

## Actor input object example

```json
{
  "boards": [
    "greenhouse:airbnb",
    "ashby:ramp",
    "lever:palantir",
    "https://jobs.smartrecruiters.com/BoschGroup"
  ],
  "maxJobsPerBoard": 20,
  "onlyWithSalary": false,
  "trackChanges": true
}
```

# Actor output Schema

## `jobs` (type: `string`):

One row per job: title, location, salary range, currency, period, evidence text and change type.

## `summary` (type: `string`):

Per-board counts: jobs, jobs with salary, disclosure %, median/min/max annual salary per currency, change counts and errors.

# 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 = {
    "boards": [
        "greenhouse:airbnb"
    ],
    "maxJobsPerBoard": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("sayuj/tankha").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 = {
    "boards": ["greenhouse:airbnb"],
    "maxJobsPerBoard": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("sayuj/tankha").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 '{
  "boards": [
    "greenhouse:airbnb"
  ],
  "maxJobsPerBoard": 20
}' |
apify call sayuj/tankha --silent --output-dataset

```

## MCP server setup

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

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/0mes3ZPhJio6Wb5Y8/builds/eG8G5x7GHnAeS0sDL/openapi.json
