Looking for the overview — what this API returns, what it costs, and a call you can run without a key? See the Hiring Signals API page →
Jobs & Hiring

Hiring Signals API & Scraper

The Hiring Signals API turns a company domain into a hiring signal as clean JSON.

3 actionsLive JSON1,000 free credits$0.67–$1.50 / 1,000 creditsMCP-ready
Get a free keyOpen in playground

🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.

The primary hiring_signal endpoint resolves the company (name, confidence, match type) and returns its ATS provider and job-board presence with supporting evidence, and a rich variant and batch add depth and scale. It is built for sales intelligence, recruiting and go-to-market tools that need to detect which companies are actively hiring without scraping career pages. One ReefAPI key, one shared credit pool, the standard envelope.

Reference

Where each number comes from, and why the source counts do not add up

This is an aggregate: one domain fans out to an ATS board probe plus two keyword job sources, and those counts measure different things. Summing them inflates the answer, which is why the response reports them separately and gives you a deduplicated total instead. Measured on stripe.com, figma.com and shopify.com.

FieldWhat it countsMeasured
ats.direct_jobs_countthe open-role count read straight off the company's own careers boardstripe.com: greenhouse board "stripe", 580 open roles. This is the number to quote.
ats.returnedhow many of those roles came back as rows for the mix calculations50 of the 580
jobs.count_by_sourceone count per source, deliberately not summedstripe.com: {ats: 580, linkedin: 50, indeed: 0}
jobs.deduplicated_totalunique postings across sources by title + location + company99 for stripe.com. This, not direct_jobs_count, is the denominator for departments and seniority_mix.
departments[].pct / seniority_mix[].pctshare of the deduplicated samplestripe.com sales 43 of 99 = 43.4%; figma.com unspecified seniority 45 of 63 = 71.4%
entity.matchhow the company was pinned down: ats-board, name-confirmed or name-onlyfigma.com scored confidence 0.94 on two pieces of evidence (a greenhouse board and a LinkedIn company); shopify.com fell back to name-confirmed at 0.85
current_hiring_intensitya level plus the basis string that explains itfigma.com: moderate, 159 open roles against ~3,017 employees, open_to_headcount_ratio 0.0527
employer (rich only)firmographics that give the ratio a denominatorfigma.com's linkedin block carried employee_count, company_size, industry, headquarters, founded and specialties; the glassdoor block was null on both rich calls measured
coverage[]per-source status with an honest note about what was droppedindeed on stripe.com: "0/50 keyword hits passed the company-name filter (dropped 50 foreign-company rows)"
freshnessISO-8601 UTC timestamp of the snapshot itselfset per call; there is no stored history behind it
batch.domainsup to 10 domains per call11 domains returns INVALID_PARAM, not a truncated result

The ATS probe covers greenhouse, lever, ashby and workable under the domain stem. shopify.com missed all four, so ats.provider was null, direct_jobs_count was null and current_hiring_intensity came back level "unknown" with basis "no source returned an open-role count". Pass ats_slug when the board token differs from the domain stem, or ats_provider to skip the probe entirely.

Live example

Real request and response JSON

Captured from the indexed primary action, hiring_signal, on .

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/enrich-hiring/v1/hiring_signal",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "domain": "stripe.com"
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "enrich-hiring",
    "endpoint": "hiring_signal",
    "mode": "basic",
    "latency_ms": 13868.6,
    "record_count": 95,
    "bytes": 0,
    "cache_hit": false,
    "completeness_pct": 100,
    "subcalls": [
      {
        "api": "ats-jobs",
        "action": "company_jobs",
        "status": "ok",
        "latency_ms": 422
      },
      {
        "api": "ats-jobs",
        "action": "company_jobs",
        "status": "fail:NOT_FOUND",
        "latency_ms": 616.6
      },
      {
        "api": "ats-jobs",
        "action": "company_jobs",
        "status": "ok",
        "latency_ms": 730.4
      }
    ]
  },
  "data": {
    "coverage": [
      {
        "source": "ats-jobs",
        "status": "ok",
        "count": 524,
        "note": "provider=greenhouse (authoritative direct count)"
      },
      {
        "source": "linkedin",
        "status": "ok",
        "count": 45,
        "note": "45/50 keyword hits passed the company-name filter (dropped 5 foreign-company rows)"
      },
      {
        "source": "indeed",
        "status": "ok",
        "count": 1,
        "note": "1/50 keyword hits passed the company-name filter (dropped 49 foreign-company rows)"
      }
    ],
    "freshness": "[redacted-phone]T17:19:36Z",
    "entity": {
      "input": "stripe.com",
      "resolved_name": "Stripe",
      "confidence": 0.9,
      "match": "ats-board",
      "evidence": [
        {
          "name": "stripe",
          "kind": "ats-board:greenhouse",
          "weight": 0.9
        }
      ]
    },
    "ats": {
      "provider": "greenhouse",
      "slug": "stripe",
      "direct_jobs_count": 524,
      "returned": 50,
      "matched": true,
      "probed": [
        "greenhouse:ok=524",
        "lever:not_found=-",
        "ashby:not_found=-"
      ]
    },
    "jobs": {
      "count_by_source": {
        "ats": 524,
        "linkedin": 45,
        "indeed": 1
      },
      "dedup_count": 1,
      "deduplicated_total": 95,
      "sample": [
        {
          "title": "Account Executive, AI Sales (Grower)",
          "location": "San Francisco, CA",
          "department": "sales",
          "seniority": "unspecified",
          "remote": null,
          "source": "ats",
          "url": "https://stripe.com/jobs/search?gh_jid=7954688",
          "posted": "[redacted-phone]T08:58:57-04:00"
        },
        {
          "title": "Account Executive, Billing",
          "location": "Sydney",
          "department": "sales",
          "seniority": "unspecified",
          "remote": null,
          "source": "ats",
          "url": "https://stripe.com/jobs/search?gh_jid=8029128",
          "posted": "[redacted-phone]T01:35:20-04:00"
        },
        {
          "title": "Account Executive, Commercial Grower (Japanese Fluency)",
          "location": "Japan",
          "department": "sales",
          "seniority": "unspecified",
          "remote": null,
          "source": "ats",
          "url": "https://stripe.com/jobs/search?gh_jid=8042978",
          "posted": "[redacted-phone]T03:24:50-04:00"
        }
      ],
      "note": "count_by_source are reported separately (NOT summed — that would inflate); deduplicated_total = unique postings across sources by title+location+company"
    },
    "departments": [
      {
        "name": "sales",
        "count": 42,
        "pct": 44.2
      },
      {
        "name": "other",
        "count": 15,
        "pct": 15.8
      },
      {
        "name": "engineering",
        "count": 11,
        "pct": 11.6
      }
    ],
    "seniority_mix": [
      {
        "level": "unspecified",
        "count": 72,
        "pct": 75.8
      },
      {
        "level": "manager",
        "count": 13,
        "pct": 13.7
      },
      {
        "level": "director",
        "count": 3,
        "pct": 3.2
      }
    ],
    "remote_ratio": 0.8,
    "remote_basis": "4/5 roles with a known workplace flag",
    "tech_signals": [
      {
        "name": "ruby",
        "count": 1
      },
      {
        "name": "salesforce",
        "count": 1
      },
      {
        "name": "react",
        "count": 1
      }
    ],
    "classified_n": 95,
    "current_hiring_intensity": {
      "level": "very_high",
      "open_jobs": 524,
      "basis": "524 open roles (count-band; no headcount anchor; snapshot, not velocity)"
    }
  }
}
Actions

What the Hiring Signals API does

ActionDescriptionConcrete use caseKey params
hiring_signaldomain → company hiring snapshot: ATS provider + direct open-roles count, per-source counts (deduped), departments/seniority/remote-ratio/tech-signals, entity-confirm, honest snapshot intensityRecruiting teams call hiring_signal to get domain → company hiring snapshot.domain, company_name, ats_slug, ats_provider, countries
hiring_signal_richeverything in hiring_signal + employer firmographics (linkedin company / glassdoor employer: industry, employee_count, HQ, founded, rating) which anchor a headcount-relative intensity (open-roles-to-headcount ratio)Labor-market analysts call hiring_signal_rich to get everything in hiring_signal + employer firmographics (linkedin company / glassdoor employer.domain, company_name, ats_slug, ats_provider, countries
batchhiring_signal for up to 10 domains in one call (cache-first, per-item ok/error)Job boards call batch to get hiring_signal for up to 10 domains in one call (cache-first, per-item ok/error).domains
Code samples

Call hiring_signal from your stack

curl -X POST https://api.reefapi.com/enrich-hiring/v1/hiring_signal \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"domain":"stripe.com"}'
MCP one-liner
Ask your MCP-connected assistant: call reefapi.enrich-hiring.hiring_signal with {"domain":"stripe.com"}.
Use cases

Who uses this API and why

  • Sales-intelligence teams call hiring_signal to flag accounts that are actively hiring (a buying signal).
  • Recruiting tools use hiring_signal_rich to see which ATS a company uses and what roles are open.
  • Go-to-market products use batch to score a whole prospect list for hiring activity at once.
FAQ

Questions developers ask before integrating

How is this different from searching a job board for the company name?

A job search gives you rows; this gives you a company-level count plus the shape of the hiring. The open-role count is read off the company's own ATS board, which is authoritative in a way a keyword search is not, and every keyword hit is then filtered against the resolved company name before it counts. On stripe.com that filter kept 50 of 50 LinkedIn hits and 0 of 50 Indeed hits, which is exactly the noise a raw keyword search would have handed you as Stripe jobs.

Why don't the source counts add up to the total?

Because they count overlapping things, and summing them double-counts every posting that appears on more than one source. stripe.com returned count_by_source {ats: 580, linkedin: 50, indeed: 0}, and deduplicated_total 99 sits beside it. Use direct_jobs_count for "how many roles are open" and deduplicated_total for "how many distinct postings did we see". The response carries a note saying the same thing.

What is the denominator behind the department and seniority percentages?

The deduplicated sample, not the full open-role count. On stripe.com, sales showed count 43 and pct 43.4, which is 43 of the 99 deduplicated postings, not 43 of the 580 roles on the board. Treat the mix as a sample profile of what the company is hiring for and the ATS count as the volume. They answer different questions, and mixing them produces numbers that do not exist.

What happens if the company does not use a supported ATS?

You still get a signal, just a weaker one, and the response tells you which. shopify.com probed greenhouse, lever, ashby and workable and matched none, so ats.provider came back null, direct_jobs_count null, and current_hiring_intensity was level "unknown" with basis "no source returned an open-role count". The ats.note names the likely reasons (an unsupported ATS, or a board slug that is not the domain stem), and ats_slug lets you force the right token.

Why is remote_ratio null?

Because it is computed from a remote flag on individual postings, and most ATS rows do not carry one. Measured on figma.com and shopify.com: remote_ratio null in both, with remote null on the job rows themselves. Null here means the sources did not state it, which is not the same as "no remote roles", so do not render it as 0%.

What does hiring_signal_rich add over hiring_signal?

Employer firmographics, and the one derived number they make possible. The rich action adds an employer block with industry, employee_count, company_size, headquarters, founded and specialties, and current_hiring_intensity then gains open_to_headcount_ratio: figma.com came back at 159 open roles against ~3,017 employees, ratio 0.0527, level moderate. Without a headcount the intensity has no denominator and reports only the raw open-role count.

How fresh is the snapshot, and does it show hiring velocity?

It is fetched at call time and stamped with an ISO-8601 UTC freshness value, and it is a snapshot rather than a trend. The intensity basis string says so in plain words: "snapshot, not velocity". If you want velocity, store the snapshots yourself and difference them. Individual postings in the jobs sample do carry their own posted date, which is the closest thing to a timeline in a single call.

Can I run a list of companies in one call?

Yes, up to 10 domains through the batch action, cache-first, with a per-item ok or error so one bad domain does not fail the run. The cap is enforced rather than trimmed: 11 domains returned INVALID_PARAM with the message "max 10 domains per batch (got 11)". Chunk longer lists on your side.

What is the Hiring Signals API?

Hiring Signals API is a ReefAPI endpoint group for hiring signals It returns live JSON through POST requests under /enrich-hiring/v1.

Is the Hiring Signals API free to try?

Yes. ReefAPI starts with 1,000 free credits, no card required. Hiring Signals calls use the same shared credit balance as every other ReefAPI engine.

Do I need a Hiring Signals login or account?

No login to Hiring Signals is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.

How fresh is the Hiring Signals data?

The page example is captured from a live hiring_signal call, and production requests fetch live data through ReefAPI rather than a static sample.

How many credits does the Hiring Signals API use?

Hiring Signals actions currently cost 5-10 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.

Can I call Hiring Signals from an AI assistant or MCP client?

Yes. Connect ReefAPI once through MCP and your assistant can call enrich-hiring actions with the same key, credit pool and JSON envelope used by normal REST requests.

docs / enrich-hiring

Hiring Signals

Hiring Signals

base /enrich-hiring/v13 endpoints
post/enrich-hiring/v1/hiring_signal6 credits

domain → company hiring snapshot: ATS provider + direct open-roles count, per-source counts (deduped), departments/seniority/remote-ratio/tech-signals, entity-confirm, honest snapshot intensity

ParameterAllowed / rangeDescription
domainoptionalCompany website domain (bare host like stripe.com; a full URL or leading www. is accepted and normalized). The ATS board slug is derived from the domain stem. Provide `domain` OR `company_name` (domain gives the strongest entity anchor).
company_nameoptionalCompany name, used when no domain is available (entity-match is weaker without a domain). Also seeds the ATS slug guess.
ats_slugoptionalAdvanced: force the ATS board token/subdomain (skips slug auto-detect). Use when the board slug differs from the domain stem (e.g. a parent-company board).
ats_provideroptionalgreenhouse · lever · ashby · workableAdvanced: force the ATS provider (skips the parallel probe). One of greenhouse / lever / ashby / workable.
countriesoptionalOptional country hints for the keyword job sources (best-effort location filter; ATS count is global).
Try in playground →
post/enrich-hiring/v1/hiring_signal_rich10 credits

everything in hiring_signal + employer firmographics (linkedin company / glassdoor employer: industry, employee_count, HQ, founded, rating) which anchor a headcount-relative intensity (open-roles-to-headcount ratio)

ParameterAllowed / rangeDescription
domainoptionalCompany website domain (bare host like stripe.com; a full URL or leading www. is accepted and normalized). The ATS board slug is derived from the domain stem. Provide `domain` OR `company_name` (domain gives the strongest entity anchor).
company_nameoptionalCompany name, used when no domain is available (entity-match is weaker without a domain). Also seeds the ATS slug guess.
ats_slugoptionalAdvanced: force the ATS board token/subdomain (skips slug auto-detect). Use when the board slug differs from the domain stem (e.g. a parent-company board).
ats_provideroptionalgreenhouse · lever · ashby · workableAdvanced: force the ATS provider (skips the parallel probe). One of greenhouse / lever / ashby / workable.
countriesoptionalOptional country hints for the keyword job sources (best-effort location filter; ATS count is global).
Try in playground →
post/enrich-hiring/v1/batch5 credits

hiring_signal for up to 10 domains in one call (cache-first, per-item ok/error)

ParameterAllowed / rangeDescription
domainsrequiredUp to 10 domains (array or comma-separated). Each gets a hiring_signal snapshot; per-item ok/error, cache-first.
Try in playground →