# Remotive Scraper (`solidcode/remotive-scraper`) Actor

\[💰 $2.0 / 1K]  Extract remote job listings from Remotive — titles, companies, salaries, tags, locations, descriptions, and apply URLs. Filter across 30 categories, keywords, and 11 employment types.

- **URL**: https://apify.com/solidcode/remotive-scraper.md
- **Developed by:** [SolidCode](https://apify.com/solidcode) (community)
- **Categories:** Jobs, Automation, Developer tools
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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

## Remotive Scraper

Pull live remote-job listings from Remotive at scale — titles, companies, categories, tags, salaries, locations, seniority, full HTML descriptions, and direct apply URLs for every active posting on the board. Built for recruiting platforms, ATS integrators, and job-aggregator teams who need a fresh, structured remote-jobs feed without scraping HTML page-by-page or settling for a stale 22-job public sample.

### Why This Scraper?

- **Searches the same internal index Remotive's own site uses** — taps the live job catalog of over 100,000 active postings, not the cached 22-job public feed competing scrapers rely on.
- **30 categories and 11 employment types, mirrored from the live index** — Software Development, Artificial Intelligence, DevOps, Customer Service, Design, Sales, Product Management, Medical, Quality Assurance, and 21 more, crossed against Full-time, Contract, Freelance, Internship, Part-time, Temporary, Casual, Per-diem, Volunteer, Interim, and Other. Every filter value matches real jobs — no dead options in the dropdown.
- **Combine categories, keywords, and job types in a single run** — every combination is queried, results merged and deduplicated by job ID so you never pay for the same posting twice.
- **Optional HTML job descriptions** parsed from each posting's structured-data block — toggle off for roughly 30× faster runs when you only need title, company, salary, and apply URL.
- **Over 10,000 rows from a single category in one run** — set `maxResults: 0` and the run paginates a whole category end-to-end instead of stopping at page one, and it trims itself to what fits your run's time limit so you never pay for a timed-out half-result.
- **Seniority, location, and skill tags on every record** — plus the company's logo image URL whenever the employer uploaded one, and the pay range exactly as the employer wrote it (`usd 100,000 - 130,000 per year`, `OTE $25k - $35k`) on the roughly half of postings that disclose one. The rest come through blank, not padded with filler, so you can sort and filter on salary in a spreadsheet.
- **Discovery timestamp on every job, and the employer's own publication date whenever descriptions are on** — critical for tracking newly-posted roles in daily monitoring runs and filtering out anything older than your freshness window.
- **Single flat row per job — 16 leaf fields, plus the description when you ask for it** — no nested arrays, no JSON-decoding gymnastics. Every row carries the same columns, so daily runs append cleanly to the same Google Sheet, BigQuery table, Postgres table, or webhook payload.

### Use Cases

**Recruitment & Talent Sourcing**

- Build a daily watch list of newly-posted senior engineering roles in your target stack
- Aggregate remote contract openings into a candidate-matching pipeline
- Monitor specific companies' hiring velocity across categories

**Market Research & Hiring Trends**

- Track remote-hiring volume across 30 categories week over week
- Compare salary ranges between Software Development, Data, and Artificial Intelligence postings
- Map which seniority levels dominate each category in the current market

**Job-Aggregator & Job-Board Platforms**

- Power a niche remote-jobs vertical with a fresh feed and proper deduplication
- Re-publish remote opportunities tagged for a specific framework (`react`, `python`, `kubernetes`)
- Backfill an internal job database with historical remote postings

**Career Services & Coaching**

- Curate weekly remote-job digests for clients searching by category and job type
- Surface internships and entry-level roles for early-career students
- Build personalized alerts for freelance and contract opportunities

**Personal Job-Search Automation**

- Pipe new full-time postings in your specialty into Slack or email
- De-duplicate listings across multiple keyword searches into one clean spreadsheet
- Filter to part-time or freelance roles only when running a side-income search

### Getting Started

#### Quick Start — Browse Today's Listings

The simplest possible run — pull 100 fresh remote jobs across all categories:

```json
{
    "maxResults": 100
}
```

#### Filter by Category and Job Type

Software Development full-time roles only:

```json
{
    "categories": ["software-development"],
    "jobTypes": ["full-time"],
    "maxResults": 500
}
```

#### Multi-Filter Search with Keywords

Combine categories, multiple keywords, and job types — every combination is queried and the results merged by unique job ID:

```json
{
    "categories": ["software-development", "artificial-intelligence", "data"],
    "searchTerms": ["python", "kubernetes", "machine learning"],
    "jobTypes": ["full-time", "contract"],
    "includeDescription": true,
    "maxResults": 2000
}
```

#### Full-Catalog Dump (Single Category)

Set `maxResults: 0` to exhaust an entire category — useful for one-time market-research snapshots:

```json
{
    "categories": ["customer-service"],
    "includeDescription": false,
    "maxResults": 0
}
```

### Input Reference

#### Filters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `categories` | string\[] | `[]` | Filter by job category. Leave empty to fetch jobs from all 30 categories. Each selected category triggers a separate query; results are unioned and deduplicated. |
| `searchTerms` | string\[] | `[]` | Free-text keywords like `python`, `react`, or `senior`. Each term is searched separately; results are unioned and deduplicated. Combine with categories to refine. |
| `jobTypes` | string\[] | `[]` | Filter by employment type. Empty = include all 11 types. |

#### Options

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `includeDescription` | boolean | `false` | Fetch the full HTML description body for each job. Leave off to run faster and cheaper if you only need title, company, salary, location, and apply URL. |
| `maxResults` | integer | `1000` | Maximum total jobs to save. Set to `0` for unlimited (returns the entire filtered catalog — can be tens of thousands of jobs). |

#### Category and Job Type Values

Categories accept any of these slugs: `software-development`, `customer-service`, `design`, `marketing`, `sales`, `product`, `project-management`, `artificial-intelligence`, `data`, `devops`, `finance`, `human-resources`, `qa`, `writing`, `legal`, `medical`, `education`, `account-management`, `business-development`, `communications`, `compliance`, `engineering`, `information-technology`, `knowledge-management`, `operations`, `research`, `strategy`, `supply-chain`, `travel-hospitality`, `all-others`.

Job types accept any of: `full-time`, `contract`, `part-time`, `freelance`, `internship`, `temporary`, `casual`, `per-diem`, `volunteer`, `interim`, `other`. These are exactly the employment types the live index uses, so every one of them matches real jobs — though `interim`, `volunteer`, `per-diem`, `casual`, and `freelance` are rare enough on the board that combining them with a narrow category can legitimately return only a handful of rows.

### Output

Each result is a flat job record. Here's a representative row with `includeDescription: true`:

```json
{
    "jobId": "1899437",
    "title": "Senior Backend Engineer (Python)",
    "companyName": "Acme Remote",
    "companyLogo": "https://remotive.com/job_board/job/logo/1899437",
    "category": "software-development",
    "categoryName": "Software Development",
    "tags": ["python", "django", "postgresql", "aws"],
    "jobType": "full-time",
    "seniority": "senior",
    "location": "Worldwide",
    "salary": "$120,000 - $160,000 USD",
    "publicationDate": "2026-08-11 20:18:02",
    "discoveredOn": "2026-08-11 20:18:02",
    "scrapedAt": "2026-08-14T18:59:26Z",
    "url": "https://job-boards.greenhouse.io/acmeremote/jobs/4512207",
    "remotiveUrl": "https://remotive.com/remote-jobs/software-development/senior-backend-engineer-python-4512207",
    "description": "<p>We're hiring a senior backend engineer to join our distributed team...</p>"
}
```

#### Job Fields

| Field | Type | Description |
|-------|------|-------------|
| `jobId` | string | Unique Remotive job identifier |
| `title` | string | Job title as posted |
| `companyName` | string | Hiring company name |
| `companyLogo` | string | URL to the company's logo image, or `null` when the employer never uploaded one (about 1 job in 10) |
| `category` | string | Category slug (e.g. `software-development`) |
| `categoryName` | string | Category display name (e.g. `Software Development`) |
| `tags` | string\[] | Skills and technology tags attached to the posting |
| `jobType` | string | Employment type (`full-time`, `contract`, `freelance`, etc.) |
| `seniority` | string | Seniority level when present, otherwise `unspecified` |
| `location` | string | Allowed locations, comma-joined (e.g. `Worldwide`, `USA, Canada`) |
| `salary` | string | Pay exactly as the employer wrote it, or `null` when the posting doesn't disclose one (roughly half don't) |
| `publicationDate` | string | When the employer published the job, formatted `YYYY-MM-DD HH:MM:SS` exactly as Remotive reports it, with no time zone attached. It lives on the job's own listing page, so it fills only when `includeDescription` is on and is `null` otherwise — use `discoveredOn` for freshness in fast runs |
| `discoveredOn` | string | When Remotive indexed the job, formatted `YYYY-MM-DD HH:MM:SS` as Remotive reports it, with no time zone attached |
| `scrapedAt` | string | ISO 8601 UTC timestamp of when this row was extracted (e.g. `2026-08-14T18:59:26Z`) |
| `url` | string | Direct apply link — usually the employer's own careers page or application form |
| `remotiveUrl` | string | The job's listing page on Remotive |
| `description` | string | Full HTML description body — present only when `includeDescription` is on |

### Tips for Best Results

- **Toggle off descriptions for first-pass discovery.** Set `includeDescription: false` for fast catalog sweeps, then re-run on just the IDs you care about with descriptions on. The detail-fetch is the slow step — skipping it speeds runs roughly 30×.
- **Use multiple short `searchTerms` instead of one long string.** Each term is searched independently and results are unioned, so `["python", "fastapi", "django"]` casts a wider net than the single string `"python fastapi django"`.
- **Combine categories with searchTerms for the cleanest signal.** A `categories: ["data"]` + `searchTerms: ["sql"]` run finds data jobs that mention SQL — far more precise than searching SQL across all categories.
- **Set `maxResults: 0` only when you genuinely want the full catalog.** The biggest categories (Sales, Software Development) run to five figures of postings on their own; pair with `includeDescription: false` to keep cost predictable.
- **Schedule daily runs and dedupe by `jobId` downstream.** Remotive's `discoveredOn` lets you filter to "new since yesterday" without re-fetching the entire catalog.
- **Use `jobTypes: ["contract", "temporary", "freelance"]`** when sourcing for short-term engagements. `contract` carries by far the most volume of the three, so include it even if freelance is what you really want — the rarer types alone return only a trickle.
- **Watch the per-page progress logs.** Each page reports how many jobs it returned, how many were new after deduplication, and the running total, so you can estimate run time and cost mid-execution. On a multi-filter run every page also names the category, keyword, or job type it came from, so you can see which filter is actually carrying your results.

### Pricing

**From $1.70 per 1,000 results** — one flat rate whether you pull listings only or full HTML descriptions. No compute or time-based charges: you pay for the jobs that land in your dataset, plus a fractional-cent fee each time a run starts. Bronze, Silver, and Gold subscribers pay progressively less, and the table below shows the total cost at each discount tier.

| Results | No discount | Bronze | Silver | Gold |
|---------|-------------|--------|--------|------|
| 100 | $0.20 | $0.19 | $0.18 | $0.17 |
| 1,000 | $2.00 | $1.90 | $1.80 | $1.70 |
| 10,000 | $20.00 | $19.00 | $18.00 | $17.00 |
| 100,000 | $200.00 | $190.00 | $180.00 | $170.00 |

A "result" is one job row in the output dataset. Duplicate postings across your categories and keywords are merged before they reach the dataset, so you never pay twice for the same job. Standard Apify platform fees (storage, data transfer) apply on top.

### Integrations

Export data in JSON, CSV, Excel, XML, or RSS. Connect to 1,500+ apps via:

- **Zapier** / **Make** / **n8n** — Workflow automation
- **Google Sheets** — Direct spreadsheet export
- **Slack** / **Email** — Notifications when new jobs hit your filters
- **Webhooks** — Trigger custom APIs on run completion
- **Apify API** — Full programmatic access

### Legal & Ethical Use

This actor is designed for legitimate recruitment, market research, and personal job-search use cases. Users are responsible for complying with applicable laws and Remotive's Terms of Service, including respectful request rates and any restrictions on republishing job content. Do not use extracted data for spam, harassment, or any illegal purpose. Be considerate of candidate and employer privacy when handling contact information surfaced through job postings.

# Actor input Schema

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

Filter by job category. Leave empty to fetch jobs from all 30 categories. Each selected category triggers a separate API call; results are unioned and deduplicated.

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

Free-text keywords like 'python', 'react', or 'senior'. Each term is queried separately; results are unioned and deduplicated. Combine with categories to refine.

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

Filter by employment type. Empty = include every type. These are exactly the employment types Remotive's live job index uses, so every option here matches real jobs — but a few (Interim, Volunteer, Per-diem, Casual, Freelance) are genuinely rare on the board, so pairing one of them with a category or keyword can legitimately return very few jobs or none.

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

Fetch the full job description body for each result. This needs one extra page fetch per job, so it runs far slower — roughly 0.7 jobs per second, against many jobs per second with it off. If your Maximum Results is larger than the run can finish in its time limit, it is automatically trimmed to what fits and the run tells you. It is also the only way to get the Publication Date field, which lives on the job's own listing page and is left empty otherwise — use Discovered On, which is always filled, if you just need to spot new postings. Disable to run much faster and cheaper if you only need title, company, salary, location, and apply URL.

## `maxResults` (type: `integer`):

Maximum total jobs to save. Set to 0 for unlimited (returns the entire filtered catalog — can be tens of thousands of jobs depending on your filters). Whatever you ask for is checked against the run's time limit first, and a value that cannot finish in time (including 0) is automatically trimmed to what fits, so you never get a timed-out run with a half-finished result you still pay for. In the default 1-hour run time limit that means roughly 45,000 jobs, or roughly 2,000 with 'Include Full Description' turned on, since descriptions run at about 0.7 jobs per second. Raise the run's timeout, narrow your filters, or split the work across runs to collect more.

## Actor input object example

```json
{
  "categories": [],
  "searchTerms": [],
  "jobTypes": [],
  "includeDescription": false,
  "maxResults": 1000
}
```

# Actor output Schema

## `overview` (type: `string`):

Table of jobs with key fields like title, company, category, location, and apply URL.

## `details` (type: `string`):

Full per-job rows including description, tags, salary, and timestamps. The Published column fills only when Include Full Description is on; Discovered is always filled.

# 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": [],
    "searchTerms": [],
    "jobTypes": [],
    "includeDescription": false,
    "maxResults": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidcode/remotive-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 = {
    "categories": [],
    "searchTerms": [],
    "jobTypes": [],
    "includeDescription": False,
    "maxResults": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("solidcode/remotive-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 '{
  "categories": [],
  "searchTerms": [],
  "jobTypes": [],
  "includeDescription": false,
  "maxResults": 1000
}' |
apify call solidcode/remotive-scraper --silent --output-dataset

```

## MCP server setup

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