# Himalayas Remote Jobs Scraper (`devilscrapes/himalayas-remote-jobs-scraper`) Actor

Walk himalayas.app's public remote-jobs feed via cursor pagination and filter results by keyword, category, or country. The feed's own search/category params are server-side no-ops, so filtering runs client-side against real job fields, returning typed rows with null-normalized salary data.

- **URL**: https://apify.com/devilscrapes/himalayas-remote-jobs-scraper.md
- **Developed by:** [DevilScrapes](https://apify.com/devilscrapes) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

<div align="center">
  <img src=".actor/icon.svg" width="160" alt="Devil Scrapes mark" />

## Himalayas Remote Jobs Scraper

**$1.70 / 1 000 results**  ·  pay only for results  ·  no credit card to try

We walk himalayas.app's live jobs feed and hand you back only the roles that actually match — the devil's in the filtering.

Pull himalayas.app's live remote-jobs feed with real keyword, category, and
country filtering — built for recruiters, sourcers, and job-board operators
who re-run this on a schedule and need clean, typed rows every time.

</div>

***

### 🎯 What this scrapes

[himalayas.app](https://himalayas.app) publishes its remote-job listings
through a public, keyless JSON feed at `/jobs/api`, paginated with a
`nextCursor` token the feed itself documents as the preferred way to page
through results (the older `offset` parameter is deprecated). We walk that
cursor chain page by page and hand back every job that matches your filters.

Here's the part that trips up a naive integration: the feed also accepts
`search=`, `category=`, and `categories=` query parameters — and none of
them do anything server-side. We verified this directly: a `search=`
request for an obscure term returned a full page of completely unrelated
job titles. So this Actor filters **client-side**, against the real title,
excerpt, description, category tags, and location-restriction fields on
each job — the only place filtering actually happens on this feed.

### 🔥 Features

- 🔁 **Correct cursor-pagination.** himalayas.app's bare first-page response
  is not stable across repeated calls — we only ever treat the verified
  cursor chain as authoritative, never the first page in isolation.
- 🎯 **Real client-side filtering.** Keyword, category, and country filters
  actually narrow the results, unlike the feed's own no-op query params.
- 🧊 **Null-normalized salary data.** When himalayas.app has no salary or
  currency for a listing, you get a clean `null` — never a fake `0` or `""`
  standing in for missing data.
- 🛡️ **Fault isolation per job.** A single malformed record is skipped and
  logged; it never aborts your run or costs you the rest of the results.
- 🔁 **We retry with exponential backoff** on `408 / 429 / 503` and honour
  `Retry-After`, rotating browser fingerprints across attempts.
- 💰 **You pay only for results that land.** No data, no charge beyond the
  small warm-up fee.

### 💡 Use cases

- **Recruiter & sourcer pipelines** — pull fresh remote roles into your ATS
  or CRM on a daily schedule, pre-filtered to the categories you place in.
- **Job-board aggregation** — add Himalayas coverage to a multi-source
  remote-jobs board alongside other feeds.
- **Labor-market research** — track category and country demand trends in
  the remote-hiring market over time.
- **Hiring-intent signals** — SDR/BD teams reading newly-opened remote reqs
  as a proxy for company growth and headcount plans.

### ⚙️ How to use it

1. Click **Try for free** at the top of the page.
2. Optionally set `keywords`, `categories`, and/or `countries` to narrow
   the feed to what you care about — leave any of them empty to skip that
   filter entirely.
3. Set `max_results` to cap how many matching jobs you want.
4. Click **Start**. Matching rows stream into the run's dataset as they're
   found.
5. Export from **Storage → Dataset** as JSON, CSV, or Excel — or fetch
   programmatically via the Apify API.

### 📥 Input

| Field | Type | Default | Description |
|---|---|---|---|
| `keywords` | array of string | `[]` | Keep jobs whose title, excerpt, or description contains ANY of these (case-insensitive substring match). |
| `categories` | array of string | `[]` | Keep jobs whose category tags include ANY of these (case-insensitive). |
| `countries` | array of string | `[]` | Keep jobs whose location restrictions include ANY of these; jobs with no restriction (worldwide) always pass. |
| `max_results` | integer | `100` | Hard stop for the cursor walk (1–5000). |
| `proxyConfiguration` | object | `{"useApifyProxy": false}` | Standard Apify Proxy configuration — not required for this keyless target, but available if you ever want it. |

```json
{
  "keywords": ["engineer", "marketing"],
  "categories": [],
  "countries": [],
  "max_results": 20,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

### 📤 Output

| Field | Description |
|---|---|
| `id` | Stable canonical URL for the posting — the unique row id. |
| `title` / `company_name` / `company_slug` / `company_logo_url` | Core listing identity. |
| `url` | Direct application URL. |
| `excerpt` / `description_html` | Short summary and full HTML job description. |
| `employment_type` / `seniority` / `categories` / `parent_categories` | Classification tags. |
| `location_restrictions` / `timezone_restrictions` | Where the role is open to (empty = worldwide). |
| `min_salary` / `max_salary` / `salary_period` / `currency` | Null-normalized salary data. |
| `published_at` / `expires_at` / `scraped_at` | ISO-8601 timestamps. |

```json
{
  "id": "https://himalayas.app/companies/example-co/jobs/senior-backend-engineer",
  "title": "Senior Backend Engineer",
  "company_name": "Example Co",
  "company_slug": "example-co",
  "url": "https://himalayas.app/companies/example-co/jobs/senior-backend-engineer",
  "employment_type": "Full Time",
  "seniority": ["Senior"],
  "categories": ["Backend-Engineering", "API-Development"],
  "location_restrictions": [],
  "min_salary": 120000.0,
  "max_salary": 160000.0,
  "salary_period": "yearly",
  "currency": "USD",
  "published_at": "2026-09-10T00:00:00+00:00",
  "expires_at": null,
  "scraped_at": "2026-09-10T00:00:00+00:00"
}
```

### 💰 Pricing

Pay-Per-Event — no subscription, no minimum.

| Event | Price |
|---|---|
| Actor start | $0.20 flat |
| Result | $0.0015 / row (→ $1.50 / 1 000 results) |

Example: 1,000 results at the rates above ≈ **$1.70** total ($0.20 start +
$1.50 for the rows). Apify gives every new account $5 of free credit to try
it with.

### 🚧 Limitations

- himalayas.app's `search=`/`category=`/`categories=` query parameters are
  confirmed no-ops server-side — filtering always happens client-side here,
  which means a very narrow filter combination walks more of the feed
  before it fills `max_results` than a server-side filter would.
- The cursor walk has an internal safety page cap so an extremely narrow
  filter that matches almost nothing can't run away on cost; if you hit it,
  widen your filters or raise `max_results` in smaller steps.
- We don't control himalayas.app's own posting freshness or how quickly
  expired listings drop off the feed.

### ❓ FAQ

**Does this use himalayas.app's `search` parameter?**
No — we verified it's a server-side no-op and filter client-side instead,
against the job's actual title, excerpt, description, category, and
location fields. That's the only way filtering reliably works on this feed.

**Will I get duplicate jobs across runs?**
No. himalayas.app's cursor pagination is documented as never returning the
same job twice within a single walk, and we never re-issue a stale cursor.

**What happens if a listing is missing a field?**
Missing salary/currency data comes through as `null`, not `0` or an empty
string — you can trust `null` to mean "not published," not "confirmed
zero."

**What if the feed briefly returns nothing for my filters?**
The run still completes successfully with zero rows and a status message
telling you what was searched — a real "no matches" isn't treated as a
failure.

### Your feedback

Found an edge case in the feed, or want a field we don't emit yet? Reach
us at [apify.com/DevilScrapes](https://apify.com/DevilScrapes) — we read
every message.

# Actor input Schema

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

Keep jobs whose title, excerpt, or description contains ANY of these keywords (case-insensitive substring match). Leave empty to skip this filter.

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

Keep jobs whose category tags include ANY of these (case-insensitive substring match). Leave empty to skip this filter.

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

Keep jobs whose location restrictions include ANY of these countries (case-insensitive substring match). Jobs with no location restriction (worldwide) always pass. Leave empty to skip this filter.

## `max_results` (type: `integer`):

Hard stop for the cursor walk — the Actor stops once this many matching jobs have been collected.

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

Apify Proxy configuration. Not required for this keyless, anti-bot-free target, but the field is available so it can be turned on if the target ever changes behavior.

## Actor input object example

```json
{
  "keywords": [
    "engineer",
    "marketing"
  ],
  "categories": [],
  "countries": [],
  "max_results": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

All dataset items as JSON.

## `datasetItemsCsv` (type: `string`):

Same data exported to CSV.

## `datasetView` (type: `string`):

Open the run dataset in the Console.

# 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 = {
    "keywords": [
        "engineer",
        "marketing"
    ],
    "max_results": 20,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/himalayas-remote-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 = {
    "keywords": [
        "engineer",
        "marketing",
    ],
    "max_results": 20,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/himalayas-remote-jobs-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 '{
  "keywords": [
    "engineer",
    "marketing"
  ],
  "max_results": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call devilscrapes/himalayas-remote-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devilscrapes/himalayas-remote-jobs-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/00PwtRhlc5677M1MV/builds/7CCgYnBo0RhDrugKH/openapi.json
