# Dice Jobs Scraper - Tech Roles, Companies, Dates (`s-r/dice-scraper`) Actor

Scrape tech job listings from Dice by keyword and location. Returns job title, company, location, posting date as both the original text and a day count, a remote flag and a direct link, deduplicated across search terms and pages.

- **URL**: https://apify.com/s-r/dice-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Dice Jobs Scraper

Tech job listings from Dice, by keyword and location. Give it a list of search
terms and you get back one row per job: title, company, location, how long ago
it was posted, and a direct link.

Dice is a specialist board rather than a general one, so the results skew
heavily toward software, data, infrastructure and security roles, including a
large volume of contract and staffing-agency work that does not appear on
consumer job sites.

### What each job gives you

- `title` and `url`, a direct link to the listing
- `company`, which on Dice is often the staffing agency rather than the end
  client. That is how Dice publishes it, and inventing an end client we cannot
  see would be worse than reporting what is there.
- `location`, and `is_remote` derived from it
- `posted`, exactly as Dice wrote it (`Today`, `2d ago`, `21d ago`)
- `posted_days_ago`, that same age converted to a number so you can filter and
  sort on it
- `query` and `page`, so every row traces back to the search that found it

Both forms of the date are kept on purpose. The number is what you filter on;
the original text is what lets you check the number.

### Two details that decide whether the output is right

**Location and posted date share one element.** A Dice card renders
`McLean, Virginia • Today` as a single line, separated by a bullet. Split it
wrong and your location column reads `Virginia • Today`, which sorts, groups
and filters badly while looking almost correct. The two halves are separated
properly, and a card that carries only a date is not mistaken for a card that
carries only a location.

**Dice abbreviates its dates.** The page says `21d ago`, not `21 days ago`. A
date parser written for the long form matches nothing and quietly returns a
null day count for every job older than today, which makes a run look far
fresher than it is. Both the abbreviated and long forms are handled, and a date
that cannot be read is reported as unknown rather than as zero days, because
zero would mean "posted today".

### How the fields are found

Every field is read through Dice's own `data-testid` attributes rather than
through styling classes. Those attributes exist for Dice's internal test suite
and change far less often than the utility classes wrapped around them, so this
survives a visual redesign that would break a class-based parser.

### Input

- **Search terms** — job titles, skills or technologies. `python developer`,
  `kubernetes`, `security engineer`. A full Dice search URL is also accepted
  and used exactly as pasted, which is the escape hatch for filters this input
  form does not expose.
- **Location** — `Remote`, `Austin, TX`, `New York`, or empty for everywhere
- **Result pages per search** — each page is roughly 30 cards and one fetch
- **Maximum jobs per search**, **maximum search terms**, **pages in parallel**,
  **retries**, and an optional **country to request from**

### Run summary

Jobs returned, how many carried a company, how many carried a location, how
many are remote, the number of unique companies, and counts of listings posted
today and within the last week.

Those last two are the useful ones for a recurring run: a search that returns
the same 60 jobs every day with none posted this week is telling you the market
is quiet, not that the actor is broken.

### Scale and deduplication

Each result page costs exactly one fetch. Two search terms at one page each
returned 69 jobs in under three seconds in testing, covering 54 distinct
companies.

Rows are deduplicated on Dice's job identifier across every term and page in
one run, so searching `python developer` and `python engineer` together gives
you the union rather than a pile of duplicates. The identifier is stable, so
deduplicating across separate runs works the same way.

### What people use this for

**Hiring-market tracking.** Run a fixed set of search terms weekly and keep the
rows. Because job ids are stable and `posted_days_ago` is a number, you can
measure how many genuinely new listings appear per week per skill, which is a
far better demand signal than a total result count that includes months-old
postings.

**Skill demand comparison.** Search several technologies in one run and compare
unique company counts rather than raw job counts. One agency posting the same
role forty times inflates the second number and not the first.

**Agency versus direct hiring.** Dice carries a lot of staffing-agency listings
alongside direct employers. Grouping by `company` shows the split immediately,
which matters if you are a candidate deciding where to spend effort or a
recruiter sizing the competition.

**Location analysis.** Pairing the same search term across several locations,
including `Remote`, shows where a skill is actually being hired for rather than
where people assume it is.

### Notes

Dice publishes relative dates only, so `posted_days_ago` is accurate to the day
rather than the hour. A job showing `5h ago` is reported as 0 days.

Listings that are filled or expire stop appearing in results. Running the same
searches on a schedule and keeping the rows gives you both a hiring-volume
trend and a read on how long postings stay open.

No login, no cookies and no API key are needed.

# Actor input Schema

## `queries` (type: `array`):

What to search Dice for. Job titles, skills or technologies all work, for example 'python developer' or 'kubernetes'. A full Dice search URL is also accepted and used as pasted.

## `location` (type: `string`):

Where to search, for example 'Remote', 'Austin, TX' or 'New York'. Leave empty to search everywhere.

## `pages` (type: `integer`):

How many pages to walk for each term. Each page holds roughly 30 job cards and costs one fetch.

## `jobs_per_query` (type: `integer`):

Upper bound on rows returned for each search term.

## `max_queries` (type: `integer`):

How many terms to process in one run.

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

How many result pages to fetch at the same time.

## `retries` (type: `integer`):

Retries with backoff before a page is reported as an error.

## `country` (type: `string`):

Optional two-letter country code to request from, for example us. Leave empty to request from wherever the run happens to sit.

## Actor input object example

```json
{
  "queries": [
    "python developer",
    "data engineer"
  ],
  "location": "Remote",
  "pages": 1,
  "jobs_per_query": 500,
  "max_queries": 30,
  "concurrency": 4,
  "retries": 3,
  "country": "us"
}
```

# Actor output Schema

## `jobs` (type: `string`):

One row per job listing.

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

Counts, unique companies and how many were posted recently.

## `errors` (type: `string`):

Searches that could not be read, with a code and a redacted message.

# 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 = {
    "queries": [
        "python developer",
        "data engineer"
    ],
    "location": "Remote",
    "pages": 1,
    "jobs_per_query": 500,
    "max_queries": 30,
    "concurrency": 4,
    "retries": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/dice-scraper").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 = {
    "queries": [
        "python developer",
        "data engineer",
    ],
    "location": "Remote",
    "pages": 1,
    "jobs_per_query": 500,
    "max_queries": 30,
    "concurrency": 4,
    "retries": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/dice-scraper").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 '{
  "queries": [
    "python developer",
    "data engineer"
  ],
  "location": "Remote",
  "pages": 1,
  "jobs_per_query": 500,
  "max_queries": 30,
  "concurrency": 4,
  "retries": 3
}' |
apify call s-r/dice-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/dice-scraper"
        }
    }
}

```

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/VXill7HZBxrje6P5z/builds/Wh1qG3UfGZibjPuP8/openapi.json
