# Dice Jobs Scraper (US tech job search) (`dataforge-tools/dice-jobs`) Actor

Get job listings from Dice search results (dice.com, US tech jobs): title, employer, location, employment type, remote/hybrid, salary when published, posting date, a short excerpt and the link. Plain requests to public pages, robots.txt obeyed, no login. Tracks new vs. existing jobs between runs.

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

## Pricing

from $2.00 / 1,000 jobs

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Dice Jobs Scraper: US tech job search results from dice.com

Give it a keyword (and optionally a location) and get **the jobs Dice lists for that search** as clean JSON: title, employer, location, employment type, on-site/remote/hybrid, salary when the employer published one, posting date, a short excerpt and the link to the posting. Each run also tells you **what changed since the last run**: new jobs, jobs still listed, and jobs that dropped off.

- **US tech jobs:** Dice is the US technology job board. Enter `python`, `data engineer in Austin, TX`, `devops in Texas`, or paste a Dice results URL.
- **Plain requests to public pages, nothing else:** one request per results page, the same page a visitor opens. No login, no proxies, no CAPTCHA or anti-bot workarounds, and the site's robots.txt is checked and obeyed during the run.
- **Structured data, not scraped HTML:** the results page embeds its job list as data (the Next.js page payload); the Actor reads that, so fields are consistent and do not break when the page design changes.
- **Only robots-allowed URLs.** Dice's robots.txt forbids crawlers from using the query-style search (`/jobs?q=...`) but allows the path-style pages (`/jobs/q-python-jobs`, `/jobs/q-python-l-Austin%2C_TX-jobs`) and their `?page=N` continuation. The Actor builds only those. By default each search returns **its first page, 20 jobs**; raise **Max jobs per search** to read more pages (up to the Actor's limit of 25 pages).
- **Change tracking built in:** every record has `changeStatus` (`new` or `existing`) and `firstSeenAt`. Turn on **Only new jobs** and schedule the Actor daily to get a feed of what appeared.
- **Reliable and polite:** timeouts, retries with backoff on server errors, at most two requests at a time and one per second, a descriptive User-Agent. A bad search is reported in the run summary and the rest still runs.

### Use cases

- **Daily job alerts:** "python in New York, NY", "react in Remote", "sre in Seattle, WA": run every morning with **Only new jobs** on and pipe the result into Slack, e-mail or a sheet.
- **Recruiters and staffing agencies:** watch which employers (and which competing recruiters) post for your specialities, with employment type, Easy Apply and visa-sponsorship flags.
- **Labour-market and salary research:** sample postings per keyword and location, including published pay and workplace type; `searchTotalResults` gives Dice's total count per search for trend lines.
- **Sales intelligence:** hiring is a buying signal; find companies opening "Snowflake", "Kubernetes" or "Salesforce" roles in your region this week.

### How to use it

1. Add searches to **Searches**, one per line, in any of these forms:
   - `python` (keyword, all of the US)
   - `data engineer in Austin, TX` (keyword and city; `devops in Texas` for a state; `python in Remote` works too)
   - `https://www.dice.com/jobs/q-python-l-New_York%2C_NY-jobs` (a results URL without `?...`)
2. Set **Max jobs per search** if you want more than the first 20 (20 per page, one request per page).
3. For daily monitoring, schedule the Actor with **Only new jobs** turned on.
4. Run it. Jobs land in the dataset; a run summary lands in the `OUTPUT` record.

Keywords are matched the way Dice's own URLs work: words are searched as a phrase, and a hyphen counts as a space (`front-end` is searched as `front end`, because Dice reads `q-front-end-jobs` as `front`). Results come in Dice's default (relevance) order. A keyword Dice does not know returns no jobs, which is reported as a successful search with zero results.

#### Input example

```json
{
    "searches": ["python in New York, NY", "data engineer in Austin, TX", "https://www.dice.com/jobs/q-devops-jobs"],
    "onlyNew": false,
    "maxItemsPerSource": 40
}
```

| Field | Default | What it does |
|---|---|---|
| `searches` | 1 sample search | Keyword, `keyword in location`, or a Dice results URL. One request per page read. |
| `onlyNew` | `false` | Outputs only jobs that earlier runs with the same searches have not seen. |
| `maxItemsPerSource` | `20` (one page) | Reads at most N jobs per search, most relevant first; 20 per page, at most 500. |

### Output

One record per job. Fields are stable and documented in `.actor/dataset_schema.json`.

```json
{
    "id": "dice:python/new york, ny:f997b73b-95a2-4b8d-b669-1396fdac00a2",
    "source": "dice",
    "sourceKey": "python/new york, ny",
    "title": "Senior Python Backend Developer (Microservices, FastAPI & GenAI)",
    "url": "https://www.dice.com/job-detail/f997b73b-95a2-4b8d-b669-1396fdac00a2",
    "company": "Rose International",
    "employmentType": "Full-time",
    "employerType": "Recruiter",
    "location": {
        "displayName": "Rutherford, New Jersey, USA",
        "city": "Rutherford",
        "state": "New Jersey",
        "stateCode": "NJ",
        "country": "USA"
    },
    "isRemote": false,
    "workplaceTypes": ["On-Site", "Hybrid"],
    "salary": "USD 80.00 per hour",
    "postedAt": "2026-10-08T00:01:03.000Z",
    "modifiedAt": "2026-10-08T00:01:03.000Z",
    "easyApply": true,
    "willingToSponsor": null,
    "descriptionExcerpt": "Date Posted: 10/07/2026 Hiring Organization: Rose International Position Number: 508796 Industry: Financial Services Job Title: Senior Python Backend Developer (Microservices, FastAPI & GenAI) Job Location: Rutherford, NJ, USA, 07070 Work Model: Hybrid Work Model Details: 3 days onsite and 2 days r…",
    "searchUrl": "https://www.dice.com/jobs/q-python-l-new_york%2C_ny-jobs",
    "searchPage": 1,
    "searchTotalResults": 5192,
    "firstSeenAt": "2026-10-08T06:50:56.000Z",
    "changeStatus": "new",
    "scrapedAt": "2026-10-08T06:50:56.817Z"
}
```

- `employmentType`: Dice's label(s), e.g. `Full-time`, `Contract`, `Third Party`, `Part-time`; several are comma-separated. `employerType` says who posted: `Direct Hire` (the employer), `Recruiter`, or `Other`.
- `location`: as Dice lists it; all fields are `null` when Dice lists none (typically remote jobs). `isRemote` and `workplaceTypes` (`On-Site`, `Remote`, `Hybrid`) come from the employer's own flags.
- `salary`: free text exactly as the employer wrote it (`USD 120,000 - 150,000 per year`, `Depends on Experience`); `null` when none.
- `descriptionExcerpt`: the first 300 characters of the posting as plain text. The full text is never output; follow `url`. Email addresses and common US telephone formats are redacted; summaries with contact, recruiter or hiring-manager labels are omitted.
- `searchPage`: which results page the job was on; `searchTotalResults`: Dice's total for the search, of which only the requested pages are read.

#### Run summary (`OUTPUT` record)

Totals (searches requested, succeeded and failed; jobs output, new, existing, removed since the last run), one entry per search with its status, a `warning` when a later page could not be read, and the list of errors. One bad search never fails the run. A search that Dice refuses (HTTP 403) is reported as such and never retried or worked around.

### Change tracking: how it works

History is kept in a named key-value store, keyed by the set of searches. The first run outputs everything as `new`; later runs mark jobs `new` or `existing`, and the `OUTPUT` record lists IDs that dropped out of the pages read since the last run. With **Only new jobs** on, only `new` records are output. Jobs that disappear are remembered for 90 days, so a job that returns is not reported as new again. Because only the first N jobs of each search are read, "removed" means "no longer among the first N results", not necessarily "closed"; lowering **Max jobs per search** between runs therefore reports the jobs beyond the new limit as removed.

### Pricing

Pay per event: one `job` event per record. Suggested price: $0.002 per job; free to try with the Apify free plan. The owner sets the actual price in the Store; it is never in the code.

What that costs, assuming every search returns a full page of 20 jobs and the price above:

- The default input (one search, one page) outputs 20 jobs: about 4 cents per run.
- Five searches with **Max jobs per search** at 100 (5 pages each) output 500 jobs per run, about $1.00; run daily, about $30 a month (30 runs).
- Five searches, one page each, with **Only new jobs** on: after the first run (100 jobs, about 20 cents) a daily run costs only the newly posted jobs, a few cents on a quiet day, up to 20 cents if every page turned over.

### FAQ

- **How many jobs can one search return?** Up to 500 (25 pages of 20), which is the Actor's request limit. If Dice repeats or clamps a page, paging stops with a warning. The default is one page so a first run costs cents. Narrower searches (add a location, use a more specific keyword) cover more ground than deep paging.
- **Can it filter by full-time, remote, posted date or salary?** Not through Dice's filters, which are query parameters the Actor does not send (only `?page=N` is used). Filter the output on `employmentType`, `isRemote`, `workplaceTypes`, `postedAt` and `salary` instead; the `python in Remote` form works for a remote-only search.
- **Does it fetch the full job description?** No. The results page already carries the opening of each posting; the Actor outputs its first 300 characters together with the link. Job detail pages are not requested.
- **Does it log in or bypass bot protection?** No. Plain requests with a descriptive User-Agent, robots.txt obeyed at run time. If Dice blocks a request, the run reports it and moves on.
- **How fresh is the data?** It is read live during the run, in Dice's default (relevance) order.
- **Why `front-end` found "front" jobs?** It does not: the Actor turns hyphens into spaces before building the URL, because Dice's path format uses the hyphen as its separator. Type `front end` or `front-end`, both search the phrase.

### Legal

This Actor reads only public job postings that employers publish on Dice, sends plain GET requests to pages that Dice's robots.txt allows for every crawler, identifies itself with a descriptive User-Agent, obeys robots.txt at run time, requests nothing robots.txt disallows (no query-style searches, no `/jobsearch/`, no feeds, no APIs), and outputs a contact-screened excerpt of at most 300 characters plus the link. It stores no personal data beyond what the employer wrote into the posting. Dice's Terms and Conditions of Service restrict unapproved automated retrieval, republication and deep links; written permission remains an owner publishing decision; see `LEGAL.md` for the exact wording, dates and the open questions. "Dice" is a trademark of its owner; this Actor is not affiliated with or endorsed by Dice or DHI Group.

# Actor input Schema

## `searches` (type: `array`):

One search per line. Forms: "python", "data engineer in Austin, TX", "devops in Texas", or a results URL such as https://www.dice.com/jobs/q-python-l-New_York%2C_NY-jobs. Each search returns its first 20 jobs unless "Max jobs per search" asks for more. Keep the list short: the default must finish in under 5 minutes.

## `onlyNew` (type: `boolean`):

Output only jobs not seen in previous runs with the same searches. The first run outputs everything; later runs only what is new. Every record carries changeStatus ('new' or 'existing') and firstSeenAt either way.

## `maxItemsPerSource` (type: `integer`):

How many jobs to read per search, most relevant first: 20 per page, one request per page, at most 500 (25 pages, the Actor's request limit). Default 20 (one page). More pages mean more requests and a longer run.

## Actor input object example

```json
{
  "searches": [
    "python in New York, NY"
  ],
  "onlyNew": false,
  "maxItemsPerSource": 20
}
```

# Actor output Schema

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

The jobs found in this run, one per record (the default dataset).

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

Totals, per-search status and any errors for this run (the OUTPUT record).

# 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 = {
    "searches": [
        "python in New York, NY"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataforge-tools/dice-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 = { "searches": ["python in New York, NY"] }

# Run the Actor and wait for it to finish
run = client.actor("dataforge-tools/dice-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 '{
  "searches": [
    "python in New York, NY"
  ]
}' |
apify call dataforge-tools/dice-jobs --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataforge-tools/dice-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/nbpb3uocaXGa6D3LD/builds/JYs5He9wFZSz2NQXM/openapi.json
