Never charged for a blocked page, an empty search or a repeat. Glassdoor listings with the employer's star rating on the same row — no second run. About 4 in 5 rows carry one. Your row cap and deadline are hard limits.
1.0.17 — the employer-profile fetch allowance counts correctly
The per-run allowance on our employer-profile page fetches now counts every fetch at its
true cost. A fetch that cost more than one unit could previously be counted as one, so a
profile-heavy run could spend past the allowance before its clean stop engaged. That
allowance is ours and was never billed to you — nothing changes about what a run delivers
or charges.
1.0.16 — the employer-profile stop rows now say what is actually true
When the free preview runs out of employer profiles, the row no longer tells you to start
a new run. During the preview every run gets the same fixed profile allowance, so a new
run would walk the same employers and stop in the same place. The row now says the
remaining profiles become collectable when the add-on fully launches — and confirms that
the profiles already delivered were included free, rather than "charged once each".
A run that is moved to another server mid-way keeps the pricing it started under. A
preview run could previously resume and announce that "no profiles were fetched" directly
above profile rows it had already delivered.
If our profile provider ever refuses our key, the run stops asking straight away instead
of trying once per employer and reporting each one as a temporary problem. Nothing was
charged for those rows either way — but they said "please re-run" for something re-running
could not fix.
1.0.15 — employer profiles are on, included free while the add-on rolls out
Turn on "Include employer profiles" and the profile rows arrive now. While the add-on
rolls out, delivered employer profiles are included free: each profile row carries
charged: false and says so right on the row, and nothing extra is charged for it.
Everything else about the option is exactly as documented: one profile per unique employer
among your delivered listings, never fetched twice — including across a moved or restarted
run — and a profile that cannot be fetched, or an employer Glassdoor has no rating for, is
an uncharged row that says exactly that.
Nothing changes for runs that leave the option off.
1.0.14 — a dropped connection is not Glassdoor answering "not found"
A connection that failed on the way to Glassdoor is no longer reported as a verdict about
your search. When a network hop dropped mid-request, the run used to end that search with
"Glassdoor answered 'not found' … check the country site and search term" — though Glassdoor
had answered nothing at all. A failed connection is now treated as what it is: temporary. The
request is retried on a fresh route, and if it still cannot get through, the row says so
honestly, uncharged, and invites a re-run — it never blames your query for our network.
1.0.13 — refused pages stop faster; a typo in a field name gets a pointer
A run that Glassdoor keeps refusing now wastes far less on every refused page. Once a
run has already stepped up through its retry options, each further refused page is answered
after one fresh attempt instead of a full round of retries that could not end differently.
Refused pages stay exactly what they were: uncharged, honestly reported, and worth a re-run
later — runs on busy days simply finish sooner and spend less doing it.
A field name the actor does not recognise is named back to you. If you sent your search
under a field this actor does not have — query instead of queries, say — the run used to
answer with the free sample rows, exactly as if you had asked for a preview, and nothing told
you the input had been ignored. You now get one uncharged row that names the field it did not
recognise, names the field you meant, and shows the shape to send. Sending only settings, with
no search terms at all, gets the same pointer. A run with no input at all still returns the
free sample rows, unchanged.
1.0.12 — employer profiles: an optional deep-dive row per employer
New opt-in "Include employer profiles": one extra row per unique employer
among your delivered listings, read from the employer's Glassdoor profile
page — overall rating, how many ratings it rests on, the share who would
recommend the company to a friend, and the CEO's name with their approval
score, plus a canonical link to the profile.
One profile per employer per run, however many of its listings you
collected, and it is never fetched or charged twice — including when a run is
moved or restarted.
Charged as its own event, only on delivery. A profile page that answers
with an access check is an uncharged row marked re-runnable; an employer
Glassdoor has no rating for is an uncharged row that says exactly that.
Off by default, and honest when unavailable. A run that does not ask for
profiles is billed exactly as before; a run that asks for them while the
add-on is not yet enabled gets one uncharged row saying so, and its job
listings as normal.
Ratings are read from the page's own structured data first, with the
visible markup as a fallback — so a cosmetic redesign of the profile page
cannot quietly turn every employer into a false "no rating" answer. A page
that claims a rating we cannot read ships as an uncharged, re-runnable row;
"no rating" is reserved for pages that positively carry none.
A profile source outage ends the leg at once. If the profile backend
stops answering mid-run, remaining employers are reported once as skipped
and uncharged — not written out one by one as misleading "temporary —
re-run" rows.
A re-run that hits the same access check again writes one row, not two.
The employer is still retried on every re-run; the "could not fetch" row is
only ever recorded once.
An employer id that cannot have a profile page is one honest final row —
nothing is fetched for it, nothing is charged, and it is not marked
re-runnable when re-running cannot help.
The run deadline always stops profile fetching, including on runs that
already reached their row cap — no profile fetch starts when there is no
time left to finish it cleanly.
A run revived after its time window had already passed can now actually
run. Reviving a run hours later used to compute a deadline that was
already behind it, so it stopped on its first step, delivered nothing, and
blamed your maxRunSeconds. It now gets a short fresh window — enough to
finish the work, never a second full one.
Encoded text in job fields is decoded exactly once. Text that literally
spells out a code like < no longer has it decoded a second time into a
stray <; it now reads exactly as the page published it.
1.0.11 — a listing posted "14m" ago is 14 minutes old, not 14 months
postedAt on a fresh listing was wrong by more than a year. Glassdoor's ages cap at
30d+, so its m means minutes — but we were reading it as months, dating a 14m listing
420 days into the past. relative was always right; postedAt and ageDays now agree
with it.
A restarted run no longer repeats its explanation rows. The demo rows, the input-error
rows and the "this search returned nothing" rows are now recorded the same way delivered
listings are, so a run that Apify moves to another server — or the daily zero-input check —
cannot write any of them a second time. None of them was ever charged, and none is now.
Your row cap counts job listings only. Those explanation rows used to count against
maxItems and against the resumed run's starting tally, so a run with a few blocked
searches could stop short of the listings you asked for. They no longer take a slot.
A run that cannot bill still counts what it delivered: its rows now carry an explicit zero
against the job event rather than an empty count, so the row cap behaves identically
whether or not the run is charging.
A run that stopped at your row cap or deadline no longer tells you to re-run with
resumeCursor — it is a field on the summary row, not an input you can pass. The summary
now says what actually works: narrow the search or raise the caps and run again.
1.0.10 — the summary row of a restarted run reports the whole run's charges
Found by restarting a real run, not by a test. When a run was restarted and had nothing new
left to collect, its final summary row reported chargedEvents: {} — sitting directly above
eight rows that each said charged: true, and against an invoice for eight. Nothing was
billed wrongly, but the one row you use to reconcile the invoice disagreed with the rows
above it.
chargedEvents on the summary row now counts the whole run, exactly as delivered
already did, so a restarted run reconciles against your dataset instead of against the
fraction of it that the last restart happened to handle. Unchanged on a run that was never
interrupted, and still {} when nothing was charged at all.
1.0.9 — a run that gets moved or restarted never bills you twice
Apify can move a long run to another server while it is working, and you can restart
(resurrect) a finished run yourself. Until now, either of those made the run start its
de-duplication from scratch: listings already sitting in your dataset were fetched again,
written again as duplicate rows, and charged again. This build closes that.
One listing, one row, one charge — across the whole run, not just within one stretch
of it. On restart the run reads back what it has already delivered and skips exactly those
listings. It does not fetch them, does not write them, and does not charge for them.
If the run was interrupted between delivering a listing and billing it, that one charge
is now completed rather than quietly dropped, so chargedEvents in the summary row and
your invoice agree with the rows in front of you. This can only ever go one way: the run
bills after it delivers, so an interruption can under-bill you but never over-bill you.
maxItems is a limit on the run, not on each restart. A run capped at 100 rows that
got moved could previously deliver up to 200. Now the cap counts everything already in
your dataset.
maxRunSeconds is measured from when the run started, not from the restart, so a moved
run no longer quietly gets a second full time allowance.
If a restarted run cannot confirm what it already delivered, it stops instead of
guessing. You keep every row already collected, nothing extra is charged, and the last
row says plainly what happened and to start a new run for the rest.
The README now links a live example dataset from a real run — 30 listings, 26 with the
employer's star rating, and 60 repeated listings skipped and charged once.
Nothing about the output changed: same columns, same prices, same charged and missReason
on every row.
1.0.8 — review-fix build: honest de-dupe wording on the per-search row
The per-search summary row explains why a listing you can see on Glassdoor is missing from
your rows and missing from your invoice. When exactly one repeat was skipped it read
"1 listing was already delivered in this run and were not charged again" — a singular
subject with a plural verb, in the one line a buyer reads to reconcile the bill. It now
reads "1 listing was already delivered in this run — not charged again", and
"2 listings were already delivered in this run — not charged again" for more than one.
Wording only. What is delivered, what is de-duplicated and what is charged are unchanged.
1.0.7 — pre-publish sweep: honest charged, and a rating you can trust
An employer rating outside Glassdoor's own 1-5 scale is now dropped instead of shipped.
If our reader ever picks up the wrong number on the card — a review count, say — you get
no rating rather than a false one, because a wrong rating quietly passes the
"minimum employer rating" filter as though it were real. Same guard on salary figures.
charged on every row now reports what actually happened rather than what was intended:
when the run cannot bill, delivered rows say charged: false instead of claiming a charge
that never landed.
README leads with a real output row, states the price up front, names the two failures you
are most likely to see, and carries the full steadyfetch shelf.
1.0.6 — initial release
Glassdoor job search with the employer's star rating on the same row, priced per delivered
job listing and nothing else.
One row per delivered job listing: title, company, the employer's Glassdoor star rating,
location, salary exactly as Glassdoor prints it, whether that salary is a Glassdoor
estimate or the employer's own figure, posted age, Easy Apply, the results-page summary,
and a listing link rebuilt from the job's own id with no tracking token in it.
The employer rating costs nothing extra — it rides the same page as the job card. About
4 in 5 rows carry one; the rest report null, never a zero.
minEmployerRating, postedWithinDays and remoteOnly filter the rows Glassdoor already
returned, so anything they remove is never charged.
Glassdoor repeats the same 30 listings across deeper result pages. A search de-duplicates
by listing id and stops once two pages in a row add nothing new, so the same job is never
fetched or charged twice.
Search United States and United Kingdom (verified end to end), plus Canada, Ireland,
Australia, New Zealand, India and Singapore. Paste a Glassdoor search URL to search one
city with the location and filters you set on Glassdoor itself.
Nothing is charged for a search Glassdoor answered with an access check or a rate limit, a
search that matched nothing, a listing whose card could not be read, a repeat, a row your
filters removed, an input we could not read, or a run that stopped at one of your limits.
Every row carries charged and missReason so the invoice reconciles from the dataset.
Your limits are exact: maxItems, maxPagesPerSearch and maxRunSeconds stop the run
cleanly, the run still finishes successfully, and the last row names the setting that
stopped it and hands back the searches still to do.
Running it with no search terms returns uncharged sample rows and contacts Glassdoor not
at all, so a workflow can be wired up and tested before it costs anything.