# Drushim.co.il \[$1/1k] Job Scraper (`memo23/drushim-scraper`) Actor

\[$1/1k] Scrape Drushim.co.il — Israel's leading job board — by keyword (Hebrew or English), category, region or any pasted URL. Full descriptions + requirements, salary where published, skill tags, geo-coded cities, direct apply links. Incremental monitoring built in. $1 per 1,000 jobs.

- **URL**: https://apify.com/memo23/drushim-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Jobs
- **Stats:** 17 total users, 16 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 job results

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/platform/actors/running/actors-in-store#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

## Drushim.co.il Job Scraper — Israeli Jobs, Full Descriptions & Apply Links

Turn [drushim.co.il](https://www.drushim.co.il) — Israel's leading job board — into structured hiring data. Search any keyword in Hebrew or English (or paste any drushim URL) and get the **complete job payload in a single call**: full description + requirements, salary where published, skill tags, geo-coded cities, experience level, company profile and the direct apply link. Powered by drushim's own search API — no HTML parsing fragility, no browser.

**$1 per 1,000 jobs** ($0.005 per run + $0.001 per result), with incremental monitoring that only bills you for jobs that are new or changed.

![How Drushim.co.il Job Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-drushim.png)

### Why this actor

- **Single-call architecture.** Every search hit already contains the full description, requirements, salary fields, skills and company data — there is no second "detail fetch" to fail, and runs are fast.
- **Hebrew-first, English-friendly.** Search in either language; rows keep the Hebrew source values verbatim and add English where drushim provides it (city names, zone names).
- **Skill tags built in.** Each job carries drushim's own skill sub-categories (`skills`: C#, NodeJS, React…) — filter-ready, no NLP needed.
- **Geo-coded.** Every work location comes with city (Hebrew + English), city id and latitude/longitude.
- **Incremental monitoring.** Run on a schedule and only receive (and pay for) NEW / UPDATED / REAPPEARED jobs, with expired-job tracking and repost detection.
- **Notifications without glue.** Telegram, Slack, Discord, WhatsApp Cloud API, or a generic JSON webhook after each run.
- **Israeli residential network.** drushim.co.il rejects foreign IPs; this actor routes through Israeli residential IPs with rate-limit-aware retries out of the box.

### Use cases

| Who | What |
|---|---|
| Recruiters & sourcers | Fresh Israeli postings by keyword/category with direct apply links and employer names |
| Job-market analysts | Demand by skill tag, region, experience level and scope across the Israeli market |
| Lead-gen teams | Companies actively hiring in Israel — with opt-in contact-email discovery |
| Job boards & aggregators | Structured Israeli feed with stable ids, change tracking and expired-job signals |
| AI agents / LLM apps | Compact mode + description truncation for token-efficient pipelines |

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `query` | array | `["מפתח"]` | Each keyword runs as its own search; Hebrew works best |
| `category` | integer | — | Drushim `catdir` code, e.g. `6` = software/hi-tech; works with no keyword (whole-category browse) |
| `area` | array | — | Zone codes as in `/jobs/area/1-2-3/` URLs (e.g. `1` = Center) |
| `experience` / `scope` | array | — | The site's filter codes, passed through |
| `cvOptional` | boolean | `false` | Only jobs that accept applications without a CV |
| `startUrls` | array | — | Any drushim URL: search pages with filters, `/jobs/cat{N}/`, `/jobs/area/…`, raw `/api/jobs/search?…`, direct `/job/{code}/{hash}/` |
| `incrementalMode` | boolean | `false` | Only emit NEW / UPDATED / REAPPEARED vs the previous run (`stateKey` names the baseline) |
| `includeKeywords` / `excludeKeywords` | array | — | Keep/drop by title, company, description or requirements — dropped rows are never charged |
| `fromDate` / `toDate` / `maxAgeMinutes` | — | — | Date gates on the posted date |
| `compact` / `excludeEmptyFields` / `descriptionMaxLength` | — | — | Output shaping for AI/MCP pipelines |
| `telegramBotToken` + `telegramChatId`, `slackWebhookUrl`, `discordWebhookUrl`, `whatsapp*`, `webhookUrl` | — | — | Per-run notifications |
| `enrichEmails` | boolean | `false` | Opt-in employer contact-email discovery (billed only per email found) |
| `maxItems` | integer | `1000` | Hard cap across all searches |

#### Example input

```json
{
    "query": ["מפתח", "QA"],
    "maxItems": 200
}
```

Daily new-jobs feed (schedule it; only new/changed postings after the first baseline run):

```json
{
    "query": ["data engineer"],
    "incrementalMode": true,
    "stateKey": "data-eng-il",
    "notifyOnlyChanges": true,
    "telegramBotToken": "…",
    "telegramChatId": "…"
}
```

Whole-category browse (software/hi-tech), Center region only:

```json
{
    "category": 6,
    "area": [1],
    "maxItems": 500
}
```

### Output schema

One row per job. Hebrew source values are kept verbatim; unavailable data points are `null`, never omitted (unless `excludeEmptyFields` is on).

#### Example job record (trimmed real row)

```json
{
    "jobId": "37833886",
    "jobHash": "6fce3f3a",
    "url": "https://www.drushim.co.il/job/37833886/6fce3f3a/",
    "title": "מפתח/ת Full stack",
    "companyName": "עוז תוכנה בע\"מ",
    "companyId": "2284810",
    "companyLogoUrl": "https://webapi.drushim.co.il/logos/2284810/page/…png",
    "descriptionText": "דרוש/ה מפתח/ת Full Stack בכיר/ה – לחברה ביטחונית…",
    "requirementsText": "6+ שנות ניסיון בפיתוח צד שרת\n3+ שנות ניסיון ב־C# / .NET…",
    "salaryText": null,
    "scope": ["משרה מלאה"],
    "experienceText": "5-6 שנים",
    "categories": ["הייטק-תוכנה"],
    "skills": ["C#", "NodeJS", "React"],
    "regions": ["תל אביב", "רמת גן / גבעתיים", "חולון / בת ים"],
    "addresses": [{ "city": "יהוד", "cityEnglish": "Yehud", "cityId": 488, "latitude": 32.030339, "longitude": 34.8870985 }],
    "postedDate": "2026-07-22T13:13:21.827",
    "postedAgo": "לפני 16 שעות",
    "isExpired": false,
    "allowNoCV": false,
    "applyUrl": "https://www.drushim.co.il/sendcv.aspx?jobcode=37833886",
    "applyExternalUrl": null,
    "searchQuery": "search:מפתח",
    "scrapedAt": "2026-08-17T09:45:12.338Z"
}
```

#### Key output fields

| Group | Fields |
|---|---|
| Identity | `jobId`, `jobHash`, `url`, `employerJobCode` |
| Role | `title`, `fullTitle`, `descriptionHtml/Text`, `requirementsHtml/Text`, `scope`, `experienceText/Code` |
| Salary | `salaryText` (where the employer publishes it), `salaryValue` |
| Classification | `categories`, `skills` (drushim's own sub-category tags) |
| Employer | `companyName`, `companyId`, `companyUrl`, `companyLogoUrl` |
| Location | `regions`, `zones` (code + Hebrew + English), `addresses` (city, cityEnglish, lat/lng) |
| Dates & flags | `postedDate`, `bumpedDate`, `postedAgo`, `isExpired`, `preferred`, `topJob`, `hotJob`, `allowNoCV` |
| Apply | `applyUrl`, `applyExternalUrl` (employer's own ATS when the posting uses one) |
| Monitoring | `changeType` + `firstSeenAt` (incremental mode) |
| Run context | `searchQuery`, `scrapedAt` |

### How it works

1. Your keywords and filters become drushim searches (pasted URLs are auto-classified: search / category / zone / raw API / direct job).
2. Each search calls drushim's own search JSON API page by page (~10 jobs per page) through Israeli residential IPs, with rate-limit-aware retries.
3. Every API hit already carries the complete job payload — description, requirements, salary, skills, company, geo — so each job costs exactly one API row, no detail fetches.
4. Optional gates run before anything is charged: keyword/date filters, then incremental diffing against your previous run.
5. The mapped row is pushed to the dataset — one row per job, JSON/CSV/Excel export — and optional notifications fire at the end of the run.

### What makes this different from other Drushim actors

- **True single-call scraping.** API-based rows, not HTML scraping of a JavaScript SPA — faster and far less brittle when the site tweaks its markup.
- **Skill tags + geo coordinates.** Drushim's own structured sub-categories and per-city lat/lng come through on every row.
- **Change tracking that saves money.** Incremental mode emits only NEW/UPDATED/REAPPEARED (with expired sweeps and repost detection) — a daily monitor costs cents, not dollars.
- **Real enrichment, optional.** Contact-email discovery looks up the employer from public sources — not a regex over the posting text — and bills only per email actually found.

### Notes & limitations

- **Salary coverage follows the source.** Most Israeli employers don't publish salary; where drushim has it, you get `salaryText`/`salaryValue`.
- `applyUrl` is drushim's apply endpoint for the job; `applyExternalUrl` is set when the employer routes applications to their own site.
- Hebrew is the site's first language — English keywords work, but Hebrew terms match more postings.
- Very old `/job/…` URLs may return HTTP 410 (the posting expired and was removed) — reported in the log, never charged.
- Employer websites are not exposed by drushim, so email enrichment uses name-based discovery (best-effort).

### FAQ

**How fresh are the postings?**
Each row carries `postedDate` (first posted), `bumpedDate` (last promoted) and the site's own `postedAgo` string. Pair `incrementalMode` with a schedule for a since-last-run feed.

**Can I scrape a whole category?**
Yes — set `category` (e.g. `6` for software) with no keyword, or paste the category URL (`https://www.drushim.co.il/jobs/cat6/`) into `startUrls`.

**How do I filter by city rather than zone?**
Use `includeKeywords` with the city name (matches the job's location fields), or paste a drushim search URL that already carries the site's own geo filter.

**Does incremental mode charge for unchanged jobs?**
No. Unchanged jobs are suppressed before they reach the dataset, so they are never billed. Set `emitUnchanged: true` if you want them anyway.

**Can it notify me only about new jobs?**
Yes — `incrementalMode: true` + `notifyOnlyChanges: true` + any notification channel.

### Support

- **Bugs / feature requests** — open an issue on the actor's Issues tab
- **Custom exports / tailored fields** — drop a note via the Apify Store contact form
- **Other actors** — see the [memo23 profile](https://apify.com/memo23) for the rest of the catalog

### 🔎 Explore more scrapers

Same engine, other job boards:

- [Naukrigulf Job Scraper](https://apify.com/memo23/naukrigulf-jobs-scraper) — UAE / Saudi / Gulf
- [Eluta.ca Jobs Scraper](https://apify.com/memo23/eluta-scraper) — Canada
- [TotalJobs Scraper](https://apify.com/memo23/totaljobs-scraper) — UK all sectors
- [StepStone Job Scraper](https://apify.com/memo23/stepstone-search-cheerio-ppr) — Germany, Austria, France, Denmark
- [Bumeran Job Scraper](https://apify.com/memo23/bumeran-job-scraper) — Argentina & LATAM

…plus 100+ more on the [memo23 profile](https://apify.com/memo23).

### 🤖 For AI Agents & LLM Apps

This actor is MCP-friendly by design: set `compact: true` for core-fields-only rows, cap prompt cost with `descriptionMaxLength`, drop nulls with `excludeEmptyFields`, and use `incrementalMode` so an agent polling on a schedule only sees deltas. The output schema is declared on the actor, so agents can discover the dataset shape programmatically.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is **not affiliated with, endorsed by, or sponsored by** Drushim.co.il, Drushim HR Group Ltd., or any of their subsidiaries or affiliates. All trademarks mentioned are the property of their respective owners.

The scraper accesses only **publicly visible** job postings served by drushim.co.il — no login, no CAPTCHA solving, no authenticated endpoints. When the site rate-limits, the actor backs off and retries politely rather than attempting to overwhelm it.

Users are responsible for:

- Complying with Drushim.co.il's Terms of Service
- Following Israel's Privacy Protection Law and your jurisdiction's data-protection laws (GDPR, CCPA, etc.) when storing, processing, or republishing scraped postings
- Not republishing scraped data in a way that competes commercially with Drushim.co.il

***

### SEO Keywords

drushim scraper, scrape drushim.co.il, drushim api, israel job scraper, israeli jobs api, israel job listings data, tel aviv jobs scraper, hebrew job board scraper, דרושים scraper, israel hiring data, israel job market analytics, jobs israel json, israeli tech jobs data, job monitoring israel, drushim jobs export, israel recruitment data, job feed israel, alljobs alternative, jobmaster alternative

# Actor input Schema

## `query` (type: `array`):

Job search keywords, Hebrew or English — each entry runs as its own drushim search (e.g. `מפתח`, `מנהל מוצר`, `QA`).

## `category` (type: `integer`):

Drushim category (`catdir`) code, e.g. `6` = הייטק-תוכנה (software / hi-tech). Find the code in any category URL: drushim.co.il/jobs/cat6/ → 6. Combined with every keyword; also works with no keyword at all (full-category browse).

## `area` (type: `array`):

Zone codes (`area`) to filter by, e.g. `1` = מרכז (Center), as seen in drushim.co.il/jobs/area/1-2-3/ URLs. Leave empty for all of Israel.

## `experience` (type: `array`):

Experience filter codes (`experience`) as used by the site's filter URLs (e.g. `4` = 5-6 שנים). Leave empty for any experience.

## `scope` (type: `array`):

Scope filter codes (`scope`) — e.g. `1` = משרה מלאה (full-time). Leave empty for all scopes.

## `cvOptional` (type: `boolean`):

Limit to postings that accept applications without an uploaded CV (the site's משרות ללא קורות חיים filter).

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

Full drushim.co.il URLs to crawl instead of (or in addition to) keyword searches. Supported: search pages with any filters, `/jobs/cat{N}/`, `/jobs/subcat/{N}/`, `/jobs/area/{codes}/`, raw `/api/jobs/search?…` URLs, and direct job pages (`/job/{code}/{hash}/`).

## `enrichEmails` (type: `boolean`):

If enabled, tries to discover a contact email for each hiring employer from public sources (name-based discovery — drushim postings don't expose employer websites). Adds contactEmail + contactWebsite columns plus a detailed emailEnrichment object. Best-effort; only charged when an email is returned, never for misses.

## `qualifyByPayment` (type: `boolean`):

Requires "Enrich with contact emails". Scans each discovered employer site — reusing the pages already fetched for email discovery — for payment processors and e-commerce platforms. Adds takesPayments, paymentProcessors, stripeLiveKey and paymentConfidence. No extra charge.

## `includeKeywords` (type: `array`):

Only keep jobs whose title, company, description or requirements contains at least one of these (case-insensitive, Hebrew or English). Filtered rows are never charged.

## `excludeKeywords` (type: `array`):

Drop jobs whose title, company, description or requirements contains any of these (case-insensitive).

## `fromDate` (type: `string`):

Only keep jobs posted on or after this date (ISO 8601, e.g. 2026-08-01).

## `toDate` (type: `string`):

Only keep jobs posted on or before this date (ISO 8601). A date-only value means through the end of that day.

## `maxAgeMinutes` (type: `integer`):

Only keep jobs posted within the last N minutes. Empty or 0 = no age limit. Ideal for tight scheduled runs.

## `incrementalMode` (type: `boolean`):

Compare against the previous run and only emit jobs that are NEW, UPDATED, or REAPPEARED — you are only charged for what changed. The first run seeds the baseline. Each emitted row carries `changeType` and `firstSeenAt`.

## `stateKey` (type: `string`):

Optional stable name for this tracked search (e.g. "dev-tel-aviv"). Leave empty to auto-derive one from the search inputs — same inputs, same baseline.

## `emitUnchanged` (type: `boolean`):

When incremental, also emit rows whose content did not change (charged as normal results).

## `emitExpired` (type: `boolean`):

When incremental, emit a small `changeType: EXPIRED` row for every previously-tracked job that vanished from drushim.

## `skipReposts` (type: `boolean`):

When incremental, skip jobs whose content matches a previously-expired posting (employer re-listed the same ad under a new id).

## `resetIncrementalState` (type: `boolean`):

One-shot: drop the stored baseline for this state key at the start of the run.

## `compact` (type: `boolean`):

Emit only the core fields (id, title, company, regions, salary, skills, apply link, dates) — small payloads for AI-agent / MCP / LLM pipelines.

## `excludeEmptyFields` (type: `boolean`):

Drop null / empty-string / empty-array fields from each record.

## `descriptionMaxLength` (type: `integer`):

Truncate description/requirements fields to this many characters (0 = full text). Controls LLM prompt cost and dataset size.

## `telegramBotToken` (type: `string`):

Your bot's token from @BotFather. With Chat ID set, a summary of new jobs is sent after each run.

## `telegramChatId` (type: `string`):

Chat or channel id the bot posts to (e.g. "-1001234567890").

## `slackWebhookUrl` (type: `string`):

Slack incoming-webhook URL (api.slack.com/messaging/webhooks).

## `discordWebhookUrl` (type: `string`):

Discord incoming-webhook URL (Server Settings → Integrations → Webhooks).

## `whatsappAccessToken` (type: `string`):

WhatsApp Cloud API token (Meta Business system user). The recipient must have messaged your business number within the last 24h — Cloud API rule.

## `whatsappPhoneNumberId` (type: `string`):

Your WhatsApp Business phone-number id (numeric, from the Meta dashboard).

## `whatsappTo` (type: `string`):

Recipient phone in E.164 without + (e.g. "972501234567").

## `webhookUrl` (type: `string`):

Receives one JSON POST with { metadata, items } after the run — the universal escape hatch for n8n / Make / Zapier / custom backends.

## `webhookHeaders` (type: `object`):

Optional custom headers for the generic webhook (e.g. {"Authorization": "Bearer …"}).

## `notificationLimit` (type: `integer`):

How many job lines each chat notification includes (1–20). The rest are summarised as a count.

## `notifyOnlyChanges` (type: `boolean`):

With Incremental mode on, notify only about NEW / UPDATED / REAPPEARED jobs.

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

Hard cap on the number of jobs collected across all searches. Use this to limit billing.

## `maxPagesPerSearch` (type: `integer`):

Stop paginating each search after this many API pages (~10 jobs per page). The walk also stops automatically at the last page.

## `maxConcurrency` (type: `integer`):

Parallel search tasks / job pages. Kept modest by default — drushim rate-limits bursty clients.

## Actor input object example

```json
{
  "query": [
    "מפתח"
  ],
  "cvOptional": false,
  "enrichEmails": false,
  "qualifyByPayment": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "skipReposts": false,
  "resetIncrementalState": false,
  "compact": false,
  "excludeEmptyFields": false,
  "notificationLimit": 5,
  "notifyOnlyChanges": false,
  "maxItems": 1000,
  "maxPagesPerSearch": 20,
  "maxConcurrency": 4
}
```

# Actor output Schema

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

One row per job: title, company, regions, salary, skills, description + requirements, apply link.

# 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 = {
    "query": [
        "מפתח"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/drushim-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 = { "query": ["מפתח"] }

# Run the Actor and wait for it to finish
run = client.actor("memo23/drushim-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 '{
  "query": [
    "מפתח"
  ]
}' |
apify call memo23/drushim-scraper --silent --output-dataset

```

## MCP server setup

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