# Dice Jobs Scraper (US tech jobs) (`hexagone-data/dice-jobs-scraper`) Actor

Scrape Dice, the US tech job board, by posted date: above the site's 10,000-row limit a search is split automatically and what stays out of reach is counted. Each job with its full job page (description, skills, contract, apply link). Pay per job.

- **URL**: https://apify.com/hexagone-data/dice-jobs-scraper.md
- **Developed by:** [Hexagone Data](https://apify.com/hexagone-data) (community)
- **Categories:** Jobs, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 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.
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?

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 jobs)

Scrape **Dice**, the US tech job board. Type a search term or paste the URL your browser shows, filters included, and get one record per job: title, company,
location, salary, employment and employer type, remote and Easy Apply flags, and the job page details (full description, skills, contract type and term,
postal code, how to apply).

What it does, measured on the site on 2026-11-06:

- **Above the site's 10,000-row limit: split automatically, and what stays out of reach is counted.** The search page shows at most 25 pages of 30 jobs
  (750); the site's search service stops at 10,000 rows per search. Above 10,000, the Actor splits the search by the site's own filters (employer type, Easy
  Apply, visa sponsorship, work from home, remote, then US state) and puts it back together, each job delivered once. What no split can reach is counted in the
  run summary (status `partial_collection`, number `missing`), never hidden. Measured on 2026-11-06: `java` 12,251 of 12,251 jobs (`done`); `python`
  29,993 of 30,233 (`partial_collection`, 240 missing, counted). Measured dry on `python`: 30,235 jobs announced, 58 slices, none above 10,000; jobs with no
  state (location "US" only, or none) cannot be reached by the state slices: 473 of the 19,006 jobs of the deepest slice. The Actor also reads that slice
  itself, newest first, up to 10,000 rows. The whole site (113,369 jobs): about 97% reachable, estimated from a dry split: California alone holds 11,290 jobs
  in a slice no filter can split further, and about 2,900 jobs (2.6%) are out of reach; a run on it ends in `partial_collection` with the numbers.
- **By posted date, nothing lost between pages.** The site's default order (relevance) lets jobs with the same score swap places from one read to the next:
  reading `kubernetes` in Texas page after page, 100 jobs at a time, gave 2,196 then 2,197 distinct jobs out of 2,198. The Actor always reads by posted date,
  1,000 rows at a time: 2,198 of 2,198.
- **The same search as the site's page.** For each search, the Actor opens the search page once and sends the site's search service the exact parameters
  that page sends (location, coordinates, radius, every filter), so any URL the site understands is read the way the site reads it.
- **The job page is included, at the same price.** Full description (text and HTML), every skill (the page folds them behind "Show more"), contract type
  (W2, corp-to-corp, independent) and term, postal code, Position Id, and the apply link or e-mail the page serves. On 51 job pages read: skills 50, apply link 32,
  apply e-mail 19, postal code 39, contract type 14.
- **Salary as numbers only when the text is unambiguous.** `salaryMin`, `salaryMax`, `salaryCurrency` and `salaryPeriod` are filled only when the salary text
  states both the currency and the period ("USD 197,300.00 - 225,100.00 per year", "$45 - $55 hourly"): 1,307 of 3,332 rows measured (39%). Otherwise they are
  `null`, and the salary text is always delivered as the site wrote it ("Depends on Experience", "120000 - 150000").
- **The run tells you what it got.** Distinct jobs against the total announced at the start and read again at the end, with both numbers if anything is missing.

### Try it with 20 jobs

1. Keep the prefilled search term (`python developer`) and **Maximum number of jobs = 20**.
2. Click **Start**. You pay for 20 jobs: $0.022 (job pages included, start fee included).
3. Open **Jobs overview** for the table, **Run summary** for what was collected and why anything is missing.

### What to paste

Type words in **Search terms** and use the filters of the form (location, radius, posted within, employment types, workplace types, employer types, Easy
Apply only, willing to sponsor only), or paste into **Start URLs**:

| You paste | Example | What you get |
|---|---|---|
| A search | `https://www.dice.com/jobs?q=python&location=Austin%2C+TX%2C+USA` | the search read to the end (above 10,000 jobs: split, what is out of reach counted) |
| A search with filters | `https://www.dice.com/jobs?q=python&filters.employmentType=CONTRACTS&filters.workplaceTypes=Remote&radius=50` | the same filters as the site |
| The URL copied after clicking a job on the list | `https://www.dice.com/jobs?q=python&location=Austin%2C+TX%2C+USA&latitude=30.267153&longitude=-97.7430608&countryCode=US&locationPrecision=City&adminDistrictCode=TX&selectedJobId=8863ad7f-...` | the search it shows, not only that job |
| The site's other search pages | `https://www.dice.com/jobs/q-python-jobs`, `/jobs/l-texas-jobs`, `/jobs/q-python-l-austin%2c+tx-jobs`, `https://www.dice.com/jobs/l-mclean%2c+va%2c+us-jobs?filters.clientBrandNameFilter=Capital+One` | read as the site reads them |
| The site itself | `https://www.dice.com/jobs` exactly, with **Maximum number of jobs = 0** | about 97% of the 113,369 jobs measured on 2026-11-06, the rest counted (`partial_collection`) |
| A single job (`https://www.dice.com/job-detail/...`, or an old `https://www.dice.com/jobs/detail/...`) | | **not supported yet**: the URL is rejected with this reason; paste a search URL |

`page` and `pageSize` in a URL are replaced (the search is read 1,000 rows at a time) and named in the run summary (`droppedParams`), like any other
parameter the site's own page does not use (`selectedJobId`, `utm_source`...): they have no effect. A filter of the form that the site's page does not send
to its search is reported (`OPTION_WITHOUT_EFFECT`), never silently dropped. Any other address (another site, the home page, a company page, any other
path under `/jobs/`) is **rejected with the reason**; the other addresses of the run are still read. The site answers unknown paths under `/jobs/` with a
search of the whole site, so a URL that asks for a search (words, a place, a filter, a `/jobs/...` path) but whose page sends an empty search to the site is
rejected after that page ("this URL would read the whole site").

**A location the site does not understand filters nothing, and the Actor checks it.** The site keeps any location text, even one it does not use.
Measured on 2026-11-06: `Anywhere`, `Work from home` or a typo gave 113,044 jobs (the whole site); `Remote, USA` gave 10,914, a real filter (the same
as `filters.workplaceTypes=Remote`). So for each search with a location, the Actor makes
two control counts, one after the other: the search as the site's page sends it, then the same search without the location (one row each, before any
job is delivered, never charged). The site's totals move by a few jobs from one request to the next (six identical counts of the whole site varied by 10;
the page and the search API differed by up to 35 out of 112,863), so the two counts are taken as the same when they differ by at most 3 jobs or 0.2% of
the larger one: the location filtered nothing. Then a URL with no words and no filter is **rejected** ("this URL would read the whole site: Dice did not
use the location ..."); with words or a filter, the search is read and the run summary warns (`LOCATION_NO_EFFECT`), giving both counts. If a control
count fails, the search is still read, with a warning (`LOCATION_NOT_CHECKED`). On the searches we measured, a real location removed thousands of jobs (`Texas`: 21,159 against 112,851). Limits: a
location the site reads as another place is a real filter for the check and nothing is said (`Remote US` gave 110,376 jobs, as many as `United States`,
2.4% less than the whole site: the site reads it as the US, not as remote jobs); a small search whose jobs all lie in the location you gave, or where it
removes 3 jobs or fewer, also gets the same two counts: the warning can then be wrong, but a search with words or a filter is never rejected for it.
**A search with a location also includes remote jobs, as on the site**: the site's page sends `includeRemote=true` with any location (measured:
`data engineer` + `Phoenix`, 441 remote jobs out of 467). **"Remote" is not a place on Dice**: as a
location it filters nothing (`q=python` 30,157 jobs, `q=python&location=Remote` 30,157, `q=python&filters.workplaceTypes=Remote` 3,080). For remote jobs,
use **Workplace types = Remote** (`filters.workplaceTypes=Remote`); "Remote" or "remote only" typed in the form's Location, and "Remote US", "Remote USA",
"Remote - US" or "Remote (US)" (read by the site as the US), are turned into Workplace types = Remote (the workplace types you chose are kept), and the
run summary says so (`LOCATION_REMOTE_AS_WORKPLACE`); if Remote is already among them, the location is sent as typed and checked like any other. In a
pasted URL, the location is read as the site reads it. An empty form is refused.

### Output

One record per job and run (`jobId` is the key: a job found by two of your searches is delivered once). Unknown values are `null`, never `""`, `0` or `false`: an empty
or blank text served by the site is delivered as `null`. One exception: `isFeatured` is `false` when the site does not mark the job (the site leaves the field
out for jobs it does not feature).
Every field is listed in the dataset schema. A real record, built from the site's own data captured on 2026-11-06, **abridged** (texts and skills shortened):

```json
{
  "jobId": "02b3cfd1-af9d-42f0-be19-0165a0c93840",
  "legacyJobId": "08beeaf16ba51f3df617bec0d2d1cbda",
  "url": "https://www.dice.com/job-detail/02b3cfd1-af9d-42f0-be19-0165a0c93840",
  "title": "Line Support Technician Plant Floor Desktop Support (3rd Shift)",
  "companyName": "Calance",
  "companyProfileId": "736fcdd4-39f2-5eef-b3fd-23f427d39cfd",
  "companyProfileUrl": "https://www.dice.com/company-profile/736fcdd4-39f2-5eef-b3fd-23f427d39cfd",
  "diceId": "cxbcsi",
  "location": "Anna, Ohio, USA",
  "city": "Anna",
  "state": "Ohio",
  "stateCode": "OH",
  "country": "USA",
  "postedDate": "2026-11-06T01:01:44Z",
  "salary": "USD 45,302.00 per year",
  "salaryMin": 45302,
  "salaryMax": 45302,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "employmentType": "Contract",
  "employmentTypes": ["Contract"],
  "employerType": "Direct Hire",
  "workplaceTypes": ["On-Site", "Hybrid"],
  "isRemote": false,
  "workFromHome": true,
  "easyApply": false,
  "willingToSponsor": false,
  "isFeatured": false,
  "externalApplyUrl": null,
  "summary": "CALL US NOW for immediate consideration!  ** We will NOT accept 3rd Party (C2C) Contractor ...",
  "detailsStatus": "ok",
  "description": "CALL US NOW for immediate consideration!\n===\n** We will NOT accept 3rd Party (C2C) Contractors **\n...",
  "skills": ["Remote Support", "Project Management", "Preventive Maintenance", "Performance Management", "Network Support", "React.js"],
  "postalCode": "43040",
  "validThrough": "2026-12-07T01:01:44.000Z",
  "positionId": "Job45206",
  "applyType": "APPLY_TO_URL",
  "applyUrl": "https://www.calancejobs.com/bcsijobs.nsf/o/45206?OpenDocument&s=DiceAd",
  "applyEmail": null,
  "contractTypes": ["CONTRACT_W2"],
  "contractTerm": "6 Months",
  "searchUrl": "https://www.dice.com/jobs?q=contract",
  "scrapedAt": "2026-11-06T08:31:57Z"
}
```

`detailsStatus`: `ok`; `expired` (the job was removed between the list and its page: list data delivered, page fields `null`); `not_found`; `failed` (the
job page could not be read after 3 tries, the job stays delivered from the list); `skipped` (job pages stopped for this run after repeated refusals);
`null` when **Include the job page details** is off. The job page is always read at `https://www.dice.com/job-detail/<jobId>`, even when the card links to
a `/direct-apply/` page (about 5% of jobs), which leaves Dice.

### Run summary

Each run writes a readable **Run summary** (and the same as JSON in `OUTPUT`), per search: `status`, `jobs`, `jobsDistinct` (distinct jobs seen for this
search; when the run stopped at your `maxItems` or maximum charge: distinct jobs delivered for it), `totalAnnounced`, `totalAtEnd` (read again after
the last page), `missing` (null when the run stopped at your `maxItems` or maximum charge: jobs not read are not lost), `totalOnSearchPage`, `slices`, `slicesOver10000`, `slicesReread`,
`slicesStillMoving`, `passes`, `droppedParams`, `orderNote` (the order of the records, see Known limits), `detail`. For the run: `rejectedUrls`,
`detailsRead`, `detailsFailed`, `detailsExpired`, `detailsNotFound`, `detailsStopped`, `accessRefusals`, `serverErrors`, `accessLevel`, `anomalies`.

| Status | Meaning |
|---|---|
| `done` | every distinct job counted against the total read at the end; at most 2 jobs or 0.1% missing, shown with its number (warning `MISSING_WITHIN_TOLERANCE`) |
| `partial_collection` | something is missing: both numbers and the cause; warning `PARTIAL_COLLECTION` |
| `stopped_at_limit` | your `maxItems` or maximum charge was reached: `jobsDistinct` = jobs delivered, `totalAnnounced` kept, `missing` null |
| `no_results` | the site announced 0 jobs: the search is empty, the URL is wrong or the site is down (`NO_RESULTS` warning) |
| `no_new_jobs` | the jobs of this search were all delivered by an earlier search |
| `blocked_by_site`, `rate_limited`, `network_error`, `http_error`, `not_found`, `site_maintenance` | the site refused or did not answer even after new sessions: never reported as an empty search |
| `unrecognized_page`, `structure_changed` | the answer is not a search the Actor can read; never reported as an empty search |
| `not_processed`, `interrupted`, `internal_error` | the search was not reached or finished (limit reached before it, run stopped, unexpected error) |

If jobs are missing after the last page, the first page of each slice is read again, and the slices that still lack jobs are read to the end; at most 3
readings per slice in all, passes included. Only what was missing is added, nothing is delivered or charged twice. A job delivered without its job page is
counted in `detailsFailed` or `detailsStopped` and in the warning `DETAILS_FAILED`. A run where no search delivers anything **fails** with the exact cause.
Warnings are listed under **Checks**; "Checks passed" is shown only when there is none. Codes starting with `ANO_` come from the shared run checks.

### Pricing

Pay per event: **$0.85 per 1,000 jobs**, **the job page included**: the same price with or without **Include the job page details**.
Plus a **$0.005 start fee per run** (charged by Apify once per GB of memory; the default is 1 GB).
Apify subscribers pay less automatically, on every event: 10% off on the Bronze plan, 20% on Silver, 30% on Gold and above.

| Jobs | With job pages, start fee included | Without job pages, start fee included |
|---|---|---|
| 20 | $0.022 | $0.022 |
| 1,000 | $0.855 | $0.855 |

Only jobs written to your dataset are paid, each once, even if two of your searches find it or the list is read twice. Your maximum charge per run is
respected: the run stops cleanly before the job that would exceed it, and no job page is read for a job that could not be paid.

### Known limits

- **Order: newest first within each search (and within each slice when a search is split above 10,000); jobs the site features come first on the first
  page.** There is no order across slices or searches. With a small `maxItems` (10,000 or less) a search is not split: you get its most recently posted
  jobs, after the 0 to 3 jobs the site features (they can be older). A `sortBy` in your URL is ignored (`orderNote` says so), because the site's own order
  loses jobs between pages.
- **A single job URL is not accepted yet** (`/job-detail/...`): paste a search URL.
- **"Remote" is not a location on Dice, nor is any text the site does not understand** ("Anywhere", "Work from home", a typo: 113,044 jobs, the
  whole site). Each location is checked with two counts, with and without it: if they differ by at most 3 jobs or 0.2% (the site's totals move by a few
  jobs between requests), the URL is rejected if it has no words and no filter, read with a warning (`LOCATION_NO_EFFECT`) otherwise. "Remote US" is
  read by the site as the United States (110,376 jobs, like `United States`): in a pasted URL it is a real filter for the check, so nothing is said;
  typed in the form's Location, it is turned into Workplace types = Remote. A small search whose jobs are all in that location (or where it removes 3
  jobs or fewer) gets the same counts too: the warning may then be wrong. Remote jobs are asked with Workplace types = Remote
  (`filters.workplaceTypes=Remote`: 3,080 for `python`, against 30,157 with or without `location=Remote`).
- **A location also brings remote jobs**, as on the site (`includeRemote=true` is sent by the site's page): `data engineer` + `Phoenix` gave 441
  remote jobs out of 467.
- **Expired jobs still listed by the site.** In our 11 test searches read to the end with job pages, 0% to 1% of the jobs the search shows had a job page answering "no longer
  available" (HTTP 410), some posted months ago: they are delivered from the list and charged like the others, with `detailsStatus: "expired"`.
- **Platform migrations** (Apify moving the run to another server) are resumed from the saved state; jobs found in your dataset are
  not written again. If one hits while a batch of jobs is being written, the resumed run reads the dataset back for 3 minutes: jobs already there
  are kept, and only the jobs still missing after that are written, once. If the Apify storage itself fails and the run has to stop, the run ends
  with "PARTIAL RESULT", says how many jobs you received, and tells you to **resurrect the run** (run page > Actions > Resurrect) to
  get the rest without paying again for the jobs already delivered; a new run with the same input starts from zero.
- **No number of applicants**: the site does not show it without an account.
- **No e-mails searched outside Dice**: `applyEmail` is only the apply e-mail the job page itself serves.
- **No incremental mode**: each run reads what you ask, from the newest down, and has no memory of earlier runs.
- **Jobs out of reach in very large searches.** Above 10,000 jobs, the last split is by US state; jobs listed with no state are read only among the newest
  10,000 of their slice (`python`: 240 missing of 30,233). A slice still above 10,000 with no filter left gives its newest 10,000 rows (on the whole site:
  California, 11,290). Any job still missing is counted in the run summary (`partial_collection` with the numbers), never reported as `done`.
- New jobs keep coming during a long run: a few may be missing at the end (counted). A job whose page could not be read is still delivered and charged:
  its list data is complete, `detailsStatus` tells which. After 10 refused job pages in a row, the run pauses and tries one; after 3 refused tries, job pages
  stop for the run (`detailsStopped`).
- No proxy is used by default: no refusal was measured from a direct connection, Apify's datacenter proxy or Apify's residential proxy (3 tries each). If the
  site refuses (HTTP 403 or 429, or a challenge page instead of the answer), the run retries with new sessions, then falls back to Apify's datacenter proxy
  (US), then, for the search list only (1,000 jobs per request), to Apify's residential proxy (US). Job pages never go through the residential proxy: if they
  are refused through the datacenter proxy too, job pages stop for the run (`detailsStopped`, warning `DETAILS_STOPPED`) and the list goes on. A server error
  of the site (HTTP 500, 502, 503, 504) is not a refusal: it is retried after a pause and counted (`serverErrors`); a page still failing is counted as failed;
  it never changes the proxy. Your price per job is the same whatever the proxy. This fallback has been tested offline only. The search service needs the key that the site's own page serves: the Actor reads it from the page at the start of the run and
  again if it is refused; if the page no longer serves it, the run stops with that reason.

### Support

Questions, a URL that does not work, a field you need: open an issue on the Actor's **Issues** tab with the run link.

This Actor is not affiliated with, endorsed or sponsored by Dice or DHI Group, Inc. The name is used only to describe the website the data comes from. Only
publicly visible job ads are collected; ad texts and apply e-mails can contain personal data such as a recruiter's name: use the data in line with the
site's terms and the privacy laws that apply to you.

# Actor input Schema

## `searchTerms` (type: `array`):

One search per term, as typed in the site's 'Job title, skill, company, keyword' box (e.g. python developer, java, nurse). The filters below apply to these searches.

## `startUrls` (type: `array`):

Dice search URLs exactly as your browser shows them, filters included: https://www.dice.com/jobs?q=python\&location=Austin%2C+TX%2C+USA, the URL copied after clicking a job on the list (the search is read, not only that job), or https://www.dice.com/jobs/q-python-jobs. A URL whose page would turn it into a search of the whole site is rejected. A location the site does not understand ("Anywhere", a typo) filters nothing: each location is checked with two counts, the search with it and the same search without it (never charged); if they differ by at most 3 jobs or 0.2% (the site's totals move by a few jobs between requests), the URL is rejected if it has no words and no filter, read with a warning (LOCATION_NO_EFFECT) otherwise (the warning can be wrong for a small search whose jobs are all in that location). A search with a location also includes remote jobs, as on the site. Read in addition to the search terms; a URL keeps its own filters. A single job URL (/job-detail/..., or an old /jobs/detail/...) is not supported yet. A remote list of URLs is not supported.

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

Stops at exactly this number across all searches. Order: newest first within each search (and within each slice when a search is split above 10,000); jobs the site features come first on the first page. 0 = no limit.

## `includeDetails` (type: `boolean`):

Reads each job's own page: full description, skills, postal code, contract types (W2, C2C, independent) and term, apply link or e-mail the page serves. Same price per job with or without; turn it off only to go faster.

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

As in the site's location box: a city (Austin, TX, USA) or a state (Texas). Empty = all of the US. For remote jobs, use Workplace types = Remote: "Remote" as a location filters nothing on Dice (typed here, "Remote", "remote only", "Remote US", "Remote USA", "Remote - US" or "Remote (US)" is turned into Workplace types = Remote, your other workplace types kept, and said in the run summary). Any location is checked with two counts, the search with it and without it (never charged): if they differ by at most 3 jobs or 0.2% (the site's totals move by a few jobs between requests), the location filtered nothing and the run summary warns (LOCATION_NO_EFFECT). A location also includes remote jobs, as on the site.

## `radiusMiles` (type: `string`):

Around the location, as on the site. Empty = the site's default.

## `postedWithin` (type: `string`):

Empty = any date.

## `employmentTypes` (type: `array`):

Several values = jobs of any of them, as on the site. Empty = all.

## `workplaceTypes` (type: `array`):

Several values = jobs of any of them. Empty = all.

## `employerTypes` (type: `array`):

Several values = jobs of any of them. Empty = all.

## `easyApplyOnly` (type: `boolean`):

Only jobs with the site's Easy Apply.

## `willingToSponsorOnly` (type: `boolean`):

Only jobs whose employer is willing to sponsor a work visa.

## `proxyConfiguration` (type: `object`):

Optional. By default no proxy is used (no refusal measured); if the site refuses (HTTP 403 or 429, challenge page), the run retries, then falls back to Apify's datacenter proxy (US), then to Apify's residential proxy (US) for the search list only: job pages never use the residential proxy. Server errors (HTTP 5xx) are retried, never taken as a refusal. Set a proxy here to use it from the first request; turning the proxy off is respected.

## Actor input object example

```json
{
  "searchTerms": [
    "python developer"
  ],
  "maxItems": 20,
  "includeDetails": true,
  "easyApplyOnly": false,
  "willingToSponsorOnly": false
}
```

# Actor output Schema

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

All job records, one per job, with every field.

## `jobsOverview` (type: `string`):

Title, company, location, salary, employment type, workplace, posted date, link.

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

Readable per-search summary: status, distinct jobs against the total announced at the start and at the end, slices, reason for anything missing.

## `summaryJson` (type: `string`):

The same summary as JSON (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 = {
    "searchTerms": [
        "python developer"
    ],
    "maxItems": 20,
    "includeDetails": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("hexagone-data/dice-jobs-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 = {
    "searchTerms": ["python developer"],
    "maxItems": 20,
    "includeDetails": True,
}

# Run the Actor and wait for it to finish
run = client.actor("hexagone-data/dice-jobs-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 '{
  "searchTerms": [
    "python developer"
  ],
  "maxItems": 20,
  "includeDetails": true
}' |
apify call hexagone-data/dice-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,hexagone-data/dice-jobs-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/26Bw3JLpBldwVIWxO/builds/Wwugcf6wdRs5uO8ir/openapi.json
