The Muse Jobs $0.49/1K — Remote Job Board, Education Jobs avatar

The Muse Jobs $0.49/1K — Remote Job Board, Education Jobs

Pricing

from $0.49 / 1,000 jobs

Go to Apify Store
The Muse Jobs $0.49/1K — Remote Job Board, Education Jobs

The Muse Jobs $0.49/1K — Remote Job Board, Education Jobs

$0.49/1K | 411,812 jobs on The Muse — the public API stops at 1980 per query; this splits the search to pass it. Full job description text inline, no extra call per job. Software engineering jobs, entry level jobs, sales jobs, retail jobs, marketing jobs, customer service jobs, remote work listings.

Pricing

from $0.49 / 1,000 jobs

Rating

0.0

(0)

Developer

Snow Leo Data

Snow Leo Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

The Muse Jobs Scraper — 411K jobs, full text, past the 1,980 cap

$0.49 per 1,000 jobs — half the price of the other job scrapers in this store, because this source hands over the entire job advert text in the search response. No second request per job, so a run is both faster and cheaper.

411,813 jobs on The Musethe API's own counter, read live on the day this page was last updated
The public API stops at 1,980 per querymeasured: page 99 is the last with data, page 100 returns HTTP 400, and the page size is fixed at 20
This Actor goes past itmeasured: 2,060 unique jobs in 176 s from one run, zero duplicates
27 fieldscity, state and country parsed out of the location; emails and links pulled from the advert body
Full advert inline5,000-character bodies arrive with the listing, not behind a second call

The 1,980 wall

Ask for page 100 and the API answers HTTP 400. The page size is fixed at 20 no matter what you pass — send items_per_page=50 and you still get 20 rows back. So one query can never hand you more than 1,980 jobs, out of 411,813. A scraper that simply pages through a search reaches 0.48% of this curated job board.

This Actor splits a wide search until every slice fits under the wall: first by category, then by seniority, then by location. The values are read from the source's own results — The Muse publishes no taxonomy endpoint, so inventing a category list would mean inventing data. Slices overlap; every job is deduplicated by id during the walk.

walk: 2060 unique jobs in 176 s, 30 queries, split by category, level, location

Who this is for

Anyone building a job board. The advert text arrives with the listing, so a posting can be republished without a second fetch, and the landing page URL is right there.

Recruiters watching a market. Turn on Only jobs not seen before: delivered jobs are remembered in a named store that survives between runs, and a daily schedule charges you only for what appeared since yesterday.

AI and agent pipelines. Compact output returns 14 core fields and no advert body — the shape an LLM actually consumes — and Drop empty fields removes what the employer never filled in.

Quick start

Press Start. With no filters you get a 300-job sample of the newest listings.

{
"categories": ["Software Engineering"],
"levels": ["Senior Level"],
"locations": ["New York, NY"],
"maxItems": 0
}

What a row lets you filter on

27 fields per job; the full list is under Fields in detail. Three of them decide what a run is worth to you. The Muse publishes a location as one string, and this Actor turns it into job location data — city, state and country as separate fields, by a rule spelled out further down, so New York jobs separate from New Jersey without matching strings. Job level data is the board's own seniority band rather than a reading of the title, and job tags data is the set of labels the employer itself attached to the advert.


Job category data and seniority, as fields

The Muse files every advert under exactly one category and tags it with a seniority band, so job category data and job seniority data arrive as fields instead of as something you infer from the title. That is what turns a slice like internship listings in software engineering, or remote job listings in healthcare, into a filter rather than a keyword guess.

Internship listings here are the board's own Internship tag, not adverts that happen to contain the word intern: internship jobs data and senior jobs data are two values of the same level column, so one filter gives you either. Remote job listings are the ones whose location entry reads Flexible / Remote, not the ones whose advert text says "no remote work".

The Muse sells employer branding — companies pay to be profiled here — so what comes back is employer branding data as much as a job feed: the employer list is self-selected, which is why tech company jobs and large-employer adverts are over-represented and small local shops are not here at all.

Remote as a field, not a filter

is_remote is a column on every row, so a run doubles as remote work data: which companies post remote roles, in which of the 32 categories and at which seniority. Filter it after the fact instead of guessing from the location string: work from home data and US remote jobs are a cut across the whole board, not a corner of it. Remote hiring data therefore needs no separate run — it is one column on the rows you already paid for.

Category names are The Muse's own, not ours: data science jobs sit under Data and Analytics, social work under Social Services, and remote work jobs are a column rather than a category. The rest of the map is just as hard to guess — sales jobs data lives under Sales and under Account Management, marketing jobs data under Advertising and Marketing, designer jobs data under Design and UX, operations jobs data under Business Operations, HR jobs data under Human Resources and Recruitment, finance jobs data under Accounting and Finance, healthcare jobs data under Healthcare, and engineering jobs data under Science and Engineering. Ask for a name the board does not use and you get nothing back rather than a guess, and Unknown is one of the 32 real names, not a parsing failure.

Every run leaves a job listings dataset — JSON, CSV or Excel — with the job listings full text already in it, so the job posting text is in the same row as the title rather than behind a second call: the advert arrives inside the search response, so nothing is fetched twice.

How does this Actor get past the cap?

Why a Max jobs setting cannot help you

Raising a limit does not raise the ceiling. The ceiling belongs to the source: the public search endpoint refuses page 100 and pins the page size at 20, so a single query is worth 1,980 rows and no input value changes that. Ask one broad query for fifty thousand jobs and you still get 1,980, then silence. The only way through is to stop asking one broad question.

What the Actor does instead

Before paging, the Actor reads how many jobs the current query claims. If that number is under the cap, it simply pages the query out. If it is over, the query is split along the next unused axis — category first, then seniority, then location — and each child query is measured the same way. The walk repeats until every leaf fits, which is why one run can return far more than any single search could.

The values used for splitting are not a hard-coded list. The Actor samples the first three pages of the current query and collects the category, level and location names that the returned jobs actually carry. The Muse has no taxonomy endpoint to read, and a hand-written list would drift out of date the moment the source added a category.

What happens to jobs that appear in two slices

They are returned once. A job filed under both Data and Analytics and Software Engineering shows up in both slices, so the walk keeps the set of ids it has already yielded and drops the repeat before it reaches the dataset — which means before it reaches your bill. The measured run above ended with zero duplicates across 30 queries.

The walk also has a hard stop of 2,500 HTTP requests per run, so a pathological split cannot spin forever. When it trips, the run report says so instead of pretending the board was exhausted.

Fields

FieldWhat it is
title, companyheadline and employer
location, location_city, location_state, location_countrythe label split up — Jacksonville, FL becomes city, state and United States
is_remotetaken from the job's own location entry, never guessed from the text
category, level, employment_type, tagshow The Muse files the job
description_text, description_markdown, description_htmlthe full advert in three forms; pick one to keep rows small
contact_emails, contact_urlspulled out of the advert body
published_at, posted_days_agowhen it appeared
change_typein incremental runs: NEW, UPDATED or UNCHANGED
urlthe listing page

Empty means the source did not publish it. location_country stays empty rather than being filled with a guess when the label carries only a city.

How the location label is taken apart

The Actor splits the label on commas and reads the tail: a two-letter code means a US state, and the country is then set to United States; anything else is treated as the country itself. So Jacksonville, FL yields a city, a state and a country, while Munich, Germany yields a city and a country with location_state left empty.

Nothing is inferred beyond that rule. A bare Flexible / Remote stays in location as written, and locations_all keeps every place the employer listed, not just the first.

Fields in detail

Three shapes of the same advert

The source ships the advert as HTML. The Actor keeps that original in description_html, renders a readable plain-text version into description_text, and converts headings, lists, links, bold and italic into description_markdown. Markdown is usually the right choice for an LLM prompt; plain text is the cheapest to store.

All three together roughly triple the weight of a row, which is why the Description format input defaults to plain text and drops the rest. If you need every form, choose All three deliberately rather than by accident.

Contacts out of the advert body

Some employers put a hiring manager's address or an application link straight into the advert. The Actor scans the plain-text body and returns whatever it finds in contact_emails and contact_urls, deduplicated and stripped of trailing punctuation. Most jobs have neither; the Contacts dataset view exists for the ones that do.

What is deliberately missing

There is no salary field. The Muse does not publish structured pay on the public jobs endpoint, and a number guessed out of the advert text by a model is a number you cannot use. Rather than ship a column that is wrong often enough to matter, this Actor ships no column at all — if pay figures are in the body, the body is right there in description_text.

Remote is a location, not a word

Remote only uses the job's own location entry. Searching the advert body for "remote" instead would match "no remote work" just as happily — tests/test_logic.py keeps a trap job for exactly that case.

Filters run before you are charged

Keywords, exclusions, country, category, seniority, remote, posting age, employer allow and block lists. A row a filter removes never reaches the dataset and never appears on the bill. Dataset views can only choose columns — no view on Apify can drop rows — so the filtering lives in the input, where it is also cheaper. See the dataset documentation for what a view can and cannot do.

The run report records how many rows each filter removed, so a surprisingly small result is explainable rather than mysterious.

Running it on a schedule

How do I pay only for what is new?

Turn on Only jobs not seen before and attach a schedule. The Actor keeps the ids and a short content fingerprint of everything it has delivered in a named key-value store, which — unlike the default store — survives from one run to the next. Tomorrow's run returns the jobs that appeared since today and marks them NEW, and you are billed for those alone.

The memory holds 150,000 keys. A key weighs about 25 bytes, and one record in a key-value store is capped at 9 MB; 300,000 keys measured 8.0 MB, close enough to the ceiling that half of it is kept in reserve. When the memory fills, the oldest keys are dropped first, never random ones.

What counts as a change

The fingerprint covers the title, the employer and the first 2,000 characters of the advert. A job whose text was rewritten comes back as UPDATED; a job that merely sat there comes back as UNCHANGED and, by default, is not returned at all — you already paid for it once. Switch on Also return unchanged jobs if your pipeline would rather see the full picture every morning.

Putting the output somewhere useful

The dataset can be pushed to Google Sheets, Slack, a webhook or your own database through the platform's integrations and webhooks, or pulled directly from the Apify API. For agent pipelines, the store's MCP server can call this Actor as a tool — Compact output is there precisely for that.

Questions people ask

How many jobs can one run return?

There is no cap in the Actor itself. The measured run returned 2,060 unique jobs in 176 seconds, which is already above what a single query of the source can produce, and the walk keeps splitting as long as slices remain. What bounds a real run is your Max jobs value, your spending limit and the 2,500-request ceiling on the walk, in that order.

Why does the public API refuse page 100?

That is the source's rule, not a guess. Page 99 returns 20 jobs, page 100 returns HTTP 400, and the page size ignores whatever you pass — send fifty and twenty come back. Twenty rows times ninety-nine pages is the 1,980 ceiling every naive The Muse scraper runs into, including the ones that do not tell you.

Does it fetch the full advert text?

Yes, and without a second request. The Muse returns the advert body inside the search response itself, which is unusual and is the reason this Actor is priced below the rest of the category. Bodies of about 5,000 characters arrive with the listing, already converted to plain text and Markdown as well as the original HTML.

What does a first run cost?

Press Start with nothing filled in and the run stops after 300 jobs — about fifteen cents at $0.49 per 1,000. That default exists so an empty form is a trial and not an accidental order for four hundred thousand rows. Set Max jobs to 0 once you know what you want.

What happens if I set a spending limit?

The Actor reads the limit at start and refuses to return more rows than it covers. This matters because the platform stops charging when a limit is reached but does not stop the run, so an Actor that ignored the limit would keep scraping at the developer's expense and yours in compute. See pay-per-event pricing for how the charging model works.

How do I get only new jobs each morning?

Set your filters, switch on Only jobs not seen before, save the configuration as a task and give it a daily schedule. The first run fills the memory and bills for everything it returns; every run after that returns and bills for the difference. Leave Also return unchanged jobs off unless you need the full set each time.

Can I feed this straight into an LLM?

Turn on Compact output for 14 core fields with no advert body, or keep the body and set Description format to Markdown so headings and lists survive the trip into a prompt. Drop empty fields removes keys the employer never filled in, which shortens the JSON an agent has to read and cuts the token bill.

Why are some fields empty?

Because the employer left them empty at the source. Nothing here is back-filled by inference: a job with no seniority stated returns an empty level, a location with no country in it returns an empty location_country, and an advert with no email in it returns an empty contact_emails. An empty cell is information — a wrong cell is not.

Does it need a proxy?

There is no proxy input, and every number quoted on this page was measured without one. The client retries on failure, honours the source's Retry-After on HTTP 429, and applies that cooldown across all threads at once so a slowdown request is not undone by the rest of the run hammering the same host.

How do I filter by country?

Use Only these countries with the country as it appears after parsing — United States, Germany. Because the country is derived from the location label rather than a field the source publishes, a job whose label is only a city has no country and will not pass a country filter. Location contains is the looser tool for those cases.

Output views

The dataset arrives with four views, so the same run can be read four ways: the default Jobs table, Location for the parsed city, state, country and remote flag, Contacts for the addresses and links found in advert bodies, and All fields for everything including both extra description formats. A view selects columns only — it never removes rows, and it never changes the bill.

Honest limits

  • No salary column, for the reason given above.
  • Splitting is by category, seniority and location — those are the axes the source exposes on the search endpoint. A query that stays above the cap even after all three are used is reported as capped, with the number of jobs it could not reach, rather than quietly truncated.
  • The category list in the input is a snapshot of what the source was serving when it was taken; The Muse publishes no endpoint that lists them.
  • posted_days_ago is computed in UTC from the publication timestamp, so a job posted late in the day may read as one day old sooner than you expect.

Jobs this board does not carry

The Muse is a curated board: employers pay to be listed, so it is deliberately narrower than the open market. Three sibling Actors cover the vacancies that never reach it, and each stamps its rows with a source field so their datasets can be concatenated and still be told apart:

A run succeeded but the dataset is empty — is that a failure?

Not necessarily, and the Actor always says which case it is. The run report carries empty_reason and the log carries a warning naming it: nothing-changed (the incremental mode working as asked — everything had already been delivered by an earlier run, so nothing was written and nothing was charged), filtered-out (your filters removed everything that was collected), source-returned-nothing (the search matched nothing at all), or charge-limit-reached (the run's spending limit was already used up). A silent empty dataset is treated as a defect, not as a normal outcome.

Reproducing the numbers

python3 tests/run_all.py --live

Every figure above comes from that run.