skills/tinyfish-io/tinyfish-cookbook/salary-market-scanner

salary-market-scanner

Installation
SKILL.md

Salary Market Scanner

Scrape live job boards and salary databases to find real compensation data for any role and location — not outdated surveys, but what companies are actually posting and paying right now.

Pre-flight Check (REQUIRED)

Before making any TinyFish call, always run BOTH checks:

1. CLI installed?

which tinyfish && tinyfish --version || echo "TINYFISH_CLI_NOT_INSTALLED"

If not installed, stop and tell the user:

Install the TinyFish CLI: npm install -g @tiny-fish/cli

2. Authenticated?

tinyfish auth status

If not authenticated, stop and tell the user:

You need a TinyFish API key. Get one at: https://agent.tinyfish.ai/api-keys

Then authenticate:

tinyfish auth login

Do NOT proceed until both checks pass.


Step 1 — Gather inputs

You need:

  • Job title / role — e.g. Senior Software Engineer, ML Engineer, Product Designer, DevOps Engineer
  • Location — e.g. London, Singapore, San Francisco, Remote
  • Years of experience (optional) — e.g. 3-5 years, senior, entry level
  • Specific company (optional) — if the user wants to know what a specific company pays

If location is not provided, ask before proceeding. Salary data varies dramatically by market.


Step 2 — Parallel salary scan

Fire all agents simultaneously. Sources vary by location — include the most relevant ones.

# Agent 1 — Levels.fyi (best for tech roles, especially US/global big tech)
tinyfish agent run \
  --url "https://www.levels.fyi/t/{ROLE_SLUG}/?country={COUNTRY}" \
  "You are on Levels.fyi showing compensation data for {ROLE} in {LOCATION}.
   Extract:
   - Median total compensation
   - Base salary range (p25 to p75)
   - Bonus range
   - Stock/equity range (if shown)
   - Sample size (number of data points)
   - Top companies listed and their compensation ranges
   - Any breakdown by years of experience if visible
   STRICT RULES:
   - Do NOT click any company or individual entry
   - Read only the aggregate data visible on the page
   - If no data for this location, return {found: false, reason: 'no data for location'}
   Return JSON: {found: bool, median_total, base_p25, base_p75, bonus_range, equity_range, sample_size, top_companies: [{company, base, total}], yoe_breakdown: []}" \
  --sync > /tmp/sal_levels.json &

# Agent 2 — Glassdoor salaries
tinyfish agent run \
  --url "https://www.google.com/search?q=glassdoor+{ROLE_ENCODED}+salary+{LOCATION_ENCODED}+site:glassdoor.com/Salaries" \
  "You are on Google search results. Find the most relevant Glassdoor salary page for {ROLE} in {LOCATION} and click it.
   On the Glassdoor salary page extract:
   - Median base salary
   - Salary range (low to high)
   - Number of salary reports
   - Additional pay (bonus, profit sharing) range if shown
   - Top companies paying for this role if listed
   STRICT RULES:
   - Click only the first Glassdoor salary result
   - Do NOT click any other links after landing on Glassdoor
   - Read only the aggregate salary data visible on the page
   - If the page asks you to sign in, extract whatever is visible before the gate
   Return JSON: {median_base, salary_low, salary_high, report_count, additional_pay_range, top_companies: [{company, salary}]}" \
  --sync > /tmp/sal_glassdoor.json &

# Agent 3 — LinkedIn Jobs (extract posted salary ranges from active listings)
tinyfish agent run \
  --url "https://www.linkedin.com/jobs/search/?keywords={ROLE_ENCODED}&location={LOCATION_ENCODED}&f_SB2=1&sortBy=DD" \
  "You are on LinkedIn job search results for {ROLE} in {LOCATION}, filtered to show salary information, sorted by date.
   For each job listing card visible on the page:
   - Click into the listing to open the job detail panel on the right
   - Look for the salary range in the detail panel (often shown near the top under the job title)
   - Extract: job title, company name, salary range, employment type
   - Go back to the listing and repeat for the next one
   STRICT RULES:
   - Only extract listings that show an explicit salary — skip those without
   - Maximum 10 listings then stop
   - Do NOT navigate away from the search results page
   Return JSON array: [{title, company, salary_range, employment_type}]" \
  --sync > /tmp/sal_linkedin.json &

# Agent 4 — Indeed salaries
tinyfish agent run \
  --url "https://www.indeed.com/career/{ROLE_INDEED}/salaries?from=top_sb&l={LOCATION_ENCODED}" \
  "You are on Indeed's salary page for {ROLE} in {LOCATION}.
   Extract:
   - Average base salary
   - Salary range (low to high)
   - Number of salary reports
   - Salary by experience level (if shown: entry, mid, senior)
   - Top paying companies for this role (if listed)
   STRICT RULES:
   - Do NOT click any links
   - Read only the aggregate data on this page
   Return JSON: {average_salary, salary_low, salary_high, report_count, by_experience: [{level, salary}], top_companies: [{company, salary}]}" \
  --sync > /tmp/sal_indeed.json &

wait

echo "=== LEVELS ===" && cat /tmp/sal_levels.json
echo "=== GLASSDOOR ===" && cat /tmp/sal_glassdoor.json
echo "=== LINKEDIN ===" && cat /tmp/sal_linkedin.json
echo "=== INDEED ===" && cat /tmp/sal_indeed.json

Before running, replace:

  • {ROLE} — human-readable e.g. Senior Software Engineer
  • {ROLE_ENCODED} — URL-encoded e.g. Senior%20Software%20Engineer
  • {ROLE_SLUG} — Levels.fyi slug e.g. software-engineer
  • {ROLE_INDEED} — Indeed format e.g. software-engineer
  • {LOCATION} — e.g. London, Singapore
  • {LOCATION_ENCODED} — URL-encoded e.g. London%2C%20England
  • {COUNTRY} — country code for Levels.fyi e.g. GB, SG, US

Step 3 — Synthesize the market picture

Combine data from all sources and calculate aggregate ranges.

## Salary Market Report — {ROLE} · {LOCATION}

*Live data scraped from Levels.fyi, Glassdoor, LinkedIn, and Indeed*
*{date} · Based on {N} total data points*

---

### 💰 Compensation Summary

| | Low | Median | High |
|---|---|---|---|
| **Base Salary** | {low} | {median} | {high} |
| **Total Comp** (incl. bonus/equity) | {low} | {median} | {high} |

> All figures in {CURRENCY}. "Total comp" includes base + annual bonus + annualized equity where data is available.

---

### 📊 By Experience Level

| Level | Typical Base |
|---|---|
| Entry (0-2 yrs) | {range} |
| Mid (3-5 yrs) | {range} |
| Senior (6+ yrs) | {range} |
| Staff / Principal | {range} |

*(Skip levels where no data was found)*

---

### 🏢 What Companies Are Posting

From active LinkedIn job listings with disclosed salaries:

| Company | Role | Posted Range |
|---|---|---|
| {company} | {title} | {range} |

---

### 🏆 Top Paying Companies

*(From Levels.fyi and Glassdoor)*
| Company | Median Base | Median Total |
|---|---|---|
| {company} | {base} | {total} |

---

### 📈 Market Signals

{2-3 sentences on what the data says about this market — is it competitive, is there a wide spread, are companies being transparent about pay?}

---

### 🔍 Data Sources
- Levels.fyi: {sample_size} data points / not found
- Glassdoor: {report_count} salary reports / not found  
- LinkedIn: {N} active listings with disclosed salaries
- Indeed: {report_count} salary reports / not found

Edge Cases

  • Levels.fyi has no data for this location — lean on Glassdoor and Indeed; note that Levels.fyi skews toward US big tech
  • Role title is unusual — try common variations (e.g. "ML Engineer" → "Machine Learning Engineer", "AI Engineer")
  • Location is a small city — broaden to the country or nearest major city and note the change
  • Remote role — scrape for both the user's country and the US market, present both (remote jobs often use US pay bands)
  • Non-tech role — skip Levels.fyi (tech-only), rely on Glassdoor and Indeed
  • Salary shown in different currencies — normalize to the local currency and note conversion rate used
  • User is asking if their salary is competitive — after presenting the data, ask what they're currently earning and give a direct assessment
Weekly Installs
2
GitHub Stars
1.7K
First Seen
Apr 11, 2026