# Eluta.ca Jobs Scraper (`memo23/eluta-scraper`) Actor

Scrape eluta.ca — Canada's national job search engine — by keyword and city. Full descriptions, CAD salary min/max, employer website, NAICS code, HQ geo, Top-100 employer rating, and the real ATS apply link (not an eluta redirect). Incremental monitoring built in. JSON or CSV out.

- **URL**: https://apify.com/memo23/eluta-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 23 total users, 22 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

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

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

## Eluta.ca Jobs Scraper — Canadian Jobs, Salaries & Real Apply Links

Turn [eluta.ca](https://www.eluta.ca) — Canada's national job search engine, indexing postings directly from ~10,000 employer career sites — into structured hiring data. Search any keyword × location combination (or paste any eluta URL) and get full job descriptions, structured CAD salaries, employer metadata with NAICS codes and HQ coordinates, and the employer's **real ATS apply link** — not an eluta redirect.

**$1 per 1,000 jobs** ($0.001/result — a third less than the $1.5 category leader), and the only Eluta actor with **real employer-email discovery**: opt-in enrichment that looks up the employer's actual contact addresses instead of regexing the description text and hoping an email was typed into it.

![How Eluta.ca Jobs Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-eluta.png)

### Why this actor

- **Real apply URLs.** Every job's outbound redirect is resolved to the employer's actual ATS posting (Workday, iCIMS, Greenhouse, SmartRecruiters, Talcura…) — the link a candidate or recruiter actually needs.
- **Full descriptions, not snippets.** Complete description HTML + plain text from the job page (typically 1,500–6,000 chars), plus the SERP snippet.
- **Salary-first.** Structured CAD salary (`salaryMin` / `salaryMax` / `salaryType`) straight from eluta's own JSON-LD where the employer provides it, with a regex fallback over description text.
- **Employer intelligence built in.** Employer website + domain, logo, description, NAICS industry code, HQ street address with latitude/longitude, and the Canada's Top 100 Employers rating where awarded.
- **$1 per 1,000 jobs.** $0.005 per run + $0.001 per result — priced below the leading Eluta actor, with the same fields and more.
- **Source transparency.** Each row carries the employer career-site domain eluta indexed the posting from (`sourceDomain`) and the employer's ATS job ID (`atsJobId`).
- **Monitoring mode.** Run it on a schedule and only get postings you haven't seen before — no charge for skipped repeats.

### Use cases

| Who | What |
|---|---|
| Recruiters & sourcers | Fresh Canadian postings by keyword/city with direct ATS links and hiring-employer websites |
| Job-market analysts | Salary bands, employment types, NAICS industries and posting velocity across Canada |
| Lead-gen teams | Companies actively hiring (with website, domain, HQ geo) as buying-intent signals — add opt-in contact-email enrichment |
| Job boards & aggregators | Structured Canadian feed with dedupe-friendly stable IDs and change monitoring |
| AI agents / LLM apps | Clean JSON rows with full description text, ready for embedding or summarisation |

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `query` | array | `["software engineer"]` | Each keyword runs as its own search; eluta in-query qualifiers allowed |
| `location` | array | `["Toronto"]` | Combined with every keyword; empty = all of Canada |
| `sortBy` | string | `relevance` | `date` = newest first (best for monitoring), `popularity` |
| `startUrls` | array | — | Any eluta.ca URL: `/search?q=…`, SEO pages (`/Nurse-jobs-in-Toronto-ON`), employer pages (`/jobs-at-…`), direct job URLs (`/spl/…`) |
| `includeDetails` | boolean | `true` | Fetch each job's detail page (description, dates, employer metadata, salary). Off = fast SERP-only rows |
| `resolveApplyUrls` | boolean | `true` | Resolve the outbound redirect to the employer's real ATS URL |
| `enrichEmails` | boolean | `false` | Opt-in employer contact-email discovery (billed per email found) |
| `monitoringMode` | boolean | `false` | Only return jobs not delivered on previous runs |
| `maxItems` | integer | `1000` | Hard cap across all searches |
| `maxPagesPerSearch` | integer | `20` | ~10 jobs per SERP page |
| `maxConcurrency` | integer | `10` | Parallel job-page fetches |

#### Example input

```json
{
    "query": ["registered nurse", "software engineer"],
    "location": ["Toronto", "Vancouver"],
    "sortBy": "date",
    "maxItems": 200
}
```

Monitoring feed (schedule daily; only new postings after the first baseline run):

```json
{
    "query": ["data engineer"],
    "location": ["Toronto"],
    "sortBy": "date",
    "monitoringMode": true
}
```

### Output schema

One row per job. All fields always present — unavailable data points are `null`, never omitted.

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

```json
{
    "type": "job",
    "jobId": "a69ac728cec9333a7faf711a00cec597",
    "atsJobId": "23804",
    "title": "Registered Nurse Critical Care 6 East Certificate Required",
    "companyName": "Humber River Health",
    "companySlug": "humber-river-health",
    "companyUrl": "https://www.eluta.ca/jobs-at-humber-river-health",
    "companyWebsite": "https://www.hrh.ca/",
    "companyDomain": "hrh.ca",
    "naicsCode": "622110",
    "companyHeadquarters": {
        "streetAddress": "1235 Wilson",
        "addressLocality": "North York",
        "addressRegion": "Ontario",
        "postalCode": "M3M 0B2",
        "addressCountry": "Canada",
        "latitude": 43.7256,
        "longitude": -79.4849
    },
    "topEmployer": false,
    "topEmployerRating": null,
    "location": "North York, ON",
    "postedAgo": "1 hour ago",
    "postedDate": "2026-08-04T03:38:14",
    "closingDate": "2026-09-08T00:00:00",
    "employmentType": "FULL_TIME",
    "industry": "Health/Medical - Nursing",
    "salaryText": "$41.15 - $58.98",
    "salaryMin": 41.15,
    "salaryMax": 58.98,
    "salaryCurrency": "CAD",
    "salaryType": "HOUR",
    "sourceDomain": "careersen-hrrh.icims.com",
    "elutaUrl": "https://www.eluta.ca/spl/registered-nurse-critical-care-6-east-certificate-required-a69ac728cec9333a7faf711a00cec597",
    "applyUrl": "https://careersen-hrrh.icims.com/jobs/23804/registered-nurse-critical-care-6-east-certificate-required/job",
    "applyUrlResolved": true,
    "descriptionText": "Position Profile Humber River Health. Tomorrow's Healthcare, Today…",
    "descriptionHtml": "<p>…</p>",
    "searchQuery": "registered nurse",
    "detailFetched": true,
    "scrapedAt": "2026-08-04T09:28:41.112Z"
}
```

#### Key output fields

| Group | Fields |
|---|---|
| Identity | `jobId` (stable eluta hex id), `atsJobId`, `elutaUrl` |
| Role | `title`, `employmentType`, `industry`, `descriptionText`, `descriptionHtml`, `descriptionSnippet` |
| Salary | `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency` (CAD), `salaryType` (`YEAR` / `HOUR`) |
| Employer | `companyName`, `companyLegalName`, `companySlug`, `companyUrl`, `companyWebsite`, `companyDomain`, `companyDescription`, `companyLogoUrl`, `naicsCode`, `companyHeadquarters` (address + lat/lng) |
| Recognition | `topEmployer`, `topEmployerRating`, `topEmployerReviewUrl` (Canada's Top 100 Employers) |
| Location | `location`, `addressLocality`, `addressRegion`, `addressCountry` |
| Dates | `postedAgo`, `postedDate` (ISO), `closingDate` (ISO) |
| Apply | `applyUrl` (real ATS link), `applyUrlResolved`, `sourceDomain` |
| Run context | `searchQuery`, `searchUrl`, `isSponsored`, `detailFetched`, `scrapedAt` |

### How it works

1. Your keywords × locations become eluta searches (or your pasted URLs are auto-classified: SERP / SEO page / employer page / direct job).
2. SERP pages are walked via `?pg=N`, ~10 jobs per page, streaming rows into a sliding-window worker pool — first results arrive within seconds.
3. Each job's detail page is fetched and parsed: eluta renders two page variants (microdata and JSON-LD) and both are handled, with the full description, ISO dates, employer metadata and structured salary.
4. The outbound apply redirect (`/direct/i`) is resolved to the employer's real ATS URL.
5. The merged row is pushed to the dataset — one row per job, JSON/CSV/Excel export.

### What makes this different from other Eluta actors

- **Apply links that actually apply.** Other actors return the eluta.ca job URL as `applyUrl`. This one resolves the redirect to the employer's ATS posting (e.g. `careersen-hrrh.icims.com/jobs/23804/...`).
- **Full description coverage.** Both eluta page variants are parsed, so descriptions come back complete — including postings where SERP-only scrapers return empty text.
- **Employer depth.** NAICS code, HQ address with coordinates, employer website/domain/logo/description, and Top-100 employer ratings — fields for lead-gen and market analysis, not just job listings.
- **Honest rate-limit handling.** Eluta soft-blocks bursty IPs; this actor detects it and rotates through a residential pool automatically instead of silently returning truncated results.
- **Real contact discovery, not description regex.** Other actors "extract emails" by pattern-matching the posting text — which contains no email for the vast majority of jobs. Opt-in `enrichEmails` discovers the hiring employer's actual contact emails from their website and public sources, and bills only per email actually found.

### Notes & limitations

- **Salary coverage follows the source.** Only a minority of Canadian employers publish pay on their postings (~20–30% in our tests). Where eluta has it, you get structured min/max; where it only appears in prose, the text parser picks it up best-effort.
- `applyUrl` falls back to the eluta job URL when the redirect can't be resolved (`applyUrlResolved: false`) — rare (≥85% resolve in testing).
- `topEmployerRating` only exists for employers recognised in Canada's Top 100 Employers project (eluta is run by its publisher, Mediacorp).
- Monitoring mode's first run returns everything matching — that seeds your baseline; subsequent runs return only new postings.
- Sponsored SERP slots are included and flagged via `isSponsored`.

### FAQ

**How fresh are the postings?**
Eluta indexes employer career sites continuously — with `sortBy: date` the top results are typically minutes to hours old, and each row carries `postedAgo` + an ISO `postedDate`.

**Can I scrape a specific employer's jobs?**
Yes — paste their eluta employer page (e.g. `https://www.eluta.ca/jobs-at-sunnybrook`) into `startUrls`.

**What does a "real apply URL" mean exactly?**
Eluta's apply buttons go through an internal redirect. The actor follows that redirect server-side and stores the destination — the employer's own ATS posting — in `applyUrl`, with the source career-site domain in `sourceDomain`.

**Does it cover all of Canada?**
Yes. Leave `location` empty to search nationally, or run multiple cities in one go — every query × location pair becomes its own search in the same run.

**How do I get only new jobs each day?**
Enable `monitoringMode`, set `sortBy: date`, and schedule the actor daily. Already-delivered job IDs are skipped without fetching (and without charge). `resetMonitoringState` rebuilds the baseline.

### Support

- **Bugs / feature requests** — open an issue on the GitHub repo
- **Custom exports / tailored fields** — drop a note via the Apify Store contact form
- **Other actors** — see my Apify Store profile for the rest of the catalog

### 🔎 Explore more scrapers

Same engine, other job boards:

- [TotalJobs Scraper](https://apify.com/memo23/totaljobs-scraper) — UK all sectors
- [IrishJobs.ie Scraper](https://apify.com/memo23/irishjobs-scraper) — Ireland's leading job board
- [StepStone Job Scraper](https://apify.com/memo23/stepstone-search-cheerio-ppr) — Germany, Austria, France, Denmark
- [s1jobs Scraper](https://apify.com/memo23/s1jobs-scraper) — Scotland
- [CWJobs Scraper](https://apify.com/memo23/cwjobs-scraper) — UK tech & IT

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

***

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/eluta-scraper`).

**Purpose:** Turn eluta.ca keyword × location searches (or any eluta.ca URL) into structured Canadian job rows with CAD salary, employer metadata, and the employer's real ATS apply link.

**Minimal input:**

```json
{
    "query": ["software engineer"],
    "location": ["Toronto"],
    "maxItems": 20
}
```

Instead of keyword search you can pass `startUrls` — any eluta.ca URL (`/search?q=…`, SEO pages like `/Nurse-jobs-in-Toronto-ON`, employer pages `/jobs-at-…`, or direct `/spl/…` job URLs) is auto-classified.

**Output:** one row per job — `jobId`, `atsJobId`, `title`, `companyName`, `companySlug`, `companyUrl`, `companyWebsite`, `companyDomain`, `naicsCode`, `companyHeadquarters` {streetAddress, addressLocality, addressRegion, postalCode, addressCountry, latitude, longitude}, `topEmployer`, `topEmployerRating`, `location`, `postedAgo`, `postedDate`, `closingDate`, `employmentType`, `industry`, `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryType`, `sourceDomain`, `elutaUrl`, `applyUrl`, `applyUrlResolved`, `descriptionText`, `descriptionHtml`, `searchQuery`, `detailFetched`, `scrapedAt`.

**Behaviors an agent should know:**

- Always set `maxItems` — uncapped it defaults to 1000 across all searches.
- `startUrls` are crawled instead of (or alongside) keyword searches; each `query` × `location` pair becomes its own search.
- `includeDetails` and `resolveApplyUrls` default true (one extra request per job each); set both false for fast SERP-only rows.
- `incrementalMode` (preferred) or legacy `monitoringMode` keep a per-search baseline in the key-value store and emit only NEW/UPDATED/REAPPEARED jobs — billed only for what changed.
- Billing: $0.005 per run + $0.001 per result ($1 per 1,000 jobs); `enrichEmails` is billed per contact email actually found, never for misses.
- Public postings only — salary is published on a minority of Canadian jobs, so salary fields are often null.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is **not affiliated with, endorsed by, or sponsored by** Eluta.ca, Mediacorp Canada Inc., 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 rendered server-side by eluta.ca — no login, no CAPTCHA solving, no authenticated endpoints. When eluta rate-limits an IP it backs off and rotates rather than attempting to bypass the verification page.

Users are responsible for:

- Complying with Eluta.ca's Terms of Service
- Following PIPEDA 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 Eluta.ca

***

### SEO Keywords

eluta scraper, scrape eluta.ca, eluta api, eluta.ca job scraper, Apify eluta, canada job scraper, canadian jobs api, canada job listings data, toronto jobs scraper, vancouver jobs scraper, canadian salary data, job postings canada api, canada hiring data, recruitment data canada, job market analytics canada, canada's top 100 employers data, naics job data, employer career site scraper, ats apply links, job feed canada, job monitoring alerts canada, indeed canada alternative, job bank canada alternative

# Actor input Schema

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

Job search keywords — each entry runs as its own eluta search. Eluta in-query qualifiers (e.g. `nurse sort:post`) are allowed.

## `location` (type: `array`):

City or region filters, e.g. `Toronto` or `Vancouver, BC`. Combined with every keyword. Leave empty to search all of Canada.

## `sortBy` (type: `string`):

Result ordering on the eluta side. `date` surfaces the newest postings first (recommended for monitoring), `relevance` is eluta's default ranking.

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

Full eluta.ca URLs to crawl instead of (or in addition to) keyword searches. Supported: `/search?q=...`, SEO pages like `/Nurse-jobs-in-Toronto-ON`, employer pages like `/jobs-at-sunnybrook`, and direct job URLs (`/spl/...`).

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

Fetch each job's detail page for the full description (HTML + text), ISO posted/expiry dates, employment type, industry + NAICS code, employer website, logo, HQ address with latitude/longitude, and the Canada's-Top-100 employer rating. Disable for fast SERP-only rows.

## `resolveApplyUrls` (type: `boolean`):

Follow eluta's outbound redirect to capture the employer's real apply URL (e.g. `sunnybrook.talcura.com/...JobId=12800`) instead of just the eluta.ca job page. One extra lightweight request per job.

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

If enabled, tries to find a contact email for each hiring employer. The employer website usually comes straight from the job page's own metadata, and its contact/about pages are scanned for an email. Adds contactEmail + contactWebsite columns plus a detailed emailEnrichment object. Best-effort, billed per contact email found; only charged when an email is returned, never for misses.

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

Requires "Enrich with contact emails". Scans each employer's website — reusing the pages already fetched for email discovery, so no extra cost or time — for payment processors and e-commerce platforms (Stripe, Shopify, PayPal, Paddle, Lemon Squeezy, WooCommerce, Square, Chargebee and more). Adds takesPayments, paymentProcessors, stripeLiveKey and paymentConfidence. No extra charge — included with each enriched employer.

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

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

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

Drop jobs whose title, company or description 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. "nurse-toronto"). 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 eluta.

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

## `monitoringMode` (type: `boolean`):

Legacy id-only monitoring: jobs whose eluta ID was already delivered to you are skipped before the detail fetch (no charge). Kept for existing scheduled tasks — new setups should use Incremental mode above, which also detects UPDATED / REAPPEARED / EXPIRED and powers change-only notifications.

## `resetMonitoringState` (type: `boolean`):

One-shot toggle: clear the legacy 'jobs already seen' record at the start of this run. Has no effect when legacy monitoring mode is off.

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

Emit only the core fields (id, title, company, location, salary, 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 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. "14165551234").

## `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 result pages (~10 jobs per page). The walk also stops automatically when a page returns no new jobs.

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

Maximum number of job pages processed in parallel.

## `minConcurrency` (type: `integer`):

Minimum number of job pages processed in parallel.

## `maxRequestRetries` (type: `integer`):

Number of retries before a failed request is given up.

## Actor input object example

```json
{
  "query": [
    "software engineer"
  ],
  "location": [
    "Toronto"
  ],
  "sortBy": "relevance",
  "includeDetails": true,
  "resolveApplyUrls": true,
  "enrichEmails": false,
  "qualifyByPayment": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "skipReposts": false,
  "resetIncrementalState": false,
  "monitoringMode": false,
  "resetMonitoringState": false,
  "compact": false,
  "excludeEmptyFields": false,
  "notificationLimit": 5,
  "notifyOnlyChanges": false,
  "maxItems": 1000,
  "maxPagesPerSearch": 20,
  "maxConcurrency": 10,
  "minConcurrency": 1,
  "maxRequestRetries": 5
}
```

# 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": [
        "software engineer"
    ],
    "location": [
        "Toronto"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/eluta-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": ["software engineer"],
    "location": ["Toronto"],
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/eluta-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": [
    "software engineer"
  ],
  "location": [
    "Toronto"
  ]
}' |
apify call memo23/eluta-scraper --silent --output-dataset

```

## MCP server setup

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