# WeWorkRemotely Jobs Scraper \[Only $0.99💰] (`memo23/weworkremotely-jobs-scraper`) Actor

Scrape WeWorkRemotely for remote-job leads at $0.99/1K — recruit cross-border, benchmark salaries, mirror the board into your warehouse. Title, company, salary, employment type, eligible-countries list (200+ per job), external apply URL. Auto-routes any WWR URL. JSON or CSV.

- **URL**: https://apify.com/memo23/weworkremotely-jobs-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Jobs, Lead generation, Agents
- **Stats:** 26 total users, 23 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.99 / 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## WeWorkRemotely Jobs Scraper

### How it works

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

**Scrape every remote job on WeWorkRemotely.com — title, company, full description, salary, employment type, applicant country eligibility (200+ countries per job), and the real external apply URL. One actor, JSON or CSV out.**

Paste any WeWorkRemotely URL — the main listing, a category, a search, or a direct job link — and get a clean dataset with one row per job. Auto-routes by URL pattern. Pure HTTP (no browser, no CAPTCHA solver).

> 💰 **$1.99 per 1,000 results.** Half the price of the next-tier competitor, with a richer schema and the only WWR scraper that surfaces `applicantCountries`.

---

### ✨ Why use this scraper?

- **Every URL surface, one actor.** Main listing (`/remote-jobs`), category pages (`/categories/{slug}`), search results (`/remote-jobs/search?term=...`), and direct job-detail URLs are all accepted. URLs are auto-classified — mix freely in one run.
- **JSON-LD-first parsing.** Every WWR detail page exposes a structured `JobPosting` block. We extract from that, not brittle DOM selectors — your scrapes don't break when WWR redesigns.
- **The `applicantCountries` differentiator.** WeWorkRemotely encodes a per-job country eligibility list (often 200+ countries). No other scraper on the Apify Store exposes this cleanly. It's *the* field for geo-targeted recruiting.
- **Real external apply URL.** The on-page "Apply now" button is locked behind a WWR login on most jobs. We extract the JSON-LD `url` field, which is the actual company-side apply URL — what your buyers actually want.
- **Salary parsing.** `salaryMin`, `salaryMax`, `salaryCurrency`, and `unit` parsed from JSON-LD `baseSalary`. (Note: WWR sometimes uses 0/0 as a placeholder when the employer didn't supply a salary — that's surfaced as `min: 0, max: 0`, not omitted.)
- **Pagination handled.** Category and search listings paginate via `?page=N`; the actor walks all pages until `maxItems` is hit.
- **JSON or CSV out.** Auto-exported to both formats for every run.

---

### What makes this richer than alternatives

| Capability | This actor | Other WWR scrapers on Apify Store |
|---|---|---|
| **Pricing** | **$1.99 / 1,000 results** | Free up to $4 / 1K |
| **`applicantCountries`** | ✅ Full country array (200+ entries) | ❌ Not exposed by any other scraper |
| **External apply URL** | ✅ Real company-side URL from JSON-LD | ⚠️ Often returns WWR's locked "Apply now" link |
| **Salary parsing** | ✅ `min`, `max`, `currency`, `unit` from JSON-LD | ⚠️ Often raw string only |
| **URL surfaces** | Main + category + search + direct | Mostly main listing or single URL only |
| **Pagination** | Auto-walks `?page=N` | Mixed (some scrapers stop at page 1) |
| **Output options** | JSON + CSV (auto-exported) | JSON only on most |
| **HTTP stack** | impit (Firefox/Chrome TLS fingerprints) + retry chain | Plain HTTP on some |

---

### Supported inputs

The actor has **two alternative input paths** — pick one:

#### Option A — Search by URL 🔗 (recommended)

Paste any weworkremotely.com URL in `startUrls`. **Any WWR page that lists jobs is supported** — the actor extracts every `/remote-jobs/{slug}` anchor from whatever page you paste.

##### Listing URLs

| Pattern | Example | What it does |
|---|---|---|
| `/remote-jobs` | `/remote-jobs` | Main remote-jobs listing |
| `/categories/{slug}` | `/categories/remote-front-end-programming-jobs`, `/categories/remote-customer-support-jobs`, etc. | Category listings (every WWR category works) |
| `/100-percent-remote-jobs` | `/100-percent-remote-jobs` | Location / scope filter pages |
| `/remote-full-time-jobs` / `/remote-contract-jobs` | same | Employment-type filters |
| `/remote-jobs/search?term={query}` | `/remote-jobs/search?term=javascript` | Search results |

##### Job detail URLs

| Pattern | What it does |
|---|---|
| `/remote-jobs/{company-slug}-{job-slug}` | Single job — fetched and parsed directly. URL may end with `-1`/`-2`/`-3` etc. when the company has multiple jobs with similar slugs. |

Mix any combination in `startUrls`; each URL is auto-classified by shape.

#### Option B — Configure with Filters 🎛️

Leave `startUrls` empty and pick filters instead. The actor builds the matching WWR URL from your selection.

| Filter | Routes to | Notes |
|---|---|---|
| `searchKeyword` | `/remote-jobs/search?term=…` | Highest priority — wins over category if both are set |
| `category` | `/categories/{slug}` | 10 official categories supported (Full-Stack Programming, Front-End, Back-End, DevOps, Design, Product, Management & Finance, Sales & Marketing, Customer Support, All Other) |
| `employmentType: full-time` | `/remote-full-time-jobs` | Only applied when keyword + category are empty |
| `employmentType: contract` | `/remote-contract-jobs` | Same |
| nothing | `/remote-jobs` | Default — all jobs |

**WWR filters do not combine** (a keyword on a category URL is ignored by WWR's server), so the actor picks exactly one URL from your filter inputs in the priority order above. If you need multi-filter behavior, use Option A and paste multiple URLs.

**Option A wins when both are set** — if `startUrls` is non-empty, Option B filters are ignored entirely.

---

### 🎯 Use cases

| Team | Typical use |
|---|---|
| **Recruiters / talent sourcing** | Pull active remote jobs by category (programming, design, marketing, support, etc.) and country eligibility. Filter on `applicantCountries` to find jobs open to candidates in your region. |
| **Job-board aggregators** | Mirror WWR's listings into your own dataset with normalised fields. Schedule daily runs to capture new postings. |
| **Salary benchmarking** | Build a remote-salary dataset from `salaryMin` / `salaryMax` / `salaryCurrency` across categories. |
| **Sales / BD** | Build a target list of remote-first companies hiring engineers / designers / etc., with their external website (`companyUrl`) for outreach. |
| **Market research** | Track which job categories and countries are most heavily represented across WWR over time. |

---

### Quick start

```jsonc
// Programming jobs, first 50
{ "startUrls": ["https://weworkremotely.com/categories/remote-programming-jobs"], "maxItems": 50 }

// JavaScript jobs by search keyword
{ "startUrls": ["https://weworkremotely.com/remote-jobs/search?term=javascript"], "maxItems": 100 }

// Multiple categories in one run
{ "startUrls": [
    "https://weworkremotely.com/categories/remote-programming-jobs",
    "https://weworkremotely.com/categories/remote-customer-support-jobs",
    "https://weworkremotely.com/categories/remote-marketing-jobs"
  ], "maxItems": 200 }

// Single job by direct URL
{ "startUrls": ["https://weworkremotely.com/remote-jobs/codesignal-software-engineer-business-experience-1"] }
````

***

### Input configuration

| Field | Type | Required | Notes |
|---|---|---|---|
| `startUrls` | string\[] | yes (or use Option B) | Listing, category, search, or detail URLs. Mix freely. |
| `category` | string | optional (Option B) | One of the 10 WWR categories. Routes to `/categories/{slug}`. |
| `searchKeyword` | string | optional (Option B) | Keyword search. Wins over `category` if both are set. |
| `employmentType` | string | optional (Option B) | `full-time` / `contract`. Only used when `category` and `searchKeyword` are empty. |
| `includeDetailPages` | boolean | no (default `true`) | **ON** — fetch every job's detail page (full 23+ field schema). **OFF** — emit rows directly from listing cards (~12 fields, ~30× faster). |
| `countries` | string\[] | no | Post-filter on `applicantCountries[]`. ISO-2 codes (e.g. `["US", "GB", "DE"]`). Empty = no filter. No effect in listing-only mode. |
| `timeFilter` | string | no (default any) | Post-filter on `postedDate`. One of `""`, `"24h"`, `"7d"`, `"30d"`, `"90d"`. |
| `regions` | string\[] | no | Post-filter on listing-card region labels. Case-insensitive substring match against `tags[]`. WWR uses values like `"Anywhere in the World"` and `"United States of America"` (with flag emojis). |
| `jobTypes` | string\[] | no | Post-filter on `employmentType[]`. Multi-select array (e.g. `["Full-Time", "Contract"]`). Distinct from Option B's URL-routing `employmentType`. |
| `minSalary` | integer | no (default 0) | Post-filter — keep rows where `salary.min >= minSalary` OR salary is unspecified. Permissive on `null`/`0` to avoid dropping jobs with no posted salary. USD. |
| `includeDescription` | boolean | no (default `true`) | When `false`, drops `description`, `descriptionHtml`, `descriptionText` from output. Lighter dataset. |
| `cleanHtml` | boolean | no (default `false`) | When `true`, emits only plain-text `description` (from our `descriptionText`); drops the HTML variants. Ignored when `includeDescription` is `false`. |
| `maxItems` | integer | no (default 1000) | Hard cap on jobs collected. Non-paying users are capped at 100. |
| `maxConcurrency` | integer | no (default 10) | Parallel job-detail fetches. |
| `minConcurrency` | integer | no (default 1) | Min parallel fetches. |
| `maxRequestRetries` | integer | no (default 5) | Retries per failed request. |
| `proxy` | object | no (default RESIDENTIAL) | Apify proxy config. WWR blocks datacenter IPs — keep `RESIDENTIAL`. |

***

### Output overview

One row per job. Schema is flat (no nested arrays of rows). Each row uses these fields:

- **Identifiers**: `jobId`, `identifier`, `url`
- **Job info**: `title`, `description`, `descriptionHtml`, `employmentType[]`, `occupationalCategory[]`, `jobLocationType`, `postedDate`, `validThrough`
- **Company**: `companyName`, `companyLogo`, `companyUrl`, `companyDescription`
- **Geography**: `applicantCountries[]`
- **Compensation**: `salary` (object: `currency`, `min`, `max`, `value`, `unit`)
- **Application**: `applyUrl`, `applyType`, `directApply`
- **Meta**: `ogImage`, `scrapedAt`

***

### Output samples

#### Job row (real smoke-test data, slightly truncated for display)

```jsonc
{
  "type": "job",
  "jobId": "codesignal-software-engineer-business-experience-1",
  "url": "https://weworkremotely.com/remote-jobs/codesignal-software-engineer-business-experience-1",
  "title": "Software Engineer, Business Experience",
  "companyName": "CodeSignal",
  "companyLogo": "https://we-work-remotely.imgix.net/logos/0171/2586/logo.gif?…",
  "companyUrl": "https://codesignal.com/",
  "description": "<div>We are looking for a Software Engineer who wants to be an impactful part of a small team …",
  "jobLocationType": "TELECOMMUTE",
  "applicantCountries": ["AF", "AX", "AL", "DZ", "AS", "AD", "AO", "AI", "…", "(+241 more)"],
  "employmentType": ["Full-Time"],
  "occupationalCategory": ["Full-Stack Programming"],
  "directApply": false,
  "salary": { "currency": "USD", "min": 0, "max": 0, "value": null, "unit": "YEAR" },
  "postedDate": "2025-11-12 08:56:02 UTC",
  "validThrough": "2026-06-10 08:56:02 UTC",
  "applyUrl": "https://codesignal.com/",
  "applyType": "external",
  "scrapedAt": "2026-05-20T05:08:45.358Z"
}
```

***

### Key output fields

#### Identifiers

| Field | Description |
|---|---|
| `jobId` | Stable slug pulled from JSON-LD `identifier.value`. Unique per job. Example: `codesignal-software-engineer-business-experience-1`. |
| `identifier` | Full `{ "@type":"PropertyValue", "name":"{company}", "value":"{slug}" }` object from JSON-LD. |
| `url` | Canonical WWR job-detail URL. |

#### Job information

| Field | Description |
|---|---|
| `title` | Job title. |
| `description` | Full job description (HTML preserved). |
| `descriptionHtml` | Same content but with double-encoded HTML entities normalised. |
| `employmentType[]` | `"Full-Time"`, `"Part-Time"`, `"Contract"`, `"Freelance"`, etc. |
| `occupationalCategory[]` | WWR category labels (e.g. `"Full-Stack Programming"`, `"Customer Support"`). |
| `jobLocationType` | Typically `"TELECOMMUTE"` (WWR is remote-only). |
| `postedDate` | UTC posted timestamp. |
| `validThrough` | When the posting expires. |

#### Company

| Field | Description |
|---|---|
| `companyName` | Hiring organisation display name. |
| `companyLogo` | URL to WWR-hosted small logo. |
| `companyUrl` | Company's own website (when WWR provides it). |
| `companyDescription` | Sometimes provided; often `null`. |
| `companyId` | WWR company-profile URL, e.g. `https://weworkremotely.com/company/walter`. Populated when the URL was discovered via a listing card. |
| `location` | Company HQ as a free-form string (`"Brooklyn, NY"`, `"Colombo, 1, Sri Lanka"`, `"NYC and TLV"`). Distinct from `applicantCountries` — that's where remote workers can apply from; this is where the company itself is based. |

#### Geography (the unique field)

| Field | Description |
|---|---|
| `applicantCountries[]` | Array of country codes (often 200+ entries) the employer says they'll consider applicants from. Pulled from JSON-LD `applicantLocationRequirements`. No other Apify WWR scraper exposes this. |

#### Compensation

| Field | Description |
|---|---|
| `salary.currency` | ISO currency code (`"USD"`, `"EUR"`, etc.). |
| `salary.min` / `salary.max` | Parsed numeric range. `0`/`0` when the employer didn't specify. |
| `salary.value` | Single numeric value (when a range isn't given). |
| `salary.unit` | `"YEAR"`, `"MONTH"`, `"HOUR"`, etc. |

#### Application

| Field | Description |
|---|---|
| `applyUrl` | The **external** company-side apply URL from JSON-LD. This is the differentiator — competitors return WWR's locked button instead. |
| `applyType` | `"external"` when an apply URL was found, otherwise `"unknown"`. |
| `directApply` | Real boolean (WWR's own claim about whether the apply flow is direct). |

***

### ❓ FAQ

**What URLs can I paste?**
Any weworkremotely.com URL. Listing pages (`/remote-jobs`, `/categories/...`, `/remote-jobs/search?term=...`) and direct job-detail URLs (`/remote-jobs/{slug}-{numeric}`) are both auto-classified.

**Why does `salary.min` / `salary.max` sometimes read 0?**
WeWorkRemotely uses `0` as a placeholder when the employer chose not to supply a salary range. The field is left as `0` (not omitted) so you can distinguish "no salary published" (`0/0`) from a real `0` (rare).

**Does the actor need a proxy?**
WWR blocks Apify's datacenter IPs but works fine through RESIDENTIAL proxies (the default). Direct HTTP also works for small runs.

**How does pagination work?**
Category listings and search results paginate via `?page=N`. The actor walks every page until `maxItems` is hit. The main `/remote-jobs` index is a single-page view (no pagination).

**Is the `applyUrl` always external?**
WWR's JSON-LD `url` field is the external apply URL on most jobs. A small number of jobs route apply through WWR itself — those return `https://weworkremotely.com/...` as the apply URL. We surface whatever WWR provides.

**Why does the JSON-LD `url` field hold the apply URL, not the job URL?**
Quirk of WeWorkRemotely's schema.org implementation. The canonical job URL is in our `url` field; the JSON-LD's `url` is what they treat as the "where to apply" link.

**How fresh is the data?**
Each run hits WWR live — no caching. `scrapedAt` is the per-row timestamp.

**What's the difference between detail mode and listing-only mode?**

- **Detail mode** (`includeDetailPages: true`, the default) fetches every job's detail page and returns the full 23+ field schema, including `applicantCountries[]`, parsed `salary`, real external `applyUrl`, full `description`, `validThrough`, etc.
- **Listing-only mode** (`includeDetailPages: false`) emits rows directly from the listing card — no detail fetch. Returns ~12 fields: `title`, `companyName`, `companyLogo`, `companyId`, `location`, `tags`, `isFeatured`, `isBoosted`, `postedAge`, `url`, `scrapedFrom`. **About 30× faster** when you just need a list of jobs + headlines. The `applicantCountries`, `salary`, `description`, `applyUrl` fields are absent.
- A `listingOnly: true` marker is on every listing-only row so downstream consumers can tell them apart.

**How do the `countries` and `timeFilter` filters work?**
Both are **post-filters** applied client-side after scraping (WWR doesn't accept these as URL params). `countries` matches rows where `applicantCountries[]` contains any of the supplied ISO-2 codes (case-insensitive). `timeFilter` keeps rows where `postedDate` is within the selected window. Filtered rows are silently dropped — the run stats show how many.

**Can I combine filters?**
Yes. Country + time filters are post-applied to all rows. Option B filters (category / keyword / employment type) determine which URL gets scraped; the post-filters then narrow down the results.

**Can I get only new jobs in scheduled runs?**
Not built-in (the JSON-LD doesn't carry a stable created-at the way some boards do — `postedDate` is what we have). For deduping across scheduled runs, store seen `jobId`s in a downstream system.

***

### Support

- File an issue or feature request via the [Apify Console **Issues** tab](https://apify.com/memo23/weworkremotely-jobs-scraper/issues).
- For custom output shapes, additional filters, or scheduled feeds into a warehouse, open an issue describing the use case.

***

### Additional Services

Need a remote-jobs dataset shaped to your pipeline? Custom integrations available:

- **Custom output schemas** — alternative field names, flattened structures, filtered subsets for warehouse loaders (Postgres, BigQuery, Snowflake).
- **Webhook delivery** — push new WWR jobs to Slack, Discord, your CRM, or any HTTP endpoint.
- **Scheduled feeds** — daily/weekly runs into S3, Google Sheets, or your DB.
- **Multi-source bundles** — combine WWR with other remote job boards (RemoteOK, JustRemote, Remotive) for full-coverage remote-job intelligence.

Reach out via the [Apify Issues tab](https://apify.com/memo23/weworkremotely-jobs-scraper/issues) or [contact memo23 on Apify](https://apify.com/memo23).

***

### Explore More Scrapers

Looking for related remote-work, jobs, or recruiting data? Other actors by the same maintainer:

- [**HiringCafe Scraper**](https://apify.com/memo23/apify-hiring-cafe-scraper) — All HiringCafe job postings with company info, salary, and remote/location filters
- [**Stepstone Search Scraper**](https://apify.com/memo23/stepstone-search-cheerio-ppr) — DACH + EU jobs by URL or keywords
- [**Y Combinator Scraper**](https://apify.com/memo23/y-combinator-scraper) — YC jobs + companies + founders, $1/1K
- [**LinkedIn Jobs Scraper**](https://apify.com/memo23/linkedin-jobs-scraper) — Public LinkedIn job listings by keyword + location
- [**Naukri Scraper**](https://apify.com/memo23/naukri-scraper) — India's #1 job board with full job details + recruiter info

***

### ⚠️ Disclaimer

This Actor accesses publicly available data on WeWorkRemotely.com for legitimate research, market intelligence, recruiting, and business-analysis purposes. Use of this Actor must comply with WeWorkRemotely's Terms of Service and all applicable laws, including data protection regulations (GDPR, CCPA, etc.). "WeWorkRemotely" and "We Work Remotely" are trademarks of We Work Remotely, Inc. This Actor is not affiliated with, endorsed by, or sponsored by WeWorkRemotely. The Actor's authors are not responsible for any misuse. Users must:

- Respect rate limits and avoid overloading WeWorkRemotely's infrastructure
- Not use scraped data to violate user privacy or any platform terms
- Use the data in compliance with applicable jurisdictions
- Not republish scraped content in violation of copyright

We do not store any scraped data; the Actor returns it directly to your Apify dataset for your authorized use.

***

### SEO Keywords

weworkremotely scraper, we work remotely scraper, weworkremotely jobs scraper, remote jobs scraper, remote work scraper, weworkremotely api alternative, remote programming jobs scraper, remote developer jobs scraper, remote marketing jobs scraper, remote customer support jobs scraper, telecommute jobs scraper, work-from-home jobs scraper, remote job listings api, remote job feed, job aggregator, remote recruiter tool, job board scraper, weworkremotely json export, weworkremotely csv export, apify weworkremotely scraper, remote job lead generation, applicant country eligibility, salary benchmarking remote, remote-first companies database

***

### License

ISC. See [`package.json`](package.json).

# Actor input Schema

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

Full weworkremotely.com URLs to crawl. Mix listing and job-detail URLs in the same array.

## `category` (type: `string`):

WWR official category. Routes to `/categories/{slug}`. Ignored if Start URLs is non-empty.

## `searchKeyword` (type: `string`):

Keyword to search across WWR jobs (e.g. `python`, `react`, `golang`). Routes to `/remote-jobs/search?term=…`. **Takes priority over category** — WWR doesn't combine the two. Leave empty to use category instead.

## `employmentType` (type: `string`):

Filter by employment type. Routes to `/remote-full-time-jobs` or `/remote-contract-jobs`. **Only applied when both keyword and category are empty.**

## `includeDetailPages` (type: `boolean`):

**ON (default)** — fetch every job's detail page. Returns the full 23+ field schema including `applicantCountries`, parsed `salary`, real external `applyUrl`, `validThrough`, full `description`.

**OFF** — emit rows directly from listing cards, no detail fetch. ~30× faster. Returns ~12 fields per job: `title`, `companyName`, `companyLogo`, `companyId`, `location`, `tags`, `isFeatured`, `isBoosted`, `postedAge`, `url`. Detail-only fields (`applicantCountries`, `salary`, `description`, `applyUrl`) are absent.

## `countries` (type: `array`):

Post-filter on `applicantCountries[]`. Keeps only jobs that allow workers from at least ONE of the country codes you list. Use ISO-2 codes (`US`, `GB`, `DE`, etc.). Case-insensitive. Leave empty for no filter.

**Has no effect in listing-only mode** (which doesn't fetch `applicantCountries`).

## `timeFilter` (type: `string`):

Post-filter on `postedDate`. Returns only jobs posted within the selected window.

## `regions` (type: `array`):

Post-filter on the listing-card region label (e.g. `Anywhere in the World`, `USA Only`, `Europe Only`). Case-insensitive substring match. Matches if the row's `tags[]` contains ANY of these labels. Leave empty for no filter.

## `jobTypes` (type: `array`):

Post-filter on `employmentType[]`. Multi-select — keeps jobs whose employmentType is in this list. Common values: `Full-Time`, `Part-Time`, `Contract`, `Freelance`, `Internship`. Distinct from the Option B `employmentType` field which drives URL routing. Empty = no filter.

## `minSalary` (type: `integer`):

Post-filter — keep rows where `salary.min >= minSalary`. **Permissive**: jobs without a listed salary are NOT dropped (many WWR employers don't post salary). Specify in USD. Leave empty or 0 for no filter.

## `includeDescription` (type: `boolean`):

When **off**, the actor drops `description`, `descriptionHtml`, and `descriptionText` from every row. Useful when you only need the metadata + apply URL and want a smaller dataset.

## `cleanHtml` (type: `boolean`):

When **on**, only the plain-text `description` is emitted (taken from our normalised `descriptionText`); the HTML variants (`description` with markup, `descriptionHtml`) are stripped. Ignored when `includeDescription` is off.

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

Hard cap on the number of jobs collected. WeWorkRemotely listing pages often hold 30+ jobs; categories paginate further. Use this cap to control billing.

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

Maximum number of job-detail pages processed in parallel.

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

Minimum number of job-detail pages processed in parallel.

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

Number of retries before a failed request is given up.

## `proxy` (type: `object`):

Apify proxy settings. WeWorkRemotely blocks datacenter IPs; keep `RESIDENTIAL` (the default) for stable runs.

## Actor input object example

```json
{
  "startUrls": [
    "https://weworkremotely.com/categories/remote-programming-jobs"
  ],
  "category": "",
  "searchKeyword": "",
  "employmentType": "",
  "includeDetailPages": true,
  "countries": [],
  "timeFilter": "",
  "regions": [],
  "jobTypes": [],
  "minSalary": 0,
  "includeDescription": true,
  "cleanHtml": false,
  "maxItems": 1000,
  "maxConcurrency": 10,
  "minConcurrency": 1,
  "maxRequestRetries": 5,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "startUrls": [
        "https://weworkremotely.com/categories/remote-programming-jobs"
    ],
    "countries": [],
    "regions": [],
    "jobTypes": [],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/weworkremotely-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 = {
    "startUrls": ["https://weworkremotely.com/categories/remote-programming-jobs"],
    "countries": [],
    "regions": [],
    "jobTypes": [],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/weworkremotely-jobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://weworkremotely.com/categories/remote-programming-jobs"
  ],
  "countries": [],
  "regions": [],
  "jobTypes": [],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call memo23/weworkremotely-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=memo23/weworkremotely-jobs-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "WeWorkRemotely Jobs Scraper [Only $0.99💰]",
        "description": "Scrape WeWorkRemotely for remote-job leads at $0.99/1K — recruit cross-border, benchmark salaries, mirror the board into your warehouse. Title, company, salary, employment type, eligible-countries list (200+ per job), external apply URL. Auto-routes any WWR URL. JSON or CSV.",
        "version": "1.0",
        "x-build-id": "GlVXndokZ0KoUXFh6"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/memo23~weworkremotely-jobs-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-memo23-weworkremotely-jobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/memo23~weworkremotely-jobs-scraper/runs": {
            "post": {
                "operationId": "runs-sync-memo23-weworkremotely-jobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/memo23~weworkremotely-jobs-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-memo23-weworkremotely-jobs-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "startUrls": {
                        "title": "Start URLs",
                        "type": "array",
                        "description": "Full weworkremotely.com URLs to crawl. Mix listing and job-detail URLs in the same array.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "category": {
                        "title": "Category",
                        "enum": [
                            "",
                            "remote-full-stack-programming-jobs",
                            "remote-front-end-programming-jobs",
                            "remote-back-end-programming-jobs",
                            "remote-devops-sysadmin-jobs",
                            "remote-design-jobs",
                            "remote-product-jobs",
                            "remote-management-and-finance-jobs",
                            "remote-sales-and-marketing-jobs",
                            "remote-customer-support-jobs",
                            "all-other-remote-jobs"
                        ],
                        "type": "string",
                        "description": "WWR official category. Routes to `/categories/{slug}`. Ignored if Start URLs is non-empty.",
                        "default": ""
                    },
                    "searchKeyword": {
                        "title": "Search keyword",
                        "type": "string",
                        "description": "Keyword to search across WWR jobs (e.g. `python`, `react`, `golang`). Routes to `/remote-jobs/search?term=…`. **Takes priority over category** — WWR doesn't combine the two. Leave empty to use category instead.",
                        "default": ""
                    },
                    "employmentType": {
                        "title": "Employment type",
                        "enum": [
                            "",
                            "full-time",
                            "contract"
                        ],
                        "type": "string",
                        "description": "Filter by employment type. Routes to `/remote-full-time-jobs` or `/remote-contract-jobs`. **Only applied when both keyword and category are empty.**",
                        "default": ""
                    },
                    "includeDetailPages": {
                        "title": "Include detail pages",
                        "type": "boolean",
                        "description": "**ON (default)** — fetch every job's detail page. Returns the full 23+ field schema including `applicantCountries`, parsed `salary`, real external `applyUrl`, `validThrough`, full `description`.\n\n**OFF** — emit rows directly from listing cards, no detail fetch. ~30× faster. Returns ~12 fields per job: `title`, `companyName`, `companyLogo`, `companyId`, `location`, `tags`, `isFeatured`, `isBoosted`, `postedAge`, `url`. Detail-only fields (`applicantCountries`, `salary`, `description`, `applyUrl`) are absent.",
                        "default": true
                    },
                    "countries": {
                        "title": "Country filter",
                        "type": "array",
                        "description": "Post-filter on `applicantCountries[]`. Keeps only jobs that allow workers from at least ONE of the country codes you list. Use ISO-2 codes (`US`, `GB`, `DE`, etc.). Case-insensitive. Leave empty for no filter.\n\n**Has no effect in listing-only mode** (which doesn't fetch `applicantCountries`).",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "timeFilter": {
                        "title": "Time filter (posted within)",
                        "enum": [
                            "",
                            "24h",
                            "7d",
                            "30d",
                            "90d"
                        ],
                        "type": "string",
                        "description": "Post-filter on `postedDate`. Returns only jobs posted within the selected window.",
                        "default": ""
                    },
                    "regions": {
                        "title": "Regions filter",
                        "type": "array",
                        "description": "Post-filter on the listing-card region label (e.g. `Anywhere in the World`, `USA Only`, `Europe Only`). Case-insensitive substring match. Matches if the row's `tags[]` contains ANY of these labels. Leave empty for no filter.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "jobTypes": {
                        "title": "Job types filter",
                        "type": "array",
                        "description": "Post-filter on `employmentType[]`. Multi-select — keeps jobs whose employmentType is in this list. Common values: `Full-Time`, `Part-Time`, `Contract`, `Freelance`, `Internship`. Distinct from the Option B `employmentType` field which drives URL routing. Empty = no filter.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "minSalary": {
                        "title": "Minimum salary (USD)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Post-filter — keep rows where `salary.min >= minSalary`. **Permissive**: jobs without a listed salary are NOT dropped (many WWR employers don't post salary). Specify in USD. Leave empty or 0 for no filter.",
                        "default": 0
                    },
                    "includeDescription": {
                        "title": "Include description fields",
                        "type": "boolean",
                        "description": "When **off**, the actor drops `description`, `descriptionHtml`, and `descriptionText` from every row. Useful when you only need the metadata + apply URL and want a smaller dataset.",
                        "default": true
                    },
                    "cleanHtml": {
                        "title": "Clean HTML (plain-text only)",
                        "type": "boolean",
                        "description": "When **on**, only the plain-text `description` is emitted (taken from our normalised `descriptionText`); the HTML variants (`description` with markup, `descriptionHtml`) are stripped. Ignored when `includeDescription` is off.",
                        "default": false
                    },
                    "maxItems": {
                        "title": "Maximum items to scrape",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Hard cap on the number of jobs collected. WeWorkRemotely listing pages often hold 30+ jobs; categories paginate further. Use this cap to control billing.",
                        "default": 1000
                    },
                    "maxConcurrency": {
                        "title": "Max concurrency",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Maximum number of job-detail pages processed in parallel.",
                        "default": 10
                    },
                    "minConcurrency": {
                        "title": "Min concurrency",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Minimum number of job-detail pages processed in parallel.",
                        "default": 1
                    },
                    "maxRequestRetries": {
                        "title": "Max request retries",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Number of retries before a failed request is given up.",
                        "default": 5
                    },
                    "proxy": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Apify proxy settings. WeWorkRemotely blocks datacenter IPs; keep `RESIDENTIAL` (the default) for stable runs.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
