# iCIMS Careers Portal Scraper for icims.com (`usestring/icims-jobs`) Actor

Collects job postings from any iCIMS careers portal on icims.com: title, company or brand, department, employmentType, postedAt, apply URL, and the location split out of the portal's country-state-city code. Follows the portal's own paginator, so a 1,000-posting site comes back in full.

- **URL**: https://apify.com/usestring/icims-jobs.md
- **Developed by:** [String](https://apify.com/usestring) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.75 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

### What does iCIMS Job Board Scraper do?

It walks a company's iCIMS careers portal and writes one row per posting on the job search, following
the portal's own paginator until the postings run out. Each row carries the posting id, title, the
company or brand the role sits under, the category, the position type, the posted date, the apply URL,
and the location split out of the code iCIMS writes it in.

An iCIMS portal is addressed by subdomain, not by a path. Every tenant gets its own host, so
`https://careers-emcorgroup.icims.com` and `https://careers-eastwestbank.icims.com` are two different
portals. Pass the URL or just the subdomain.

Two things about iCIMS shape how this works:

- Every tenant renders job cards from the same platform build, so the card markup is a stable contract
  across all of them. What differs is which fields a tenant switches on and what it calls them.
- iCIMS serves the branded wrapper with zero cards unless the request presents iframe fetch metadata.
  That is not a URL parameter. Without those headers a portal listing 1,450 postings answers with a
  98 KB shell and nothing in it.

No iCIMS account, cookie or API key is used. This is the job search a logged-out visitor sees.

### What data does it extract?

| Field | Type | Description |
| --- | --- | --- |
| `jobId` | string | iCIMS's posting id, taken from the job URL. Stable, so use it to de-duplicate |
| `title` | string | The posting title. Falls back to the anchor's title attribute when a tenant drops the heading |
| `company` | string | From whichever company field the tenant publishes: `Company`, `Subsidiary`, `Brand`, `Business Unit`, `Facility` and similar. `null` when the portal publishes none |
| `tenant` | string | The portal subdomain, for example `careers-emcorgroup` |
| `location` | string | Verbatim, in the iCIMS code `country-state-city`. A posting open in several places lists them all, pipe-separated |
| `city` | string | From that code, describing the first location listed |
| `state` | string | From that code |
| `country` | string | From that code |
| `isRemote` | boolean | From the tenant's workplace field where it has one, otherwise from the location text. `null` when neither is present |
| `department` | string | From the tenant's category field: `Category`, `Function`, `Department`, `Job Family`, `Area of Interest` and similar |
| `employmentType` | string | From the tenant's type field: `Position Type`, `Employment Type`, `Full/Part Time` and similar |
| `postedAt` | string | `YYYY-MM-DD`, or `YYYY-MM-DDTHH:MM:SS` when the portal publishes a time. Local wall clock, see the FAQ |
| `jobUrl` | string | Direct link to the posting |
| `boardUrl` | string | `https://<tenant>.icims.com` |
| `sourceUrl` | string | The search page the row was read from |
| `collectedAt` | string | ISO 8601 timestamp of the run |

#### How the fields are matched

Every value on an iCIMS card is labelled in the markup, either by a `field-label` span in the card
header or by a term in the header tag list. This Actor reads those labels and maps them onto contract
fields, rather than trusting positions. Tenants also prefix labels with the form section they came from
(`Requisition Post Information* : Posted Date`), so the label is reduced to its last colon segment
before matching.

The practical result: a portal that calls its category column `Function` and another that calls it
`Area of Interest` both land in `department`, and a field a tenant does not publish comes back `null`
instead of holding some other column's value.

### Why scrape iCIMS careers portals?

iCIMS is where high-volume employers run their hiring: construction and trades, healthcare systems,
retail chains, regional banks, manufacturers. These are not the companies you find well covered on
technology-focused boards, and the postings are frequently hourly or site-based roles that aggregators
index unevenly.

- Track requisition volume at a facility or region level using `location`, which iCIMS codes precisely.
- Watch a competitor's plant, store or clinic openings by the postings that appear before them.
- Feed a staffing agency's pipeline with fresh requisitions from a defined employer list.
- Segment by `employmentType` to separate full-time headcount from seasonal or per-diem demand.
- Supply labour-market research that needs employers outside the technology sector.
- Build local candidate-facing job boards for a metro area from the employers who actually hire there.

### How to use it

1. Open the company's "careers" or "search jobs" link. If the address bar shows something like
   `careers-<company>.icims.com`, that host is the portal.
2. Put the URL, or just the subdomain, into **Job boards**. Up to 100 portals per run.
3. Set **Maximum results**. This matters more here than on a single-request board, because a large
   portal can hold thousands of postings and pagination is where the requests go.
4. Leave **Concurrency** at 3 unless you are collecting many portals and want to finish sooner.
5. Start the run, then export as JSON, CSV, Excel or XML, or pull the dataset over the API.

`maxItems` is a budget shared across every portal in the input, and each portal stops paginating as
soon as the run can no longer keep the rows. A hundred portals under a thousand-row cap fetch a
thousand rows, not a hundred portals in full.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `boards` | array of strings | required | iCIMS portal URLs or tenant subdomains. Between 1 and 100 entries |
| `maxItems` | integer | `1000` | Cap on dataset rows, up to 50,000. Shared across all portals |
| `concurrency` | integer | `3` | Portals fetched in parallel, up to 5 |

```json
{
  "boards": [
    "https://careers-emcorgroup.icims.com/",
    "careers-eastwestbank"
  ],
  "maxItems": 2000,
  "concurrency": 3
}
```

A host outside `icims.com` is rejected and reported as you wrote it, rather than fetched.

### Output

One row per job card. Illustrative shape:

```json
{
  "jobId": "28417",
  "title": "Journeyman Electrician",
  "company": "EMCOR Group",
  "tenant": "careers-emcorgroup",
  "location": "US-PA-Wilkes-Barre",
  "city": "Wilkes-Barre",
  "state": "PA",
  "country": "US",
  "isRemote": false,
  "department": "Skilled Trades",
  "employmentType": "Regular Full-Time",
  "postedAt": "2026-07-14T09:32:00",
  "jobUrl": "https://careers-emcorgroup.icims.com/jobs/28417/journeyman-electrician/job",
  "boardUrl": "https://careers-emcorgroup.icims.com/",
  "sourceUrl": "https://careers-emcorgroup.icims.com/jobs/search?ss=1&searchRelation=keyword_all&in_iframe=1&pr=0",
  "collectedAt": "2026-08-20T09:14:02.118Z"
}
```

A posting open in several places keeps all of them in `location`, pipe-separated, and the three geo
fields describe the first one. A tenant that writes locations outside the coded form leaves those three
`null` with the raw string still in `location`.

Each run also writes a `SUMMARY` record with the item count, the failure count, and every page that
failed with its error.

### How much does it cost?

Pay per event, charged once per result row written to the dataset. The current rate is on this Actor's
pricing tab, and a portal that cannot be read writes no rows and costs nothing.

Unlike a single-request board feed, an iCIMS portal is paginated, so requests scale with rows. That is
what `maxItems` is really controlling: set it to what you intend to use.

Runs started from an Apify **free plan** stop at **250 requests and 250 results** and say so in the run
status. Any paid plan runs the full input and your `maxItems`. The limit binds on requests as well as
rows because these fetches run on our own infrastructure, which Apify does not reimburse for free-plan
runs.

### Using it with the Apify API

```javascript
import { ApifyClient } from "apify-client";

const client = new ApifyClient({ token: "<YOUR_APIFY_TOKEN>" });

const run = await client.actor("usestring/icims-jobs").call({
  boards: ["https://careers-emcorgroup.icims.com/"],
  maxItems: 2000,
  concurrency: 3
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const byState = items.reduce((counts, job) => {
  const key = job.state ?? "unknown";
  counts[key] = (counts[key] ?? 0) + 1;
  return counts;
}, {});
console.log(byState);
```

The run's `SUMMARY` record, in the same run's key-value store, lists anything that failed.

### Is it legal to scrape iCIMS careers portals?

A careers portal exists to be found. Employers publish these pages for candidates and search engines,
and this Actor reads what an anonymous visitor's browser is served. No login, no session cookie, no API
key, nothing gated.

The rows describe requisitions rather than people. There are no applicant records, no application
counts and no recruiter contact details in the output.

What you are permitted to do with the collected data still depends on your jurisdiction and on any
terms you have accepted. Treat this section as context, not legal advice.

### Related actors

- [Workday Job Board Scraper](https://apify.com/usestring/workday-jobs) covers the other enterprise ATS
  you will hit constantly in the same employer set.
- [Taleo Job Board Scraper](https://apify.com/usestring/taleo-jobs) covers Oracle Taleo, still common at
  large employers that have not migrated.
- [Greenhouse Job Board Scraper](https://apify.com/usestring/greenhouse-jobs) returns a whole board in
  one request for technology-sector employers.
- [Indeed Job Listings Scraper](https://apify.com/usestring/indeed-jobs) is the better starting point
  when you want a labour market rather than named employers.
- [Glassdoor Jobs Scraper](https://apify.com/usestring/glassdoor-jobs) pairs postings with the employer
  reputation data candidates read.

### FAQ

**How do I find a company's iCIMS portal?** Click "careers" or "search jobs" on the company's site and
look at the host in the address bar. iCIMS portals are always `<tenant>.icims.com`, where the tenant is
a name the company chose, often prefixed with `careers-`. Both the full URL and the bare subdomain work
as input.

**How many postings does one request return?** It varies. iCIMS lets each tenant set its own page size,
so this Actor follows the portal's `rel="next"` link until the rows run out or the budget does, to a
ceiling of 200 pages per portal. That ceiling exists so a paginator that never terminates cannot
consume a whole run.

**Why are `company`, `department` and `employmentType` sometimes empty?** Because the tenant did not
put them on the job card. iCIMS lets each employer choose which fields appear and what to call them, so
those three are filled only when a matching labelled field is present.

**Is `postedAt` in UTC?** No, and it is deliberately not stamped as though it were. iCIMS renders the
posting time in the portal's own timezone and never states which one, so the value is kept as a local
wall-clock stamp rather than given an invented offset. Dates in `M/D/YYYY` form are read as en-US,
which is what these portals write.

**Does it open each posting's detail page?** No. The full `JobPosting` structured data lives on the
detail page, one fetch per posting, and this Actor stays on the search results. Follow `jobUrl` for the
description, requirements and any structured data the tenant publishes there.

**What if a portal's paginator points somewhere else?** A `rel="next"` link that leaves the portal's own
host is not followed. The paginator is markup the tenant controls, and following it off-host would mean
proxying a stranger's site under this Actor's pricing.

**Can I search or filter within a portal?** Not from the input. The run collects the portal's unfiltered
job search and you filter the dataset afterwards, which is usually cheaper than one run per filter.

### Feedback

If a portal comes back empty, or a field lands in the wrong column, open an issue from this Actor's
Store page and include the portal host. Label mapping is the part most likely to need a new alias, and
a named portal is all it takes to add one.

# Actor input Schema

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

iCIMS portal URLs (…icims.com).

## `maxItems` (type: `integer`):

Global cap on dataset items. Runs started from an Apify free plan stop at 250 requests and 250 results; any paid plan runs the full amount.

## `concurrency` (type: `integer`):

Targets fetched in parallel.

## Actor input object example

```json
{
  "boards": [
    "https://careers-emcorgroup.icims.com/"
  ],
  "maxItems": 1000,
  "concurrency": 3
}
```

# Actor output Schema

## `results` (type: `string`):

Collects job postings from any iCIMS careers portal on icims.com: title, company or brand, department, employmentType, postedAt, apply URL, and the location split out of the portal's country-state-city code. Follows the portal's own paginator, so a 1,000-posting site comes back in full.

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

Item count, failure count and every target that failed, with its error.

# 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": [
        "https://careers-emcorgroup.icims.com/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("usestring/icims-jobs").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": ["https://careers-emcorgroup.icims.com/"] }

# Run the Actor and wait for it to finish
run = client.actor("usestring/icims-jobs").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": [
    "https://careers-emcorgroup.icims.com/"
  ]
}' |
apify call usestring/icims-jobs --silent --output-dataset

```

## MCP server setup

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

```

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/3gFbtOuOx7xNal37r/builds/NPASOAkQCgaRAkXZ5/openapi.json
