Remote Jobs API & Scraper – Jobicy, Remote OK, Only New
Pricing
$1.00 / 1,000 new remote jobs
Remote Jobs API & Scraper – Jobicy, Remote OK, Only New
Remote jobs from sources licensed for commercial republishing (Jobicy, Remote OK). Deduplicated, yearly salary, filters, only-new mode. Pay per new job.
Pricing
$1.00 / 1,000 new remote jobs
Rating
0.0
(0)
Developer
Francesco Antonio Russo
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Remote job postings from sources that allow commercial republishing - Jobicy and Remote OK - in one clean feed. Unlike feeds built on boards whose terms bar passing jobs on (Himalayas, for example: "Do not pass them on to third parties ... including through feeds"), every source here is used within its published terms, with attribution built into each row. Same fields whatever the source, duplicates merged, salary normalized to a yearly figure, and an "only new" mode that makes a daily job feed a two-click job. You pay $0.001 per NEW job delivered - nothing for duplicates, jobs you already got, or jobs your filters drop.
Built for people who need remote jobs as data: job-board and newsletter owners, recruiters watching a niche, career coaches, salary researchers, and anyone feeding a Slack channel, a Google Sheet, an n8n / Make / Zapier flow or an AI agent with fresh remote openings.
What you get
- Two public job boards, one schema. Jobicy (the last 7 days, ~1,100 postings at the time of writing) and Remote OK (~100 current postings). Both are read through their public APIs: no scraping, no login, no proxy.
- Duplicates merged. The same posting on both boards (same company + title, or same link) is delivered once, with the other listing under
alsoListedOn. - Only new postings. Switch on
onlyNewand every run returns just what appeared since the last one. The memory is stored in your Apify account, saved after each delivered batch, and can be split per task withmemoryKey. - Salary you can compare. Hourly, daily, weekly and monthly figures become a yearly figure, and the row says whether that figure was
salaryYearlyEstimated. Currencies are kept as published (never converted). - Real filters. Keywords (any of, whole words), excluded keywords, category, contract type, experience level, allowed countries or regions, only with salary, minimum yearly salary, posted after.
- Honest labels. Seniority that is only a guess from the title is flagged (
seniorityInferred). A bare "Remote" isunspecified, never silently "worldwide". - No personal data. Company and posting data only. No recruiter field is extracted. The description is the employer's own text and may still mention a name; e-mail addresses in it, including disguised ones such as
name(at)domain.com, are replaced by[email removed]. - Attribution built in. Every row carries the posting
urlon its source board and anattributiontext, as the sources' terms require.
Pricing - pay per new job
| Event | Price | When it is charged |
|---|---|---|
job - Remote job posting | $0.001 | Once for each posting delivered to your dataset (after it is stored). |
Free: postings merged as duplicates, postings dropped by your filters, postings already delivered in an earlier run (onlyNew), postings a source could not give us, and a source that failed. If you set a maximum cost per run, the Actor stops delivering the moment the limit is reached and tells you in the SUMMARY how many postings it held back (with onlyNew they stay "new" for the next run). Example: 150 new postings = $0.15. A full unfiltered first run of ~1,200 postings = about $1.20.
Quick start
- Open the Actor and press Start: you get up to 25 current remote jobs (the prefilled
maxItems). - Add filters, for example
keywords: ["python", "django"],countries: ["DE"],onlyWithSalary: true. - For a daily feed: set
onlyNew: true, removemaxItems, and Schedule the Actor once a day. Every run returns only the postings you have not seen.
Input
| Field | Title | Default | What it does |
|---|---|---|---|
sources | Sources | ["jobicy", "remoteok"] | Which boards to read. Both are public APIs that need no key. Leave both selected for the widest feed; duplicates between them are merged. |
keywords | Keywords (any of) | empty | Keep a posting if ANY of these words or phrases appears as a whole word in its title, company, tags or description ("java" does not match "javascript"). Leave empty for no keyword filter. |
excludeKeywords | Exclude keywords | empty | Drop a posting if any of these appears. Same whole-word rule as above. |
keywordsInTitleOnly | Match keywords in the title only | false | Look for the keywords in the job title only, not in the description. Fewer, more precise results. |
categories | Categories | empty | Keep only these categories. Every posting gets one shared category whatever its source calls it. Leave empty for all. |
employmentTypes | Contract types | empty | Keep only these. Postings whose source does not state a type (most of Remote OK) are left out when you pick one. |
seniorities | Experience levels | empty | Keep only these. Jobicy publishes a level; for Remote OK it is guessed from the title (see seniorityInferred). Postings with no level are left out when you pick one. |
countries | Allowed countries | empty | Keep postings someone living in one of these countries can take. Use ISO codes (US, DE), names (Germany) or regions (Europe, EU, EMEA, APAC, LATAM, North America). Worldwide postings are kept too (see below). The sources do not publish time zones, so there is no time-zone filter. |
includeWorldwide | Include worldwide postings | true | With "Allowed countries" set, also keep postings that say "Anywhere" or "Worldwide". |
includeUnspecifiedLocation | Include postings with no usable location | false | With "Allowed countries" set, also keep postings whose location is empty, a bare "Remote" or a city we cannot place. Off by default: we do not guess. |
onlyWithSalary | Only postings with a salary | false | Drop postings that publish no salary. |
minSalaryYearly | Minimum yearly salary | empty | Keep postings whose top yearly salary reaches this amount, in the currency below (hourly, weekly and monthly figures are converted to a year first). Postings with no salary or in another currency are dropped. Leave empty for no minimum. |
salaryCurrency | Currency for the minimum | USD | Three-letter code of the currency the minimum is in. Currencies are never converted. |
postedAfter | Posted after | empty | Only postings published on or after this date, such as 2026-10-15, or a relative time such as "3 days" or "2 weeks". |
descriptionFormat | Description format | text | Plain text (easy to read and search), the original HTML (safe to sanitize before showing it), or no description (smaller output). E-mail addresses are removed either way. |
maxItems | Maximum new postings | empty | Stop after this many postings are delivered (and charged). Postings held back stay "new" for the next run when "Only new postings" is on. Leave empty for no limit. The spending limit you set on the run is respected too. |
onlyNew | Only new postings | false | Deliver only postings this feed has not delivered before. Run it on a schedule and every run returns just what appeared since. The memory is saved after each delivered batch. First run: everything currently listed counts as new. |
memoryKey | Memory key | default | Name of the memory used by "Only new postings". Use a different key for each scheduled task (for example one per filter set) so they do not share what was delivered. Letters, digits, - and _. |
Postings that do not pass the filters are dropped for free. All filters apply together. A posting whose source does not state the thing you filter on (for example a contract type) is left out when you filter on it.
Output
One row per posting, always the same fields in the same order:
{"id": "jobicy:153777","source": "jobicy","sourceName": "Jobicy","url": "https://jobicy.com/jobs/153777-sales-manager-net-new-sales","title": "Sales Manager - Net New Sales","company": "CoderPad","postedAt": "2026-10-22T04:45:25.000Z","employmentTypes": ["full_time"],"seniority": "lead_director","seniorityInferred": false,"category": "sales","locationText": "USA","locationRestriction": "restricted","countries": ["US"],"salaryMin": 200000,"salaryMax": 220000,"salaryCurrency": "USD","salaryPeriod": "year","salaryMinYearly": 200000,"salaryMaxYearly": 220000,"salaryYearlyEstimated": false,"alsoListedOn": [],"attribution": "Jobicy (https://jobicy.com) - listing and apply link are on Jobicy; used under the Jobicy API terms.","fetchedAt": "2026-10-22T10:08:37.000Z"}
(shortened: the real row also has description, companyLogoUrl, categories, tags and regions.)
| Field | Type | Meaning |
|---|---|---|
id | string | Stable ID of the posting: ": |
source | string | Board the record comes from: "jobicy" or "remoteok". When the same posting is on both, the first one in the list above supplies the record. |
sourceName | string | Human name of the source board. |
url | string | Canonical URL of the posting ON THE SOURCE board. Use it for the "Apply" button: the sources require that apply links and credit stay on their own pages. |
title | string | Title as published. |
company | string | Hiring company name. Company only: no recruiter names or e-mails are ever delivered. |
companyLogoUrl | string/null | Company logo URL when the source gives one (the Remote OK logo itself is never used). |
postedAt | string/null | Publication date, ISO 8601 UTC. |
description | string/null | Job description as plain text (default), HTML or null, see "descriptionFormat". E-mail addresses inside the text are replaced by "[email removed]". |
employmentTypes | array | Any of full_time, part_time, contract, freelance, internship, temporary, other. Empty when the source does not say. |
seniority | string/null | entry, mid, senior or lead_director. Null when the source says "any" or nothing and the title gives no hint. |
seniorityInferred | boolean | True when seniority was guessed from the title because the source gives no level (always the case for Remote OK). |
category | string | One shared category: engineering, devops, data, security, design, product, marketing, sales, support, finance, hr, legal, operations, healthcare, education or other. |
categories | array | Category names exactly as the source gives them (Jobicy industries). Empty for Remote OK. |
tags | array | Skill and topic tags as the source gives them (Remote OK). Empty for Jobicy. |
locationText | string/null | The location text of the source, untouched. |
locationRestriction | string | "worldwide" (the text says anywhere/worldwide), "restricted" (named countries or regions) or "unspecified" (nothing usable; a bare "Remote" is unspecified, not worldwide). |
countries | array | ISO 3166-1 alpha-2 codes of the countries named in the location text. |
regions | array | Regions named in the location text: europe, eu, emea, apac, latam, north america, americas. |
salaryMin | number/null | Lower end of the published range, in the published period. Null when no salary is published. |
salaryMax | number/null | Upper end of the published range. |
salaryCurrency | string/null | ISO 4217 code. Never converted: no exchange rates are applied. |
salaryPeriod | string/null | year, month, week, day or hour. |
salaryMinYearly | number/null | Lower end per year: x12 for months, x52 weeks, x260 days, x2080 hours. Same currency as salaryCurrency. |
salaryMaxYearly | number/null | Upper end per year. |
salaryYearlyEstimated | boolean/null | False when the source published a yearly figure; true when the yearly figure was computed from another period (or a guessed one). Null without salary. |
alsoListedOn | array | Same posting found on the other source: [{ source, sourceName, id, url }]. Empty when only one source has it. |
attribution | string | Credit text for the source(s). Show it, with the posting URL, wherever you display the posting. |
fetchedAt | string | When the source was read, ISO 8601 UTC (up to one hour old: sources are read at most once per hour). |
Salary: how the yearly figure is made
| Published period | Yearly figure | salaryYearlyEstimated |
|---|---|---|
| year | as published | false |
| month | x 12 | true |
| week | x 52 | true |
| day | x 260 | true |
| hour | x 2,080 | true |
Remote OK publishes yearly USD amounts; the few figures under 1,000 there are clearly hourly rates, so they are read as hourly and flagged as estimated. Some sources publish odd numbers (for example a range of 10,000-10,000): they are passed on as published. Currencies are never converted - there is no exchange-rate source, so minSalaryYearly compares in one currency (salaryCurrency, default USD) and leaves out the others.
Location: what it means
locationRestriction is worldwide when the posting says Anywhere / Worldwide, restricted when it names countries or regions (countries are ISO codes, regions are europe, eu, emea, apac, latam, north america, americas), and unspecified when there is nothing usable: an empty location, a bare "Remote", or a city we do not place. We do not guess. Time zones are not published by these sources, so there is no time-zone field or filter.
The run summary
Every run writes a SUMMARY record to the key-value store: status, what each source returned (fetched, usable, discarded, whether it came from the one-hour cache), how many postings were merged as duplicates, dropped by each filter, already delivered, delivered and held back, the stop reason, billing, and warnings.
Sources and their terms (read on 22 October 2026)
| Source | What the terms say | What this Actor does |
|---|---|---|
| Jobicy - API & RSS terms, site terms | Listings may be used in your own products, commercially, and stored, cached, processed and redistributed. "Use of content requires a clickable source link"; keep the canonical Jobicy URL and keep apply links pointing at Jobicy; credit Jobicy. "Start a new automated pass no more than once per hour"; handle HTTP 429 with a deferred retry. The API serves the last 7 days. | Uses the public API only. Keeps the Jobicy URL and an attribution on every row. Reads Jobicy at most once per hour (the last answer is cached for 60 minutes), waits at least 1 second between requests and obeys Retry-After. |
| Remote OK - API notice, site terms | The API answer says: link back to the URL on Remote OK and mention Remote OK as a source; do not use the Remote OK logo; otherwise API access can be suspended. The site terms say nothing against API reuse. | Keeps the Remote OK URL and an attribution on every row, never ships the Remote OK logo, same one-hour cache and 1-request-per-second pacing. |
Your side of the deal: if you publish these postings, show the attribution and link to the posting url (for Remote OK a normal followed link), and send applicants to that URL. Jobicy also asks you to remove postings that have expired (a posting that drops out of its 7-day feed is not necessarily closed: confirm before you mark it closed), and to keep your own data in step with the source. The terms of each board apply to you as the user of the data.
The attribution texts delivered in the rows are exactly:
- Jobicy:
Jobicy (https://jobicy.com) - listing and apply link are on Jobicy; used under the Jobicy API terms. - Remote OK:
Remote OK (https://remoteok.com) - source of this listing; link back to the Remote OK URL.
Sources deliberately NOT used (and why)
- Himalayas - its API terms (section 113, read 22 Oct 2026) allow showing jobs "only on sites and apps you operate" and say "Do not pass them on to third parties ... including through feeds". A data feed sold to other people is exactly that, so it is not included.
- Arbeitnow - its API asks for a link back, but its terms of use (section 2) grant a licence for "personal, non-commercial transitory viewing only" of the site materials, and it is unclear whether that covers a paid feed. Left out until Arbeitnow confirms in writing that commercial reuse is fine.
- Remotive - API terms ask you not to submit its jobs to third-party sites, require a 24-hour delay and a link back, and offer a paid API for other uses; not used.
- Working Nomads - its API is open but no reuse terms are published for it; no terms, no use.
- LinkedIn, Indeed and any site that blocks bots - never scraped.
Limits (honest)
- Two boards, not twenty. Together about 1,200 postings are listed at any time; most are tech, sales and operations roles. Jobicy only serves the last 7 days.
- Sources are read at most once per hour; a run started twice in an hour sees the same data (
fetchedAttells you when it was read). - Category and seniority are mapped by rules: Jobicy states a level, but it is dropped when the title contradicts it (many Jobicy postings are tagged "Director" whatever the title says), and for Remote OK the level is always a guess from the title. Check
seniorityInferred. - The description is the employer's text. We remove e-mail addresses but do not rewrite it.
- No contact details, no application tracking, no salary currency conversion, no time zones.
- A posting that disappears from a board is not announced; use
postedAtandonlyNew.
FAQ
How is "only new" decided? A posting is "delivered" once it is in your dataset. Its id (and the id of the same posting on the other board) is then saved to a memory in your account. The next run skips any posting whose id is in the memory. Postings are forgotten after 60 days.
Can I run several scheduled tasks with different filters? Yes - give each task its own memoryKey, so they do not share the memory.
What if a run is interrupted? The Apify platform may restart a run on another server. The Actor reads what is already in the dataset and does not deliver or charge those postings twice.
Why did my second run return nothing? With onlyNew that is the point: nothing new appeared. Without it, check the filters and the SUMMARY.
Can I use my own keys or another job board? Not in this version. Open an issue in the Actor page with the board and we will check its terms.
Use it from code
curl -X POST "https://api.apify.com/v2/acts/far003~remote-jobs-feed/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \-H "Content-Type: application/json" \-d '{"keywords": ["python"], "countries": ["DE"], "onlyNew": true, "memoryKey": "python-de"}'
from apify_client import ApifyClientclient = ApifyClient("<YOUR_API_TOKEN>")run = client.actor("far003/remote-jobs-feed").call(run_input={"onlyNew": True, "memoryKey": "daily", "categories": ["engineering"]})for job in client.dataset(run["defaultDatasetId"]).iterate_items():print(job["title"], job["company"], job["url"], job["attribution"])
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });const run = await client.actor('far003/remote-jobs-feed').call({ onlyNew: true, memoryKey: 'daily' });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Works with Apify Schedules (run it every morning), webhooks (call your endpoint when a run finishes), the Make, Zapier and n8n integrations, Google Sheets, Slack and the Apify MCP server for AI agents. Export the dataset as JSON, CSV, Excel, XML or RSS.
Support
Something wrong, or a field you need? Use the Issues tab of this Actor. Include the run ID; the SUMMARY record tells us what each source returned.