# We Work Remotely Jobs Scraper - Remote Jobs by Category (`superslowsloth/weworkremotely-jobs`) Actor

Remote job postings from We Work Remotely's public RSS feeds as flat rows: job title, company, category, job type, region, allowed countries, posted and expiry dates, company website, HQ and full description as HTML and plain text. Pick categories, filter by keyword.

- **URL**: https://apify.com/superslowsloth/weworkremotely-jobs.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 job scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

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

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

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

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

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

# README

## We Work Remotely Jobs Scraper - Remote Jobs by Category

Remote job postings from [We Work Remotely](https://weworkremotely.com)'s public RSS
feeds, as flat, typed dataset rows: job title, company, category, job type, region,
allowed countries, posted and expiry dates, company website and headquarters, and the
full description as both HTML and plain text. Pick categories, filter by keyword, run
it on a schedule and send the dataset to Sheets, a webhook or your warehouse.

### What you are paying for, honestly

The data comes from the RSS feeds We Work Remotely publishes itself. They need no
login, cookie or API key; anyone can read the same XML. What this actor adds:

- **Flat, typed rows.** The company is split from the title (`Vercel: Site Engineer`
  becomes `company` and `title`), dates are ISO 8601, the Headquarters and website
  lines are lifted out of the description into their own columns, the country list is
  an array without flags, and the description is also given as clean text.
- **Several feeds in one run, deduplicated.** The same posting found in two feeds,
  or listed twice in one feed, is one row and one charge.
- **Keyword filter** over title, company, category, skills and description, applied
  before the row cap, so jobs you filter out are not billed.
- **Refusal handling.** A refused (HTTP 403/429/5xx), timed-out or non-feed response is
  retried from a fresh proxy address; an unknown category is reported and skipped.

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `categories` | array | `remote-design-jobs` | Category feed slugs, one request each. Empty = the all-jobs feed. A full feed URL also works. |
| `keywords` | array | - | Keep jobs mentioning **any** of these terms (case-insensitive). |
| `maxItems` | integer | `100` | Most jobs for the whole run, after dedupe and keywords. Each job is billed, so this is a spending cap. |
| `proxyConfiguration` | proxy | Apify proxy | We Work Remotely answered 200 from an Apify datacenter and a residential address on 2026-10-09; the proxy is there so a refused request can be retried from another address. |

**Which feed is which** (measured 2026-10-09):

- The **all-jobs feed** (leave `categories` empty) is a sampler: 89 jobs, about the
  newest 10 per category.
- **Category feeds are deeper:** `remote-sales-and-marketing-jobs` 89, `all-other-remote-jobs`
  72, `remote-customer-support-jobs` 52, `remote-full-stack-programming-jobs` 50,
  `remote-design-jobs` 29, most others 18 to 25, `remote-front-end-programming-jobs` 5.
  Feeds hold the board's current postings, not its history.
- **Slugs, as on the site:** `remote-customer-support-jobs`, `remote-design-jobs`,
  `remote-product-jobs`, `remote-full-stack-programming-jobs`,
  `remote-front-end-programming-jobs`, `remote-back-end-programming-jobs`,
  `remote-devops-sysadmin-jobs`, `remote-sales-and-marketing-jobs`,
  `remote-management-and-finance-jobs`, and `all-other-remote-jobs` (**no** `remote-`
  prefix on that one). Slugs are lower-cased for you. A slug We Work Remotely does not
  have is answered with an empty HTTP 301; the run logs it, skips it and carries on.

### Output

One row per job posting.

```
job_id, guid, url                  job_id = last part of the URL; guid = the feed's guid (the URL)
title, company                     "Vercel: Site Engineer" -> company "Vercel", title "Site Engineer"
category, job_type                 "Design", "Full-Time" / "Contract"
region, countries, state           where the role is open (see below)
skills                             the employer's tags, as one string
published_at, expires_at           ISO 8601 with offset, e.g. 2026-10-08T07:31:34+00:00
headquarters, company_url, company_logo_url
description_html, description_text
source_feed                        "all" or the category slug that produced the row
```

Dataset views: **Jobs** and **Jobs with descriptions**.

#### Null means "not given", never a guess

Counts are over the 356 distinct postings read from 13 feeds on 2026-10-09.

- `company` splits at the **first** `": "` of the feed title, so
  `IxDF - Interaction Design Foundation: Course Writer and Editor: UX, UI, and AI` has
  company `IxDF - Interaction Design Foundation`. A title without that separator would
  give `company` null and the whole text as `title`; none of the 356 did. A company name
  that itself contains `": "` would be cut short.
- `state` (251 of 356 null) and `skills` (266 null) are empty on most postings. That is
  normal. `skills` is left as one string, because the tags themselves contain "and"
  (`Email Marketing and Automation`) and cannot be split reliably.
- `countries` is an **empty list**, not null, when the employer named none (269 of 356).
  When set it is the countries the employer will hire in, flags removed, as WWR writes
  them (`United States of America`, `Korea (Republic of)`). It narrows `region`, which
  is usually `Anywhere in the World` and sometimes a city or `EMEA`.
- `headquarters` (7 null) and `company_url` (205 null) are read from the first paragraph
  of the description, where We Work Remotely prints them; null when the employer left
  them out. `company_logo_url` (258 null) is sent on about one posting in four.
- `job_type` is null when the feed sends an empty `<type/>` or no type tag, which
  happened for 1 of the 356 and for every posting of the legacy `remote-programming-jobs`
  feed. `published_at` and `expires_at` are null only if the feed omits the tag; the
  legacy feed omits `expires_at`.
- `description_text` omits the Headquarters/URL paragraph and the logo (they have their
  own columns). `description_html` is as sent, **except** that an inline `data:` image is
  emptied (`src=""`): one real posting embedded a 1.4 MB base64 PNG.
- We Work Remotely lists a posting once per location variant under **one** URL
  (measured: Tailscale's `Backend Engineer, Control Plane` with Headquarters `Remote
  (United States)` and again with `Remote (Canada)`, same guid, same feed). Rows are
  deduplicated by guid and the first copy wins, so the other variant is not returned.

### Pricing

Pay per event: a small **actor start** fee plus one **job scraped** event per row. Set
`maxItems` to cap spend.

### Limits

- Only what the feeds list: the current board, not archived postings, and not every
  posting on the site (the all-jobs feed in particular is a sample).
- A snapshot: postings expire (`expires_at`) and are added through the day.
- Not affiliated with We Work Remotely.

# Actor input Schema

## `categories` (type: `array`):

Category feed slugs to read, one request each. Leave empty for the all-jobs feed, which holds only the newest ~10 jobs per category (89 jobs). Category feeds are deeper (up to 89 jobs). Slugs, as on weworkremotely.com: remote-customer-support-jobs, remote-design-jobs, remote-product-jobs, remote-full-stack-programming-jobs, remote-front-end-programming-jobs, remote-back-end-programming-jobs, remote-devops-sysadmin-jobs, remote-sales-and-marketing-jobs, remote-management-and-finance-jobs, all-other-remote-jobs. Note that the last one has no remote- prefix. A full feed URL also works. An unknown slug is reported in the log and costs nothing.

## `keywords` (type: `array`):

Optional. Keep only jobs that mention at least one of these terms (case-insensitive) in the title, company, category, skills or description: python, react, "customer success". Applied before the row cap, so filtered-out jobs are not billed. Leave empty to keep everything.

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

Most jobs to return across all the feeds of this run, after duplicates are removed and keywords applied. Each job is one billable event, so this is also a spending cap. The run stops fetching feeds once it is reached.

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

Apify proxy (automatic datacenter exits) is the default. We Work Remotely answered 200 from an Apify datacenter and a residential address on 2026-10-09, so it is not required, but a proxy lets a refused or timed-out request be retried from a fresh address.

## Actor input object example

```json
{
  "categories": [
    "remote-design-jobs"
  ],
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `descriptions` (type: `string`):

No description

# 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 = {
    "categories": [
        "remote-design-jobs"
    ],
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/weworkremotely-jobs").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "categories": ["remote-design-jobs"],
    "maxItems": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

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

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

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

```

## CLI example

```bash
echo '{
  "categories": [
    "remote-design-jobs"
  ],
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/weworkremotely-jobs --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superslowsloth/weworkremotely-jobs"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/DUgJ3kaBIAI6WVWfs/builds/sESLilHXMkxVbvh08/openapi.json
