Salary and skills research

Pick an occupation, see what it pays and which skills it asks for, then benchmark a salary, using the Insights endpoints.

The Insights endpoints answer labour-market questions from live job postings: which roles are hiring, what they pay, and which skills are rising. This guide follows one research path: find an occupation, drill into it, compare industries and AI demand, and benchmark a salary. Each call costs 1 credit.

Occupations use ISCO-08 codes and industries use ISIC Rev.4 divisions. Every endpoint takes window_days (default 90, max 365).

1. Pick an occupation

curl "https://api.jobspipe.dev/v1/insights/occupations?window_days=90" \
  -H "Authorization: Bearer jp_live_your_key_here"
200 OK
{
  "window_days": 90,
  "occupations": [
    { "code": "2512", "label": "Software Developers", "count": 184210, "share": 0.061 },
    { "code": "2221", "label": "Nursing Professionals", "count": 151900, "share": 0.05 }
  ]
}

A code with 1 to 3 digits rolls up a whole group: 25 is every ICT professional.

2. Drill into it

Four calls describe one occupation: a snapshot (volume, remote share, top companies, titles and countries), advertised pay, the skills it asks for most, and the skills growing fastest.

H="Authorization: Bearer jp_live_your_key_here"
BASE=https://api.jobspipe.dev/v1/insights/occupations/2512

curl "$BASE" -H "$H"
curl "$BASE/compensation?country=US" -H "$H"
curl "$BASE/skills/top" -H "$H"
curl "$BASE/skills/trending?direction=up" -H "$H"
import requests

HEADERS = {"Authorization": "Bearer jp_live_your_key_here"}
BASE = "https://api.jobspipe.dev/v1/insights/occupations/2512"

snapshot = requests.get(BASE, headers=HEADERS).json()
pay = requests.get(f"{BASE}/compensation", headers=HEADERS, params={"country": "US"}).json()
top = requests.get(f"{BASE}/skills/top", headers=HEADERS).json()
rising = requests.get(f"{BASE}/skills/trending", headers=HEADERS, params={"direction": "up"}).json()

print(snapshot["occupation_label"], snapshot["count"], "postings,", round(snapshot["remote_share"] * 100), "% remote")
if not pay["suppressed"]:
    print("US median:", pay["percentiles"]["p50"], pay["currency"], f"({pay['confidence']} confidence)")
print("Top skills:", [s["skill"] for s in top["skills"][:10]])
print("Rising:", [(s["skill"], s["growth_pct"]) for s in rising["skills"][:5]])
const headers = { Authorization: "Bearer jp_live_your_key_here" };
const BASE = "https://api.jobspipe.dev/v1/insights/occupations/2512";
const get = (url) => fetch(url, { headers }).then((r) => r.json());

const [snapshot, pay, top, rising] = await Promise.all([
  get(BASE),
  get(`${BASE}/compensation?country=US`),
  get(`${BASE}/skills/top`),
  get(`${BASE}/skills/trending?direction=up`),
]);

console.log(snapshot.occupation_label, snapshot.count, "postings");
if (!pay.suppressed) console.log("US median:", pay.percentiles.p50, pay.currency);
console.log("Top skills:", top.skills.slice(0, 10).map((s) => s.skill));
console.log("Rising:", rising.skills.slice(0, 5).map((s) => [s.skill, s.growth_pct]));
compensation: 200 OK
{
  "occupation_code": "2512",
  "occupation_label": "Software Developers",
  "window_days": 90,
  "country": "US",
  "count": 8945,
  "currency": "USD",
  "percentiles": { "p10": 98000, "p25": 130000, "p50": 175500, "p75": 210000, "p90": 245000 },
  "confidence": "high",
  "suppressed": false
}
skills/trending: 200 OK
{
  "window_days": 90,
  "direction": "up",
  "skills": [
    { "skill": "rust", "recent_count": 3120, "prior_count": 2210, "recent_share": 0.017, "prior_share": 0.012, "growth_pct": 41.2 }
  ]
}

Trending compares the window with the one before it; use direction=down for declining skills.

3. Follow a skill

Pick a skill from the lists above and see its monthly share of postings, plus the occupations and industries that ask for it most.

curl "https://api.jobspipe.dev/v1/insights/skills/rust/trend?window_days=365" \
  -H "Authorization: Bearer jp_live_your_key_here"
200 OK
{
  "skill": "rust",
  "window_days": 365,
  "series": [
    { "month": "2026-08", "mentions": 1480, "total": 212000, "share": 0.007 },
    { "month": "2026-09", "mentions": 1620, "total": 205000, "share": 0.0079 }
  ],
  "top_occupations": [{ "value": "2512", "label": "Software Developers", "count": 9100, "share": 0.61 }],
  "top_industries": [{ "value": "62", "label": "Computer programming, consultancy and related activities", "count": 5400, "share": 0.36 }]
}

4. Compare industries and AI demand

List industries by posting volume, see which skills are rising inside one of them, and compare how much AI skills are asked for, and paid for, across occupations.

H="Authorization: Bearer jp_live_your_key_here"

curl "https://api.jobspipe.dev/v1/insights/industries" -H "$H"
curl "https://api.jobspipe.dev/v1/insights/industries/64/skills/trending?direction=up" -H "$H"
curl "https://api.jobspipe.dev/v1/insights/technology/ai-exposure?by=occupation" -H "$H"
ai-exposure: 200 OK
{
  "window_days": 90,
  "by": "occupation",
  "rows": [
    { "value": "2512", "label": "Software Developers", "count": 184210, "ai_share": 0.14, "ai_median_usd": 192000, "non_ai_median_usd": 168000, "premium_pct": 14.3 }
  ]
}

ai_share is the share of postings asking for AI skills; premium_pct is how much more those postings advertise at the median. Pass by=industry to group by ISIC division instead.

5. Benchmark a salary

Finally, check an offer against the market. The benchmark takes an occupation code or a title, plus optional country and seniority (entry, mid, senior, lead, exec).

curl https://api.jobspipe.dev/v1/insights/salary/benchmark \
  -H "Authorization: Bearer jp_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "data engineer",
    "country": "US",
    "seniority": "senior",
    "window_days": 90
  }'
bench = requests.post(
    "https://api.jobspipe.dev/v1/insights/salary/benchmark",
    headers=HEADERS,
    json={"title": "data engineer", "country": "US", "seniority": "senior", "window_days": 90},
).json()

offer = 165000
if bench["suppressed"]:
    print("Not enough postings with stated pay to benchmark.")
else:
    p = bench["percentiles"]
    band = next((k for k in ["p10", "p25", "p50", "p75", "p90"] if offer <= p[k]), "above p90")
    print(f"{offer} is at or below {band} (median {p['p50']}, n={bench['count']}, {bench['confidence']})")
const bench = await fetch("https://api.jobspipe.dev/v1/insights/salary/benchmark", {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ title: "data engineer", country: "US", seniority: "senior", window_days: 90 }),
}).then((r) => r.json());

if (bench.suppressed) console.log("Not enough postings with stated pay to benchmark.");
else console.log("Median:", bench.percentiles.p50, "n =", bench.count, bench.confidence);

The response has the same shape as the compensation endpoint: count, currency, percentiles (p10 to p90), confidence and suppressed.

Reading the numbers

  • Pay is advertised pay from postings that state it, normalized to annual USD. It measures what employers offer publicly, which is not the same as survey-based earnings.
  • Small samples are withheld. Under 30 postings, a percentile response has suppressed: true and percentiles: null. List endpoints only include groups with at least 30 postings.
  • confidence follows the sample size: low (30 to 99 postings), medium (100 to 999), high (1,000 or more). The exact count is always included.
  • A 404 means an unknown code, or no postings in the window.

On this page