# Himalayas Remote Jobs Scraper (Official API) (`ataism/himalayas-jobs`) Actor

Remote jobs from the official Himalayas API: full text, yearly salary, timezone filter, only-new alerts. Not for job boards. $0.70 per 1,000 jobs.

- **URL**: https://apify.com/ataism/himalayas-jobs.md
- **Developed by:** [Kaizoku Data](https://apify.com/ataism) (community)
- **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 saveds

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

## Himalayas Remote Jobs Scraper (Official API)

Get remote jobs from [Himalayas](https://himalayas.app) as clean, structured data: **full description included**, company, seniority, employment type, country and timezone limits, and salary **normalised to a yearly figure**. The Actor uses Himalayas' **official public jobs API**, so it needs no browser, no proxy and no API key, and it doesn't break when the website layout changes.

**Price: $0.70 per 1,000 jobs ($0.0007 per job), full description included.**

*For research, alerts and internal tools; not for job boards (Himalayas terms). Details under* Terms of use *below.*

- **Yearly salary on one scale:** hourly, weekly, monthly and annual pay converted to `yearlyMin` / `yearlyMax` when the period is known.
- **Filters that matter for remote work:** search text, countries, worldwide only, timezones (UTC offsets), seniority, employment type, company.
- **Only new jobs:** a scheduled run returns (and charges for) only jobs you haven't seen, remembered for 120 days. Expired jobs are dropped and never charged.
- **JSON, CSV or Excel, or the Apify API.** Emails and phone numbers are removed from descriptions; every row carries Himalayas' apply link and source credit.
- **Quick start:** press *Start*. The default input returns the 20 newest-updated jobs in a few seconds for $0.014.

### What you can do with it

- **Market research.** Which companies hire remotely, in which categories, countries, seniority levels and pay ranges.
- **Salary analysis.** `salary.yearlyMin` / `yearlyMax` put hourly, weekly, monthly and annual pay on one scale (when the period is known).
- **Alerts and internal tools.** Schedule it with **Only new jobs** turned on and send the new ones to your own Slack, email or dashboard.

### Read this first: no redistribution

> Himalayas' API terms forbid redistributing its jobs to other job boards, aggregators or job search engines. This Actor is for **research, analysis, alerts and internal tools**. Don't use it to feed a job board, an aggregator or a job search engine. See *Terms of use* below.

### Input

| Field | What it does |
|---|---|
| **Search text** | Free-text query, e.g. `react engineer`. Empty reads the full feed, newest-updated first. |
| **Countries** | Jobs open to applicants in these countries (`US`, `Germany`). One search per country. |
| **Worldwide jobs only** | Only jobs with no country restriction. |
| **Seniority** | Entry-level, Mid-level, Senior, Manager, Director, Executive. |
| **Employment types** | Full Time, Part Time, Contractor, Temporary, Intern, Volunteer, Other. |
| **Companies (slugs)** | e.g. `linear`. |
| **Timezones** | UTC offsets such as `1`, `-5`, `5.5`, `UTC+1` or `UTC+05:30`. One search per timezone. |
| **Sort order** | relevant, recent, salary, company name, number of open roles. Choosing one uses the search endpoint. |
| **Max jobs** | Default 20, up to 5,000 (searches are capped at 5,000 results by Himalayas). |
| **Include expired jobs** | Off by default: expired jobs are dropped, as Himalayas' terms require. |
| **Include full description** | HTML plus plain text. Turn off for a smaller dataset. |
| **Only new jobs since my last run** | Remembers the job ids it already returned to you (120 days) and skips them. |

#### Example inputs

**1. Daily alert: senior data jobs open worldwide** (schedule it daily; with *Only new jobs* each run returns only jobs you haven't seen):

```json
{
  "query": "data engineer",
  "seniority": ["Senior"],
  "worldwideOnly": true,
  "sort": "recent",
  "maxItems": 200,
  "onlyNew": true
}
```

**2. Salary research: backend jobs, highest pay first** (descriptions off for a lighter dataset; use `salary.yearlyMin` / `yearlyMax`):

```json
{
  "query": "backend",
  "sort": "salaryDesc",
  "includeDescription": false,
  "maxItems": 1000
}
```

**3. Full-time jobs open in your timezone (UTC+1):**

```json
{
  "timezones": ["1"],
  "employmentTypes": ["Full Time"],
  "sort": "recent",
  "maxItems": 100
}
```

The default input (`{}`, 20 jobs) is one request and finishes in a few seconds. Large `Max jobs` values need a longer run timeout: 5,000 jobs is 250 requests at 1.2 s each, about 5 minutes or more, so raise the timeout in the run options (the suggested 300 s fits the default run only).

### Output

One item per job:

```json
{
  "id": "https://himalayas.app/companies/acme/jobs/senior-backend-engineer",
  "title": "Senior Backend Engineer",
  "companyName": "Acme",
  "companySlug": "acme",
  "companyLogo": "https://cdn-images.himalayas.app/…",
  "employmentType": "Full Time",
  "seniority": ["Senior"],
  "categories": ["Backend-Engineer"],
  "parentCategories": ["Software Engineering"],
  "locationRestrictions": ["Germany", "Poland"],
  "worldwide": false,
  "timezoneRestrictions": [0, 1, 2],
  "salary": { "min": 80, "max": 120, "currency": "USD", "period": "hourly", "yearlyMin": 166400, "yearlyMax": 249600 },
  "excerpt": "…",
  "description": "<p>…</p>",
  "descriptionText": "…",
  "publishedAt": "2026-10-18T11:51:38.000Z",
  "expiresAt": "2026-12-17T11:51:37.000Z",
  "applicationLink": "https://himalayas.app/companies/acme/jobs/senior-backend-engineer",
  "sourceUrl": "https://himalayas.app/companies/acme/jobs/senior-backend-engineer",
  "source": "Himalayas",
  "attribution": "Source: Himalayas (https://himalayas.app/companies/acme/jobs/senior-backend-engineer)",
  "retrievedAt": "2026-10-18T13:38:52.452Z"
}
```

- `applicationLink` is exactly what the API sent. We never replace it with an employer or ATS link.
- `salary` is `null` when the posting states no pay (about 60% of jobs). `yearlyMin`/`yearlyMax` are filled only when the pay period is known: hourly x 2,080, weekly x 52, fortnightly x 26, monthly x 12, annual x 1.
- An empty `locationRestrictions` means the job is open worldwide; an empty `timezoneRestrictions` means any timezone.
- Every run also saves a `RUN_SUMMARY` record: requests, pages, jobs delivered, expired jobs dropped, Himalayas' own API notice, and any error.

You can export as JSON, CSV, Excel or XML, or read it through the Apify API.

**Fill rates** in our 2026-10-18 sample (215 jobs): title, company, employment type, seniority, categories, timezone, excerpt, description, dates and links 100%; location restrictions 92% (the rest are worldwide); parent categories 83%; company logo 89%; salary 36%.

### Pricing

Pay per event:

- **$0.0007 per job** saved to the dataset.
- Apify's standard tiny start fee per run.

1,000 jobs cost **$0.70**. A daily alert with *Only new jobs* typically returns a few dozen jobs, a couple of cents a day. Cap any run with Apify's *Max total charge*. The Actor stops at the cap, and jobs already delivered are remembered by *Only new jobs*, so you never pay twice for the same job. Expired jobs are dropped before they are delivered, so you are never charged for them.

### Use it via API, n8n or Make

For your own alerts, research and internal tools (not for feeding job boards, aggregators or job search engines; see *Terms of use*). Get an API token in Apify Console → *Settings* → *API & Integrations*, and keep it in an environment variable (`APIFY_TOKEN` below).

**cURL** (starts a run, waits for it and returns the jobs as JSON; this synchronous endpoint waits up to 5 minutes, which fits up to about 2,000 jobs; for more use the clients below):

```bash
curl -X POST "https://api.apify.com/v2/acts/ataism~himalayas-jobs/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query": "data engineer", "worldwideOnly": true, "sort": "recent", "maxItems": 50}'
```

**JavaScript** (`npm install apify-client`):

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('ataism/himalayas-jobs').call({
    query: 'data engineer',
    sort: 'recent',
    maxItems: 100,
    onlyNew: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const job of items) console.log(job.companyName, job.title, job.salary?.yearlyMin, job.applicationLink);
```

**Python** (`pip install "apify-client>=3"`):

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("ataism/himalayas-jobs").call(run_input={
    "query": "data engineer",
    "sort": "recent",
    "maxItems": 100,
    "onlyNew": True,
})
for job in client.dataset(run.default_dataset_id).iterate_items():
    print(job["companyName"], job["title"], job["applicationLink"])
```

(With apify-client versions before 3.0, `call()` returns a dict: use `run["defaultDatasetId"]`.)

**n8n** (new jobs to your own Slack channel):

1. Add a *Schedule Trigger* (daily; Himalayas refreshes its data about every 24 hours).
2. Add the **Apify** node, operation **Run an Actor and get dataset**, Actor `ataism/himalayas-jobs`, and paste your input JSON with `"onlyNew": true`.
3. Add a *Slack* (or email) node and post `title`, `companyName`, `applicationLink` and the `attribution` credit with its Himalayas link (terms 113(a) and (b)).

**Make:**

1. Add the **Apify** module **Run an Actor**, choose `ataism/himalayas-jobs`, paste the input JSON and set *Run synchronously* to *Yes*.
2. Add the **Apify** module **Get Dataset Items** and map the run's default dataset ID.
3. Send the rows to your own Slack, email or internal sheet, keeping `applicationLink` and the Himalayas credit. Schedule the scenario daily.

### Terms of use

This Actor uses the Himalayas Remote Jobs API and follows [Himalayas' Terms and Conditions for API Users](https://himalayas.app/terms) (section 113), which apply to you as well:

> 113. These terms apply when you use the Himalayas Remote Jobs API, including with an API key. You may use the API in personal and commercial integrations on these conditions:
>      (a) Every Apply action for a job from Himalayas must use the applicationLink supplied by the API. Do not replace it with a direct employer or applicant tracking system link.
>      (b) Clearly identify Himalayas as the source of the jobs, with a visible link to the Himalayas job or to himalayas.app.
>      (c) Do not redistribute jobs from Himalayas to other job boards, aggregators or job search engines.
>      (d) Cache job data where you can, stay within your request limits, and remove jobs once they expire.
>      (e) Keep your API keys private and use them only from your own servers.
>      (f) API keys grant read access to public job data only. We set and may change request limits, and we may suspend keys or accounts that break these terms. For higher limits or other uses, contact hi@himalayas.app.

How the Actor and you each follow it:

- **(a) Apply links.** Every row carries `applicationLink` unchanged. If you build anything with an Apply button, use that link.
- **(b) Source credit.** Every row has `source: "Himalayas"`, a `sourceUrl` (the Himalayas job page) and an `attribution` string. If you show these jobs to anyone, show the credit and the link.
- **(c) No redistribution.** **Do not publish these jobs on another job board, aggregator or job search engine** (Google Jobs and LinkedIn Jobs included). Use the data for research, analysis, alerts and internal tools only. You are responsible for how you use the output.
- **(d) Caching and expiry.** Expired jobs are dropped by default (`includeExpired` is off). *Only new jobs* avoids re-delivering what you've seen and stops reading once it reaches jobs you already have. Requests are sequential with a pause between them. Himalayas' [API docs](https://himalayas.app/docs/remote-jobs-api) say the data is cached and refreshed every 24 hours, so running more often than daily is unlikely to help. If you store jobs yourself, delete them once `expiresAt` has passed.
- **(e) Keys.** Himalayas says API keys are for use "only from your own servers", so this Actor has no key input and runs keyless on Himalayas' public allowance. Don't ask us to add a key.

We're not affiliated with Himalayas.

### Limits and errors

No key is used: keyless callers share one public allowance with everyone else, and Himalayas doesn't publish the exact figure. The Actor makes one request per 20 jobs, sequentially, 1.2 s apart. If Himalayas answers 429 (too many requests), it waits (the `Retry-After` time if given, otherwise an increasing pause) and retries; if it still fails it ends the run with a clear message, keeping what was already delivered. A run reads at most 300 requests (6,000 jobs scanned); if it hits that cap it logs a warning and says so in `RUN_SUMMARY` (`stoppedBy: "maxPages"`).

### Privacy

Job listings only. To avoid passing on personal data, the Actor **removes email addresses and phone numbers** from titles, excerpts and descriptions (you'll see `[email removed]` or `[phone removed]`). In our 2026-10-18 sample this changed 4 of 215 descriptions. Free-text tricks such as "jane (at) example (dot) com" may not be caught.

### Reliability

- Reads the official JSON API with cursor paging (the deprecated `offset` isn't used), so website redesigns don't break it.
- A daily self-test checks the live API against a recorded baseline: field names and fill rates. If Himalayas changes its own release notes, we're told and look at it, but nothing breaks. We fix breakages fast.
- **Only new jobs** saves its memory at least every 5 jobs. If a run is killed hard (for example an out-of-memory crash), up to 4 jobs delivered just before that may be delivered, and charged, again on the next run. Normal aborts and migrations save everything.

### FAQ

**How many jobs are there?** About 128,000 in the full feed on 2026-10-18, including many that are old but not yet expired. The newest-updated come first; use filters to narrow down.

**Why did my run stop before Max jobs?** Either the results ran out, the *Max total charge* was reached, or the 300-request cap was hit (see *Limits and errors*). `RUN_SUMMARY.stoppedBy` says which.

**Does Max jobs count expired jobs?** No. Only delivered (non-expired, and with *Only new*, unseen) jobs count.

**Can I put the jobs on my job board?** No. That is exactly what Himalayas' terms forbid.

**Something's wrong?** Open an issue on the Actor's Issues tab. We aim to reply within 2 working days.

# Actor input Schema

## `query` (type: `string`):

Free-text search, e.g. "react engineer". Leave empty to read the full newest-first feed.

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

Keep jobs open to applicants in these countries (ISO code or name, e.g. "US", "Germany"). One search is made per country, so more countries means more requests.

## `worldwideOnly` (type: `boolean`):

Only jobs with no country restriction.

## `seniority` (type: `array`):

Experience levels to keep. Leave empty for all.

## `employmentTypes` (type: `array`):

Leave empty for all.

## `companies` (type: `array`):

Himalayas company slugs, e.g. "linear" (the last part of himalayas.app/companies/linear).

## `timezones` (type: `array`):

UTC offsets, e.g. "1", "-5", "5.5", "UTC+1" or "UTC+05:30" (checked against the live API). One search is made per timezone.

## `sort` (type: `string`):

Only for filtered searches. With no filter and no sort, the full feed is read newest-updated first. Choosing a sort switches to the search endpoint.

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

Stop after this many jobs. Searches return at most 5,000 results.

## `includeExpired` (type: `boolean`):

Off by default: Himalayas' terms require removing jobs once they expire.

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

Adds the description as HTML and as plain text. Turn off for a smaller dataset.

## `onlyNew` (type: `boolean`):

Skip jobs this Actor already returned to you (remembered for 120 days in a key-value store named "himalayas-jobs-state" in your account). Ideal for scheduled alerts: you only pay for new jobs, and the Actor stops reading once it reaches jobs you've already seen.

## `stateKey` (type: `string`):

Use different names to keep separate only-new memories for different saved tasks.

## `mode` (type: `string`):

Internal: "selfTest" runs the maintainer health check and saves no jobs.

## Actor input object example

```json
{
  "worldwideOnly": false,
  "maxItems": 20,
  "includeExpired": false,
  "includeDescription": true,
  "onlyNew": false,
  "stateKey": "default",
  "mode": "scrape"
}
```

# Actor output Schema

## `results` (type: `string`):

Himalayas remote job listings, one item per row (JSON, CSV, Excel or XML).

## `runSummary` (type: `string`):

Counts, expired jobs dropped, API notice and errors for this run.

# 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 = {
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("ataism/himalayas-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 = { "maxItems": 20 }

# Run the Actor and wait for it to finish
run = client.actor("ataism/himalayas-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 '{
  "maxItems": 20
}' |
apify call ataism/himalayas-jobs --silent --output-dataset

```

## MCP server setup

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