# Changelog of Glassdoor Jobs Scraper — Glassdoor API Alternative + Ratings (`steadyfetch/glassdoor-jobs-scraper`) Actor

- **URL**: https://apify.com/steadyfetch/glassdoor-jobs-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/steadyfetch/glassdoor-jobs-scraper.md

## Changelog

### 1.0.64 — 2026-09-19

- **The whole "Maximum cost per run" you set is now spent on jobs.** Before this build the run held back about a tenth of your cost cap before it started — a cushion for platform running costs which, on this actor's pricing, your cap does not pay for at all: your cap buys jobs, and the running costs are ours. So the cushion was simply cap you had asked to spend and could not get. On a run capped at $0.10 with jobs at $0.0012 the plan asked for 75 jobs where the cap paid for 83 — 8 jobs of your own cap, unspendable by construction. From this build the plan uses your cap in full, so a run asks for as many jobs as your cap can really pay for. Nothing you are charged changes, and nothing can be charged above the cap you typed: every charge is still checked against what is left of your cap at the moment it is posted, and a run that reaches your cap still stops cleanly, ships what it had already fetched, and tells you what is left. No price, input field or output column changes.

### 1.0.63 — 2026-09-18

- **The shelf note about Google Jobs' search fee now reads as a live charge, not a coming one.** Nothing on this actor moved: no price, input field, output column or charged event changed, and a run costs what it cost yesterday. What changed is a neighbour this page points at — the Google Jobs actor's search fee of $0.004 per search that returns listings was announced ahead of time and is now charging, so the sentence that said it was coming says it is here. A search there that returns nothing still pays nothing, and the same block's note about the three actors that add a small delivery-conditional fee is stated in the present tense for the same reason.

### 1.0.62 — 2026-09-14

- **If your API token cannot open key-value stores, the run now says so — and says which permission to grant.** The check that stops you paying twice for a listing you already have lives in a key-value store in your own account, and it is also what stops a second employer-profile fetch and a second profile charge. A run started from the API with a token whose permissions are limited to this Actor's scope cannot open that store, so the check silently did not run and listings your account already had were charged again — while the run page said only "repeat check unavailable" and named no cause and no fix. The run page now names both: the token cannot open key-value stores, repeats may have been charged again, and the fix is key-value store Read, Write and Create (or Actor runs set to Full access) under Settings → API & Integrations, or running from the console. The same sentence is now the first row of your dataset, uncharged, so a script or an AI agent that reads rows and never the run page sees it too, and the run log says it as well. A store that is merely unreadable for any other reason is unchanged and still says what it always said.
- **The run page no longer loses the tail of a long status line.** Counts that are also in `OUTPUT` and on the rows now give way first when the line would run past the 500 characters the run page shows, and the line says they did, so the invoice, the uncharged items, the binding limit and the contact line always survive.
- **If your API token can read but not write key-value stores, the run now says so before you are charged twice over.** The memory that stops you paying again for a listing you already have has to be written to, not only read. A run started from the API with a token that may read key-value stores but not write them (or may write but not create the store on your first run) opened the memory, read your existing listings correctly, and then quietly recorded nothing — so the run looked perfect and your next run was charged for the whole delivery all over again. On this actor that is two charges and not one, because the employer profile is remembered with the listing and would be fetched and charged again as well. The run page now says it as soon as the first listing is delivered: the token cannot write key-value stores, the listings delivered now will be charged again next run, and the log says what to grant — key-value store Write (and Create) under Settings → API & Integrations, or Actor runs set to Full access. The same sentence lands once as an uncharged row in your dataset, so a script or an AI agent that never reads the run page sees it too. A run whose writes land is unchanged in every respect, and nothing about what this run charges has moved.
- **One wording across our actors for the older “cannot open key-value stores” line**, so running two of them shows you one sentence and not two. And when a very long status line has to give way, the count of listings you already had now gives way with the other counts — it is still in `OUTPUT` and on every row — so the cause, the binding limit and the contact line always survive.
- Nothing else moved: no input field, output column, event or price changed, and what a run charges is unchanged.

### 1.0.61 — 2026-09-13

- **The input form's own description now says how to call this actor and what a listing costs.** An AI agent shopping for actors never opens this page — it reads the input schema, and ours said little more than what the actor does. It now opens with the call that works (`{"queries": ["data analyst"], "countries": ["US"]}`), says which field is required and that every other one can be left out, gives the price of one delivered listing with the arithmetic for a 200-listing run, says that asking for employer profiles adds its own charge, lists what is never charged, and names the run option that caps the bill. The search terms field now names what is never charged and which sibling actor an Indeed search or a company's own careers page belongs in. Nothing else moved: no input field, output column, event or price changed.

### 1.0.60 — 2026-09-12

- **Employer profiles are now charged, at $0.002 per profile fetched.** The 14-day notice on this change ran out today, so the fee named on the Pricing tab is live: each unique employer profile a run delivers is one `employer-profile` event at $0.002 — one per employer per run, however many of that employer's listings you collected, and never fetched or charged twice. Job listings are priced exactly as before, a run that does not ask for profiles is billed exactly as before, and a profile that cannot be fetched or that Glassdoor has no rating for is still an uncharged row. The earlier note on this page, on the input form and in the output description — saying nothing extra was charged for profiles while the fee was still pending — has been removed, because it no longer describes what a run costs.

### 1.0.59 — 2026-09-12

- **An aborted or migrated run now leaves its receipt from the first moment of the run, instead of only after start-up finishes.** The record that says how a run ended was only put in place once the run had finished starting up — reading your input, opening its stores, working out what a previous attempt had already delivered and charged. A run stopped inside that window ended with nothing written about it at all, which mattered most on a re-started run, where the thing not written about was the earlier attempt's work. It is now in place from the run's first moment. Nothing else moved: no input field, output column, event or price changed, and a run that reaches its work behaves exactly as before.

### 1.0.58 — 2026-09-12

- **Asking for more than this actor can do no longer stops the run before it starts.** A row cap above 10,000, more than 10 result pages per search, a run time outside 30–3,600 seconds, "Posted within (days)" above 30 or a minimum employer rating above 5 used to be refused by Apify itself: no run, no rows, no explanation — just an error, which is what an API call or an AI agent guessing a round number got. Those ceilings are unchanged and still hard, but they now live in the actor instead of in the form: the run starts, continues at the nearest limit, and writes one extra uncharged row saying what you asked for and what bound it. A rating or a posting window past what Glassdoor itself publishes is now clamped and named the same way rather than being dropped in silence, so a filter you set can never quietly stop filtering. Nothing about a run inside the limits is different, and the note row is never charged.
- **The links to our other scrapers on this page name them correctly again.** Several of those actors were retitled on the store, and this page still used their old names. The links always pointed at the right actors; only the words were out of date.
- Nothing else moved: no input field, output column, event or price changed.

### 1.0.57 — 2026-09-11

- **The one-link agent pin now sits at the top of this page.** An AI agent that reads this listing sees only its opening, and the pin — along with the actor id, the single input field a run needs, and the run option that caps what a run can spend — used to sit thousands of characters below it. All four are now in the first few lines, so an agent can pin this actor and start a capped run without reading further. The links to our other job actors on this page also carry the names those actors actually go by on the store. Nothing about the actor, its input or its output changed.
- **A maintenance change besides that: nothing about your runs changes.** The private run-report this actor writes for our own support — counts only, never anything you typed — now also records the per-event prices the run was charged under, so a question about a bill can be answered from the run's own record rather than from a price table read afterwards. It records the prices that already applied; it does not set them.
- Nothing else moved: no price, event or output column changed, and only delivered results are charged.

### 1.0.56 — 2026-09-10

- **A maintenance build: nothing about your runs changes.** The private run-report this actor writes for our own support — counts only, never anything you typed — gained room for four figures it does not fill in yet: how many targets a run was given, how long it waited on a blocked source, how many of those waits recovered, and a reason code for an item our own size limit refused. The build carries the shared contract so a later one can report them; on this actor every one of them is left blank, and nothing a run does or costs is different.
- Nothing else moved: no price, event or output column changed, and only delivered results are charged.

### 1.0.55 — 2026-09-09

- **Two runs started at the same time no longer erase each other's memory.** The account's repeat memory is now merged on every write, so an item one run delivered stays remembered, and a later re-run hands it back instead of charging it again.
- Nothing else moved: no price, event or output column changed, and only delivered results are charged.

### 1.0.54 — 2026-09-09

- **A run your own filters emptied no longer ends by asking whether something went wrong.** The closing line on the run page asks you to open an issue when a run ends badly. It was deciding that from the row count alone, so a search that ran, listed jobs and had every one of them removed by a filter you set still pointed you at the Issues tab. It now says so only when nothing that happened explains the empty result — your own filter, and listings already in your account, are clean endings.
- **The same correction applies to the record the run writes back to us**, which reads that closing line: a clean run is no longer recorded as a problem run.
- **The rendered rescue is no longer started when the run cannot finish it.** When Glassdoor would not let a search through, this actor pays for one rendered fetch to get you the page anyway. It only started that fetch if 75 seconds of run time remained — but a rendered fetch retries once after a pause, so a whole one can take 135 seconds. A rescue begun inside that gap was cut off by the run clock, and it still cost a vendor credit for nothing. The gate now holds a whole rendered fetch plus room to report, so a rescue either runs to the end or is skipped cleanly and says so.
- Nothing else moved: no price, event or output column changed, and only delivered results are charged.

### 1.0.53 — 2026-09-06

- **A block of links pasted into one row is now read as the list you meant, whatever separates them.** "Search terms" takes one search per row, and a pasted Glassdoor search link is a search of its own. A block of search links pasted into a single row was read as one very long search that could only come back empty. A pasted block is now split back into its individual links whether they are separated by spaces, line breaks, tabs, commas, semicolons, pipes or nothing at all, so a column copied straight out of a spreadsheet works. Each one is then read, deduplicated and charged on its own, exactly as if you had pasted them one per row.
- **A link that carries another link inside it is still one link.** An address holding a second address in its query or its path is left whole, not broken in two, and a single link pasted on its own is never rewritten.
- **Text pasted around a link no longer breaks it.** A number, a bullet or a note sitting beside a link is ignored and the link itself is used. A row holding no link at all is still answered as one row, not one row per word.
- Nothing else moved: no price, event, output column or charge changed, and only delivered results are charged.

### 1.0.52 — 2026-09-06

- **The run's own record now counts the searches your limits left unrun.** When your row cap filled, your maximum cost per run was reached, or the run's clock ended the walk, the searches still ahead of it were simply absent from the report this run writes back to us: a run of two searches that delivered from one closed its books with the other named by nothing at all. The same is now true of a listing a results page had already listed when a limit stopped its row. The employer-profile leg has reported its own this way since 1.0.49; the job walk now does too, so a run that stops short of your searches is visible to us without you having to report it.
- **A run that stopped at your row cap now also records whether that cap really filled.** It is one true-or-false fact about the run, never the number you typed, and it lets us see a run that claimed your cap stopped it while it had in fact delivered fewer rows than you allowed.
- **Nothing you can see changed.** Your rows, your columns, your receipt row, the searches it lists as not reached and your bill are exactly as they were — only delivered rows are charged.

### 1.0.51 — 2026-09-06

- **Listings on a results page already fetched when the run's time limit lands are now delivered, never dropped.** Rendering a Glassdoor results page is the expensive step; writing its listings out costs nothing more. When the clock ran out on that render, the run used to stop part-way down the page and hand you a short list from a page it had read in full. Every listing on a page this run rendered is now delivered, charged once each exactly as before.
- **What a time limit means has not changed.** It still ends the *collecting* — no further page and no further search is fetched after it — and the summary still names `maxRunSeconds` and lists the searches that were not reached. Your row cap and your maximum cost per run still cut the delivery exactly where you set them, and the run keeps back enough time to write the receipt.

### 1.0.50 — 2026-09-05

- **An employer profile Glassdoor answers with an access check is now waited out, the way a blocked job search already was.** When the Overview page came back as an access check instead of the page, this actor took one more look on a fresh connection and then shipped an uncharged "temporary — re-run to collect it" row, usually within a minute of a run whose time limit is fifteen. The job-search side of the same run had been pausing and trying again for a while. The profile leg now does the same: after both looks are turned away, the run pauses once for a minute and a half to three minutes and takes one more look before that row ships.
- **The pause is bought against the run's own profile allowance, never against your bill.** Each look at an employer page costs this actor a page-fetch allowance, so the pause is only taken while that allowance still has room for the look it is for — read without spending any of it. Waiting itself is never charged, a walled profile is never charged, and only a real profile page is ever charged. A run whose allowance is spent stops cleanly exactly as it did.
- **Every other bound is unchanged.** One pause per employer, never a loop; all waiting on this leg shares one ceiling for the whole run; a run with several employers left keeps half its remaining time back for them while a run with one employer may use what is left; and the time kept back to finish and report is untouched. A profile page that answers on the first look costs exactly what it did before.

### 1.0.49 — 2026-09-05

- **A search that turns up nothing, and a listing your own filters remove, are now counted against what the run asked for.** Every run of this actor kept its own arithmetic of what was asked and what came back. That arithmetic used the row limit as the ask, so a run with the default limit of 100 that found 30 listings recorded itself as owing seventy more rows that nobody had ever asked for, and a listing your minimum-rating, posted-within or remote-only setting removed was recorded as nothing at all. The run now asks for exactly what its searches listed, and names every listing it did not deliver — removed by your filters, already in your account from an earlier run, or a card Glassdoor showed that we could not read. Nothing about your rows or your bill changed: the same listings are delivered, the same ones are charged, and a search that found nothing is still an uncharged row that says so.
- **The receipt row now says how many searches you asked for, not how many survived.** If a country you typed could not be read, the searches for it never ran — and the receipt used to count only the ones that did, so a run where every search was stopped said "across 0 searches". It now says how many you asked for, and the rows above it still name each search that was stopped and which setting stopped it.
- **Employer profiles the run could not reach are counted too.** When the row limit, the run clock or the profile budget stops that leg part-way, the run records how many profiles were left and which of those three stopped it. The uncharged note row you already got is unchanged, and profiles are still uncharged during the preview window.

### 1.0.48 — 2026-09-05

- **A run that waits now always keeps room to finish and report.** When Glassdoor turns every connection away, the run can pause and climb the whole ladder again — but the check that decided whether a pause was affordable kept back only half a minute for that second climb, while a real climb here takes about a minute and three quarters. On a short run — the five-minute limit the daily health check uses among them — a pause could be allowed and the climb after it then ran past the end of the run, so the honest uncharged row it was supposed to write never landed. The check now keeps back a whole climb, measured from this actor's own connection ladder and its cooldowns, so a pause is only taken when the run can finish the climb the pause was for.
- **A run you start with nothing set now gets seven and a half minutes instead of six.** The sample's own limit had been sized against that half-minute placeholder rather than the real climb; it is now long enough to hold one refused climb, one pause of up to three minutes, and a real second climb, with time kept back to write your rows and the summary. A sample Glassdoor answers straight away is exactly as fast as it always was — the limit is a ceiling, never a wait — and a lower limit you set yourself still binds.
- **Charges are unchanged.** Waiting is uncharged, a refused search is uncharged, and only delivered listings are charged.

### 1.0.47 — 2026-09-05

- **A run started with no input at all is now always a real, fresh search.** Press Start on the untouched form and this actor runs a small real sample — "software engineer" on Glassdoor US, 5 listings, charged like any run. Until now that sample also checked, and added to, the record of listings this account has already been given. That record is what stops you being charged twice for the same listing on a real search, and it works exactly as before on any run where you set what to look for. On the no-input sample it was making the shop window hand back listings from an earlier press of Start instead of fetching new ones, so pressing Start twice showed rows that cost nothing but also proved nothing about whether the search still works. Every no-input sample now searches live and charges for the few listings it delivers. Nothing else changes: a repeat on a run where you set the search terms is still handed back or skipped, and still uncharged.

### 1.0.46 — 2026-09-05

- **A run started with no input at all now has room to wait out a block.** Press Start on the untouched form and this actor runs a small real sample — "software engineer" on Glassdoor US, 5 listings, charged like any run. Glassdoor sometimes turns every connection away for a few minutes at a time, and one round of attempts could use up the whole of the sample's own three-minute limit — so the pause-and-try-again this actor gained in the last build could never happen on the most common first run of all: it reported "blocked, please re-run" while the block was still lifting. The sample's time limit is now six minutes, sized to hold one full round of attempts, one pause, one more round, and enough time left over to stop cleanly and report. A sample Glassdoor answers still finishes in about a minute and a half; only a blocked one uses the extra time, and nothing is charged unless listings are delivered.
- **How much of its remaining time one blocked search may spend waiting now follows the size of what you asked for.** A run with more result pages still to fetch keeps exactly the limit it had — no single search may spend more than a third of the time left, so a wide block still leaves the later searches their share of the run. A run with a single page to fetch — the no-input sample, or one search term capped at one page — may spend what is left on its one pause, because nothing else is waiting for that time. Every other bound is unchanged: at most three rounds per search, never into the time kept back for reporting, and never when the run could not afford another round.

### 1.0.45 — 2026-09-05

- **A search Glassdoor turns away now waits and tries again while the run still has time.** Glassdoor sometimes answers a search with an access check on every connection for a few minutes at a time. A run used to make one round of attempts, ship the uncharged "please re-run" row and move on — often with most of its time limit unused. Now, when every connection is refused and the run still holds the time, it pauses for a couple of minutes and tries the whole round again on fresh connections, up to three rounds in all, never spending more than a third of the time it has left on waiting and always keeping enough back to finish cleanly. A search Glassdoor answers — with listings, with "no matching jobs", or with "not found" — is still answered at once; nothing waits on an answer. A short run (a one-minute time limit, say) makes its one round and reports exactly as before.
- **The uncharged row now says what was tried.** When a search stayed refused after the pauses, its row says how many connections were tried over how many minutes, that the run paused between tries, and roughly how much of the run was left — beside the same honest, uncharged, re-runnable status as before. The run's summary row and its log carry the same facts. A run that never paused reads exactly as it did.

### 1.0.44 — 2026-09-05

- **An employer profile blocked behind an access check now gets a second look before it gives up.** When "Include employer profiles" is on, each employer's overview page is fetched once; that fetch can come back as a sign-in wall that clears one network route later. A single walled fetch used to end the profile with an uncharged "re-run to collect it" row while the run's profile budget still held. It now takes one more look on a fresh route, so a profile reachable one route later delivers. A profile walled on both looks still ships the same honest, uncharged row; a real page on the first look is never re-asked; and the second look never starts when the run's clock or its profile budget cannot fit it. Nothing changes on the job listings themselves.

### 1.0.43 — 2026-09-05

- **A search blocked behind the rendered page now gets a second look before it gives up.** When every ordinary search rung is walled, this actor falls back to a rendered fetch of the search page — which can itself come back as a sign-in wall. A single walled render used to end the search with an uncharged "please re-run" row while the run's rendered-page budget still held. It now takes one more look on a fresh network route, so a search reachable one route later delivers its listings. A search walled on both looks still ships the same honest, uncharged row, and nothing changes on a search the ordinary rungs answer.
- **Employer-profile lookups now stop cleanly at the run's time limit.** A profile fetch near the end of a run could retry a temporary refusal a little past the run's own deadline; it now reads the same run clock every other step does, so a run ends when it says it will.

### 1.0.42 — 2026-09-04

- **Narrowing one setting no longer costs you the sample run.** Run this actor with no input at all and it searches "software engineer" on Glassdoor US — 5 listings, one results page, charged like any run — so you see real rows. Set one thing first, though — a country site, a minimum employer rating, "Remote jobs only", a row cap — and leave **Search terms** empty, and you used to get a single row asking for search terms instead of any output at all: the caller who set nothing was served better than the caller who narrowed a field. Now your settings are kept and the same sample runs under them, charged like any run. Everything you did not set takes the sample's own value, and one extra uncharged row names which settings were yours and how to run your own search.
- **A limit you set can only shrink that sample, never grow it.** "Max job listings for the whole run", "Max result pages per search" and "Max run seconds" are ceilings: ask for at most 10,000 listings and you get the sample's own 5; ask for at most 2 and you get 2.
- **The employer-profile leg stays off unless you turn it on**, exactly as before — the sample leaves it off, and turned on with no search terms it applies to the sample's five listings and no more.
- **Two things still answer with guidance instead of rows**, unchanged: a field name this actor does not recognise, and a search-term list you set and left empty. A country with no Glassdoor site is still refused before anything is fetched.
- No row on such a run — and no line of your run log — opens by saying nothing was set; each says which half was missing.
- **Every run now reports what you asked for in the same unit it reports what it delivered.** The run's own record counted your ask in *searches* while it counted the result in *listings*, so a run that returned many listings from one search recorded an ask of 1 — an ask smaller than its own delivery. Your ask is now your row cap. The summary row still counts searches, and says "searches".

### 1.0.40 — 2026-09-04

- **"Country sites" now reads the country you actually wrote.** `United States`, `USA`, `uk`, `United Kingdom`, `Canada` and `India` all land on the Glassdoor country site they name; only the two-letter code used to be accepted, so an ordinary spelling was refused as a typo and every search term in the run was dropped with it.
- **A country this actor cannot search no longer swallows your whole search list.** Every term you sent now comes back with its own uncharged row saying it was not run and which input field stopped it, instead of one guidance row for the whole ask.
- **A refused country says which of three things went wrong.** A country that really exists but has no Glassdoor site this actor can search says so and names the nearest country sites it does cover — it is not a spelling mistake, so you are not sent to fix one. A region or whole-world value (`Europe`, `worldwide`) says Glassdoor is searched one country site at a time. A value naming no country at all says there is no honest way to guess, and lists what is accepted.
- **Every input the run refuses now names the input field it is about** — `queries`, `countries` or `resumeFromDatasetId` — on the row itself and in the run's own record, so a run that delivered nothing can say which field to fix.
- **The run's record now counts every row it delivered, employer profiles included**, and reports the searches you asked for rather than what survived the refusals.
- Wording: past entries that described a row, a profile or a preview as "free" now use the register the rest of this changelog uses — every run spends platform time you pay for, so "uncharged" or "at no extra charge" is what is true. No facts, numbers or build numbers changed.

### 1.0.39 — 2026-09-04

- **A listing already delivered to your account is never charged a second time.** Every run now remembers what it delivered for your account, in a key-value store called `glassdoor-watch-account` in your own Apify account. Re-run the same searches and those listings are skipped before they take a row slot — no row, no charge, and no bite out of your row cap — and the status line says how many. The same rule covers employer profiles, which cost their own event and their own fetch. Delete that store to forget everything; entries older than 90 days count as new again.
- **New option: "Include listings you already have" (`includeSeen`).** Off by default. Turn it on and rows your account already has come back in every run's dataset anyway, marked `repeat: true` with `firstSeenAt` and `firstSeenRunId` naming the run that first delivered them — and still not charged. Use it when you want one complete dataset per run rather than only what changed.
- **A memory that cannot be read never stops a run.** The run delivers and charges as usual and the status line says the repeat check was unavailable, so you know a repeat may have been charged that once.
- **A mistyped "Continue from an earlier run" ID no longer creates an empty dataset in your account.** The ID is now checked and looked up read-only before anything else happens, so a typo, a pasted link or pasted rows come straight back as one honest uncharged row naming what the value looked like — instead of quietly making a storage under that name and leaving you to find it.
- The status line was shortened again so the counts, the cap that bound and the support ask all survive the run page's 500-character cut; on a run that ended badly the per-profile breakdown moves to the rows, which carry it in full. A run stopped by an unreadable "Continue from an earlier run" ID now says so in one short sentence on the run page and keeps the full explanation on its row, where nothing is cut.

### 1.0.37 — 2026-09-04

- **A run that stops before it starts now reports its outcome too** — a run refused for its memory setting now reaches us the same way every other run does: counts and reason codes only, never your input or your rows.

### 1.0.36 — 2026-09-04

- **Every run now reports its own outcome to us** — counts and reason codes only, never your input or your rows — so a run that goes wrong reaches us even when nobody shares it.
- **Stop guidance names the run-wide row cap first.**
- **Row notes state what happened and what was charged; the support ask lives on the run page.**

### 1.0.35 — 2026-09-04

- **Run status lines fit the run page again; sample-run wording shortened.**

### 1.0.34 — 2026-09-04

- **Employer profiles left behind by the row cap you set no longer count as something that went wrong.** A profile we did fetch that came back unavailable or unrated still does.

### 1.0.33 — 2026-09-04

- **A run that filled the row cap you set is no longer treated as a run that went wrong.** The Issues-tab line now appears only when something actually did.

### 1.0.32 — 2026-09-04

- **One support promise on this page** — the older three-hours line is gone.
- **The run page points at the Issues tab on every ending that went wrong**, including a run refused before it started.

### 1.0.31 — 2026-09-04

- **A run that ends with a problem now says where to reach us.** A miss, an input error, an early stop or a failed run closes by pointing at the Issues tab and naming the reply time; a fully delivered run is left alone.
- **One support promise across this page** — issues are answered in a couple of hours, always within a day.

### 1.0.30 — the screenshots load from Apify's own storage

The two console screenshots on this page — the input form and the dataset table a real run delivers — are now served from Apify's own storage instead of an outside host, so every picture and every link on this page stays on Apify. Same pictures, same page. What the actor does and what it charges are unchanged.

### 1.0.29 — the suite blocks name the sibling fees that start 16 September 2026

The suite blocks at the bottom of this page now state the three delivery-conditional fees that start 16 September 2026 on sibling actors — keyword volume's fresh lookup, profile posts' profile lookup, and Google Jobs' search fee. This page also now says "nothing charged for starting a run" wherever it used to say "no start fee" — the same promise, in the words the whole shelf uses. Nothing changes on this actor: same price, same events, same rows.

### 1.0.28 — real screenshots of the input form and the delivered rows

Two pictures from the Apify console are now on this page: the input form you fill in, and the dataset table a real run delivers, employer star rating included. The suite links at the bottom were re-checked against the live store listings, and one FAQ answer that still described the retired placeholder sample was corrected to match the rest of the page. What the actor does and what it charges are unchanged.

### 1.0.27 — run it with no input and get a real sample, not placeholder rows

Running the actor with an empty input (`{}` from the API, an AI agent, or a saved task that was never filled in) used to return uncharged placeholder rows and no listings. It now runs a small real search — "software engineer" on Glassdoor US, one results page, up to 5 listings, inside three minutes — charged like any run, so the first thing you see is actual output. The status line and the summary row say it was the sample and how to run your own search. If Glassdoor does not answer at that moment, you still get one uncharged row explaining it, never an empty result. Real searches are unchanged.

### 1.0.25 — run it from an AI agent, and where feedback goes

Two additions to this page, nothing else: a one-line setup for running this actor from an AI agent over MCP (pin it by URL, no store search needed), and a note on where issues and feedback go. What the actor does and what it charges are unchanged.

### 1.0.24 — continue an earlier run without paying twice

- **A run that stopped at a cap can now be continued, and nothing you already have is charged
  again.** Until this build the summary row's advice after a cap was to raise the cap and re-run —
  and the re-run, starting from an empty dataset, fetched and charged the whole head of the list a
  second time. There is now a "Continue from an earlier run" input (`resumeFromDatasetId`): give it
  the earlier run's dataset ID and every listing already in that dataset is skipped — never fetched,
  never charged — so a higher cap collects only what is new. The summary row now carries the run's
  own `datasetId` (what you paste next time) and, on a continued run, `carriedOver`: how many rows
  were skipped because you already had them; the search rows say the same per search.
- **The advice is honest again.** A capped run's summary now tells you exactly how to collect the
  rest without paying for it twice, instead of telling you to re-run.
- **A dataset that cannot be read stops the run before it fetches anything, uncharged.** Continuing
  blind could have billed you twice, so the run refuses, says why on an uncharged row, and asks you
  to check the ID.

### 1.0.23 — a bracketed note after the state no longer costs you the country

- **"New York, NY (HQ)" now reads as New York, NY, US.** The shared location parser this
  actor uses picked up one more rule: a note in brackets at the end of a location line is an
  office note, not part of the place, so the state and country underneath it are read
  normally. Glassdoor's cards rarely carry these, so most rows are unaffected — this keeps
  every actor in the family reading locations identically.

### 1.0.22 — the country on a listing is a country, and an optional field can be sent as null

- **`location` is now parsed the same way across every job actor we run, and
  `location.country` is always a real two-letter country code.** The city and region already
  split correctly here; what is new is that a card naming its own country keeps it (a Dublin
  listing found on the UK site reads `"IE"`, not `"GB"`), a postcode gets its own
  `location.postcode` field, and a state code can never be mistaken for a country. Where a
  card prints only a city, the country site you searched still fills the country in, exactly
  as before.
- **Optional fields now accept `null`.** Templated callers — n8n, agent frameworks, anything
  that fills a request from a form — send `null` for every field left unset, and the platform
  used to refuse those runs before they started. Every optional field now accepts `null` and
  reads it as "use the default"; the paste-block note that told you to avoid nulls is gone,
  because it is no longer true. `queries` is still required.

### 1.0.21 — a search Glassdoor refuses now gets one more way in, and delivers

- **When Glassdoor answers a search with an access check, the run no longer stops there.**
  Until now a search that was refused on every route came back as a single uncharged
  "please re-run" row and no listings — honest, but not what you asked for. The run now
  keeps going: it tries the same search from a different kind of connection **and** as a
  different kind of device, waits longer between tries instead of hammering, and finally
  fetches the page through a rendered browser. Your listings arrive from whichever of those
  works. Nothing about the price changes, and the "please re-run" row is now what you see
  only when every one of those has been tried.
- **A refused search waits properly before trying again.** The pause between attempts grows
  instead of staying flat, which is what a site's own protection expects to see — and the
  whole walk still stops cleanly inside the run deadline you set.
- **A temporary hiccup while reading an employer profile is retried instead of reported.**
  A dropped connection or a busy moment used to end that profile with "temporary — re-run
  to collect it". It now waits and tries once more, so the profile usually just arrives.
  Nothing was charged either way.
- **Copy that would have gone out of date on its own has been fixed.** The employer-profile
  wording now reads the same before and after the add-on's charge goes live, and the sample
  row no longer states a charge — every profile row carries `charged`, and that field is
  what actually happened to the row. The output tab now names exactly which row statuses
  carry a fee and which never do.

### 1.0.20 — clearer release notes

- **This page's notes now read more plainly.** Nothing about what this actor delivers or
  charges changed.

### 1.0.19 — suite links now point at the full live shelf

- **Every actor named in this README is now a live store link.** All 25 steadyfetch
  actors are published, so the suite tables link straight to each store page. Nothing
  about what this actor delivers or charges changed.

### 1.0.18 — a resumed run walks on to the listings it had not delivered yet

- **A run resumed after a server move keeps walking to the listings it had not yet
  delivered.** A resumed run re-reads the search from the first page, so its early pages are
  full of listings it already delivered — pages that could be mistaken for the search running
  dry, ending the run before the fresh listings further in. Re-covered ground is no longer
  read as "nothing left"; only a page with genuinely nothing on it counts toward that stop.
- **A run paused for a server move now always continues on the new server.** The pause
  itself could previously end the run as finished — directly under a status message promising
  it would continue. The promise is now kept: the run picks up where it left off, and nothing
  is delivered or charged twice.
- **Finishing an interrupted run's billing can never risk a double charge.** If settling an
  earlier attempt's delivered-but-unbilled rows is itself interrupted, the run now stops at
  the exact point of failure and records only what actually went through — a charge that may
  already have landed is never sent again, and the rest is finished by the next resume.
- **Every kind of account-side refusal from our profile provider now stops the fetches
  straight away.** 1.0.16 stopped asking when our key was refused; one further refusal shape
  could still be retried per employer. All such refusals now stand down immediately — none of
  it was ever billed to you.
- **A numeric setting sent as `null` or an empty value now means "use the default"** instead
  of being read as the smallest allowed value, so templated API inputs that send `null` for
  an untouched knob behave exactly like leaving the field out.

### 1.0.17 — the employer-profile fetch allowance counts correctly

- **The per-run allowance on our employer-profile page fetches is now measured exactly.**
  Some fetches could previously be under-counted, 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 roll-out 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 at no extra charge, 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, at no extra charge 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 at no extra charge**: 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 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
  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 `&lt;` 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.
