# First-Time Hire Signals: a company's first marketer or IT hire (`fractionalhqforyou/first-time-hire-signals`) Actor

I read a company's live job postings and remember which functions it has hired for before. When a function such as marketing or IT appears for the first time, that is the row: the company has no team for it yet. Each row carries one line of reason and one pitch angle for whoever fills that gap.

- **URL**: https://apify.com/fractionalhqforyou/first-time-hire-signals.md
- **Developed by:** [Jessy Mariau](https://apify.com/fractionalhqforyou) (community)
- **Categories:** Lead generation, Automation, Jobs
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 company checkeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## First-Time Hire Signals

A company that posts its first ever Marketing Manager has no marketing team. Until that person starts, and for a good while after, the work is done by whoever is nearest or by an agency. The same is true of the first salesperson, the first IT hire, the first security engineer, the first finance manager. The posting is public. The gap it describes is the sales conversation.

I built this to spot those postings without reading every board by hand. Point it at the companies you care about, run it weekly, and it tells you which company is hiring for a function for the first time.

### What it does

You give it public job boards, career page URLs, or LinkedIn company ids. Seven board systems are read directly:

- Greenhouse
- Lever
- Ashby
- Personio
- Workable
- SmartRecruiters
- Recruitee

It reads every open posting and sorts each one into a function from its title, with the department as a fallback. The functions it knows:

- marketing
- sales
- customer success and support
- people and HR
- finance
- IT and internal ops
- security
- data and analytics
- design
- product management
- legal and compliance
- operations
- engineering leadership

Anything that matches no rule is `other` and is never reported. A Senior Software Engineer is not a signal.

Then it compares what it found with the snapshot it saved last time you ran with the same watch key, and writes one row per employer and function with a status:

`NEW_CATEGORY` is the row you are paying for. The employer was in the last snapshot, had no posting in this function then, and has one now.

`FOUNDING_TITLE` fires when the only posting in a function carries a title like "first", "founding" or "head of". It is inferred from the words in the title rather than from history, and the row says so, which is why it can fire on a first run.

`BASELINE` is every function present on the first run against a new watch key. There is nothing to compare against yet, so nothing is claimed.

`RECURRING` is a function the employer already had on file.

Every row carries a sample title with its URL and the number of postings open in that function now. It also says when the function was first seen on file. Underneath sit one sentence of reason with the numbers in it and one pitch angle written from the row alone. The pitch angle for a first marketing hire reads: *Acme is hiring its first marketing role, so until that person starts there is no marketing function in-house yet.*

### Who I built it for

- Marketing and growth agencies, who want the company that has just decided marketing is worth a salary and does not have a team yet
- Managed service providers and IT consultancies, for the first internal IT hire
- Security consultancies and compliance shops, for the first security or GRC role
- Fractional CFOs, finance outsourcers and recruiters, for the first finance or people hire
- Anyone selling into a function, who wants to know the week a company starts building it

### How to run it

Put one board per line as `ats:identifier`, one career page URL per line, or one numeric LinkedIn company id per line. Pick a watch key and keep using it for the same list. Run it, then sort the Overview table on the status column.

```json
{
  "atsBoards": "greenhouse:getyourguide\nlever:qonto\nrecruitee:channable\nworkable:skroutz",
  "watchedRoles": ["marketing", "sales", "it_ops", "security", "finance"],
  "watchKey": "eu-scaleups",
  "maxPostingsPerSource": 100
}
```

If another Actor already scraped the postings, paste them into the `postings` field as a JSON array of records with `employer` and `title` (plus `url`, `department`, `location` and `date_posted` when you have them) and nothing is fetched.

Leave every source empty and you get four demo rows, one per status, built by the same code that scores real ones. Nothing is charged and nothing leaves the container.

### What comes back

The dataset holds one row per employer and function. The NEW\_CATEGORY and FOUNDING\_TITLE rows come first. I also write an OUTPUT record with the count per status, every signal row named with its pitch angle, any source that failed and why, and one plain line you can post into a channel.

The snapshot lives in a named key-value store on your account, `first-hire-` followed by your watch key, so a scheduled run and an agent calling the MCP tool see the same history.

### What it costs

You are charged once per employer that produced at least one posting, whatever the number of postings or functions. The LinkedIn leg runs the public LinkedIn Jobs Scraper by curious\_coder on your own account and that run is billed on top, exactly as if you had run it yourself. Boards and career pages cost nothing beyond the per-employer event.

### Honest limits

- The first run is a baseline, not a finding. The signal arrives on the second run and after, when a function shows up that was not there before. Run it on a schedule.
- "First hire" is inferred from what the board shows. A company that already has a marketing team and posts one more marketer after months of no marketing postings will look like a new function to me, because I only know what I have seen. The row says when the function was first seen on file, so you can judge.
- Classification is a rule table on the title and department, not a model. It covers the common titles and it will misfile the odd creative one. Open the sample URL before you write.
- Teamtailor boards are not supported, because their jobs API needs a key issued per company. Give me the career page URL instead.
- The LinkedIn leg needs an Apify plan that can run public Actors. When it cannot run, the error is written into `source_errors` and the rest of the run carries on.

Built by **Fractional HQ** · https://fractionalhq.uk

# Actor input Schema

## `atsBoards` (type: `string`):

One per line, as ats:identifier. Supported: greenhouse:boardToken, lever:site, ashby:jobBoardName, personio:company, workable:subdomain, smartrecruiters:companyIdentifier, recruitee:company. Examples: greenhouse:getyourguide, lever:qonto, ashby:pennylane, workable:skroutz, recruitee:channable. Teamtailor is not supported: its jobs API needs a per-company key.

## `careerPageUrls` (type: `string`):

One per line. JobPosting JSON-LD on the page is read where it exists, otherwise job links on the page are followed up to the cap below.

## `linkedinCompanyIds` (type: `string`):

One numeric LinkedIn company id per line, for example 1441. These run the public curious\_coder/linkedin-jobs-scraper Actor for the past month of postings and that run is billed to your own Apify account on top. Leave empty to skip LinkedIn.

## `watchedRoles` (type: `array`):

Only rows in these functions are written. Every posting is still classified and remembered, so narrowing this list does not lose history.

## `watchKey` (type: `string`):

Names the saved snapshot this run compares against. Use the same key every time for the same list of companies, and a different key for a different list.

## `maxPostingsPerSource` (type: `integer`):

Cap on postings read from each board, career page or LinkedIn company.

## `postings` (type: `array`):

A JSON array of posting records to classify without fetching anything. Each needs employer and title; url, department, location and date\_posted are optional. Useful when another Actor already scraped the postings.

## Actor input object example

```json
{
  "watchedRoles": [
    "marketing",
    "sales",
    "customer_success",
    "people_hr",
    "finance",
    "it_ops",
    "security",
    "data",
    "design",
    "product",
    "legal",
    "operations",
    "engineering_leadership"
  ],
  "watchKey": "default",
  "maxPostingsPerSource": 50,
  "postings": []
}
```

# Actor output Schema

## `signals` (type: `string`):

One row per employer and function: status (NEW\_CATEGORY, FOUNDING\_TITLE, BASELINE, RECURRING), when the function was first seen, how many postings are open now, a sample title and URL, the reason with its numbers, and one pitch angle for the provider that fills that gap.

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

The OUTPUT record: employers checked, the count per status, every NEW\_CATEGORY and FOUNDING\_TITLE row named first, source errors, and one plain line.

# 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 = {
    "atsBoards": "",
    "careerPageUrls": "",
    "linkedinCompanyIds": "",
    "postings": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("fractionalhqforyou/first-time-hire-signals").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 = {
    "atsBoards": "",
    "careerPageUrls": "",
    "linkedinCompanyIds": "",
    "postings": [],
}

# Run the Actor and wait for it to finish
run = client.actor("fractionalhqforyou/first-time-hire-signals").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 '{
  "atsBoards": "",
  "careerPageUrls": "",
  "linkedinCompanyIds": "",
  "postings": []
}' |
apify call fractionalhqforyou/first-time-hire-signals --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fractionalhqforyou/first-time-hire-signals"
        }
    }
}

```

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/G4o7sIb9w0WEjfkRf/builds/aMhdhhi5pRvGTKIz5/openapi.json
