# Changelog of Google Jobs Scraper — Google for Jobs Listings, Apply Links (`steadyfetch/google-jobs-scraper`) Actor

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

## Changelog

### 1.0.48 — 2026-09-20

- **When the run page has to shorten its own status line, the counts of what you were NOT charged for now survive it.** That line is cut at 500 characters by the platform, so on a run where a lot went wrong the line gives up its least useful sentence to fit. It used to give up the whole "uncharged: …" clause — the numbers included — and replace it with "full counts on the rows and in OUTPUT". From this build it gives up only the WORDING of the reasons and keeps every number, each in its own unit: you read "uncharged: 4 searches, 6 input errors (each row says why)" instead of losing the count altogether. The reasons themselves are on the rows and in OUTPUT either way, exactly as before. No price, input field, output column or charged event changes.

### 1.0.47 — 2026-09-19

- **The whole "Maximum cost per run" you set is now spent on job listings.** Before this build the run held back a small share of your cost cap — up to a tenth — as a cushion for platform running costs which, on this actor's per-result pricing, your cap does not pay for. That cushion protected nothing and simply left the last few job listings unbought: a capped run delivered and charged a little less than you had authorised. The plan now uses the full cap. Prices, charged events, input fields and output columns are unchanged.
- **The first line of this page now says what you get and what it costs, instead of leaving the price eight lines down.** It opened on the size of the panel and nothing else, and the price — from $1.00 per 1,000 job listings — sat below the AI-agent block, past the fold a buyer arriving from store search actually reads. The opening line now says both in one sentence: a Google Jobs scraper that returns the whole Google for Jobs panel rather than the first ten results, at that price, and it names what one row carries. "Never pay for a job we didn't deliver" keeps its own line directly under, word for word, together with the row cap and deadline that stop a run exactly where you set it. The search fee, the repeat rule and everything else on the page are unchanged and still said in full below. This build changes nothing about the run: no input field, output column, charged event or price moved.

### 1.0.46 — 2026-09-18

- **A search Google refuses to render now waits and tries again before you are told to re-run it.** When Google will not serve its jobs panel, the page read comes back as a failure of the fetch rather than an answer about your search — and that one shape was the only refusal this actor would not wait on. So a run with a fifteen-minute limit could report "please re-run" after thirty seconds, having spent none of the time you gave it. It now takes the same patient pause it has always taken for a bot check — a jittered wait, then one more full read on a fresh route — before any such row ships. Everything that bounded that wait still bounds it: one wait per search, never more; only while the run holds the time for a real read afterwards and still keeps room to report; and only while what the search could still deliver is worth the pause. A search that returns nothing is still an answer and never waits, and a search that never rendered is still uncharged whether it waited or not. No input field, output column, charged event or price changed.
- **A page read can no longer run past the moment your run needs to stop reading.** Each read is sized to the time your run has left, minus the room it keeps to push the rows already in hand and write the summary. The read itself was then allowed ten seconds beyond that number — out of exactly the room being kept back. It now stops at the number it was given. The ten seconds go back to your run as working time, so a run can start one more search than it could before. Nothing you type changed, and no output column, event or price moved.
- **A country you spelled out in full, or as a three-letter code, now says how it was read.** "United States", "GBR" and "holland" have all been understood for a while, and were understood silently: the run happened under US, GB and NL and nothing told you so. Each one now adds a single uncharged row naming what you typed and the country it was read as, so the reading is yours to check rather than ours to assume. A value that already is the country code adds nothing, and a value this actor cannot pin Google results to is still refused exactly as before, with the row naming the countries it does cover. No input field, output column, charged event or price changed.

### 1.0.45 — 2026-09-18

- **The $0.004 search fee is live, and the copy that announced it now reads in the present tense.** Each search that returns at least one listing carries one search fee of $0.004 on top of the per-listing price. The README, the store listing, the input form and the run-row description had all announced it as a change still to come; it is being charged now, so each of them states it plainly instead. Nothing else about what you pay moved: the per-listing price is unchanged, a search that returns nothing — empty, blocked, or never fetched because your cap, your deadline or our own data budget bound first — still pays no search fee, a blocked or removed listing is still uncharged, a repeat this account already has still carries no result fee, and the same search is never charged its fee twice, even across a resumed run.

### 1.0.44 — 2026-09-16

- **A country's three-letter code now works in "Country".** The full English name and the everyday spellings ("India", "United Kingdom", "uk", "U.S.A.", "Holland", "UAE") have been read for a while; the three-letter ISO code had not, so "IND", "GBR" or "USA" was refused — and because "Country" scopes every search in the run, one refused code stopped all of them. It is read now. Nothing else about the field changed: a value this actor cannot pin Google results to is still refused with the row naming the countries it does cover, and leniency can still only ever land on one of those countries — a spelling is never turned into a market this actor does not support. No input field, output column, charged event or price changed.

### 1.0.43 — 2026-09-14

- **A token that can read your repeat memory but not write to it no longer costs you the same listings — and the same search fees — all over again.** This actor remembers every listing it delivers for your account so a re-run skips what you already have, never charges for it twice, and pays no search fee for a search that returned only those. Permission to **read** a key-value store and permission to **write** one are granted separately, so a run started with a scoped API token could open that memory, recognise your repeats perfectly, and still be refused every update to it. Nothing said so: the run looked completely normal, and your next run collected and charged for all of it again, search fees included. The run page now says the memory could not be updated and what that will cost next time, one uncharged row in your dataset says the same thing in full and names the permission to grant, and the run log says it beside the platform's own message — the moment the first refusal happens, not at the end of the run. Give the token key-value store **Write** (and **Create**) under *Settings → API & Integrations*, or set Actor runs to *Full access*. A run whose memory writes normally is unchanged in every respect. Nothing you type, no output column, no event and no price moved, and the run still delivers and charges exactly as it did.
- **A run started with a limited API token now tells you why it charged you for listings you already had.** This actor remembers every listing it has delivered to your account, in a key-value store in your own account, so a re-run skips what you already have, never charges for it twice, and pays no search fee for a search that returned only those. A run started with a scoped API token in restricted-access mode cannot open that store at all unless the token is allowed key-value store **Read, Write and Create** — and when it cannot, nothing can be recognised as a repeat, so everything is collected and charged again and those searches pay their fee. The run page used to say only that the repeat check was unavailable, which gave you nothing to do about it. It now names the cause and the fix, one uncharged row at the top of your dataset says the same thing (so an agent reading only rows sees it too), and the run log says it beside the platform's own message. Grant the token those three permissions under *Settings → API & Integrations*, or set Actor runs to *Full access*, or start the run from the console. Every other reason the store cannot be read reads exactly as before. Nothing you type, no output column, no event and no price moved, and the run still delivers and charges exactly as it did.

### 1.0.42 — 2026-09-13

- **The store card now says what a listing costs, and names the search fee that starts on 16 September 2026.** The card described what the actor does and carried no price at all, so a buyer comparing actors in the search results could not compare this one on price — and the search fee, already on the page and in the input form, had never reached the card. It now reads: from $1.00 per 1,000 job listings, and from 16 September 2026 a search that returns listings adds a $0.004 fee. A search that returns nothing still pays none of it. Nothing else moved: no input field, output column, charged event or price changed, and the fee itself starts on the date already filed.

### 1.0.41 — 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": ["software engineer jobs in austin"], "country": "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, repeats the search fee that starts on 16 September 2026 and the rule that a search returning nothing pays none of it, 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.40 — 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.39 — 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 or a run time outside 30–3,600 seconds 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. Both 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. Nothing about a run inside the limits is different, and the note row is never charged.
- **This actor has a new title on the store: Google Jobs Scraper — Google for Jobs Listings, Apply Links.** Same actor, same id, same URL, same input, same output, same price — only the words on the listing changed, so nothing you have saved, scheduled or wired into an integration needs touching. The title now carries the phrase buyers actually search for, which is the only reason it moved.
- **The cost of the try-it run is now stated as a real ceiling, not a guess.** Start this actor with nothing filled in and it delivers up to five listings; this page used to call that "under a cent". On the Apify free plan those five listings come to about two cents, and from 16 September 2026, with the search fee for the one search, about two and a half. The page now says at most $0.03 on the Apify free plan and less on paid plans — an upper bound you can hold us to. Nothing about what a run charges changed: the same listings at the same price, and still nothing charged for a job we did not deliver.
- Nothing else moved: no input field, output column, event or price changed.

### 1.0.38 — 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.37 — 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.36 — 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.35 — 2026-09-09

- **The run page decides "did something go wrong?" from what actually happened, not from an empty row count.** A re-run whose every listing this account already had was already treated as a clean ending; that judgement now comes from one shared rule the whole jobs family uses, so a clean ending has to be named rather than guessed at. Nothing you see on a run changes today.
- Nothing else moved: no price, event or output column changed, and only delivered results are charged.

### 1.0.34 — 2026-09-06

- **A block of links pasted into the searches box now comes back with an uncharged row saying so.** "Job searches" takes a job title or a search phrase. A whole block of links pasted into a single row used to be searched as one very long phrase, which can only come back empty. It is now answered before anything runs with a single uncharged row that says how many links it found and what the box takes.
- **Your search text is never split apart.** Only you know whether "red running shoes" is one search or three, so nothing is guessed — a row holding words rather than links runs exactly as before.
- Nothing else moved: no price, event, output column or charge changed, and only delivered results are charged.

### 1.0.33 — 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. Each is now counted under the limit that stopped it, 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.32 — 2026-09-06

- **Listings on a panel already fetched when the run's time limit lands are now delivered, never dropped.** Fetching a Google jobs panel is the expensive step; writing its listings out costs nothing more. When the clock ran out on that fetch, the run used to stop part-way through the panel — and where the search fee applies, you were charged the full search while getting a short list from a panel we had read in full. Every listing on a panel this run fetched is now delivered, charged once each exactly as before.
- **What a time limit means has not changed.** It still ends the *collecting* — no further panel 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.31 — 2026-09-05

- **A search that turns up nothing, and a listing already in your account, are now counted against what the run asked for.** Every run of this actor keeps 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 33 listings recorded itself as owing sixty-seven rows nobody had ever asked for — and a listing skipped because an earlier run of yours had already delivered it was recorded as nothing at all. The run now asks for exactly what its panels listed, and names every listing it did not deliver. Nothing about your rows or your bill changed: the same listings are delivered, the same ones are charged, a repeat is still skipped and never 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 counted 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.

### 1.0.30 — 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 Google Jobs US, up to 5 listings, charged like any run. Google sometimes refuses every connection for a few minutes at a time, and the two looks a search gets can take three minutes between them — the whole of the sample's own three-minute limit. So the wait-and-look-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 nine minutes, sized to hold both looks, one pause, one more verified look, and enough time left over to stop cleanly and report. A sample Google answers still finishes in under a minute; only a blocked one uses the extra time, and nothing is charged unless listings are delivered. This replaces the line in 1.0.29 that said the no-input sample keeps its own window.
- **How much of its remaining time one blocked search may spend waiting now follows the size of what you asked for.** A run with other searches still to make keeps exactly the limit it had — no single search may spend more than half the time left on waiting — so a wide block still leaves the later searches their share of the run. A run with a single search to make — the no-input sample, or one search term — may spend what is left on its one pause, because nothing else is waiting for that time. Every other bound is unchanged: one waiting look per search, never into the time kept back for reporting, and never when the run's own data allowance could not buy the look.
- **A no-input sample is now always a real, fresh search.** It no longer checks, or adds 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. Every no-input sample now fetches live and charges for the few listings it delivers.

### 1.0.29 — 2026-09-05

- **A search Google walls is now waited out inside your own time budget, instead of being handed straight back for a re-run.** Until now a search refused on both of its looks ended 30 to 90 seconds into a run that still held almost all of its time, and shipped its uncharged "please re-run" row — on a wall that usually lifts within minutes. Now, while the run still holds real time and the listings still open can pay for the idle minutes, it waits a minute and a half to three and takes one more verified look. A search that answers after the wait delivers its listings exactly as before, charged per listing. A search still refused ships the same honest uncharged row, which now says what was tried: how long the run waited and how many passes it made.
- **What never waits:** a real "no jobs matched", a search Google answered with nothing, an address that is not there, and the run's own row, time and cost limits — those are answers, and a wait cannot change them. A run near its platform time limit never starts a look it cannot finish, so nothing is lost half-way.
- **Short runs are unchanged.** A wait and one more look must fit inside what the run has left, with time kept back to finish and report, so a 60-second run takes its two looks exactly as before, and the no-input sample keeps its own window.
- **The waiting look is paid for out of the run's own data allowance**, not by raising the per-search cost: the run budgets a small extra pool for patient looks inside the same limit your row cap and charge budget already set, so a run can never spend more on data than it can bill.
- **Charges are unchanged:** waiting is uncharged, a refused search is uncharged, and only delivered listings are charged.
- The summary row carries what the waits bought — passes, seconds waited, how many walls lifted — and the run log says it in one line.

### 1.0.28 — 2026-09-04

- **Fixes a wrong delivered count in 1.0.27.** On a filter-only sample run the uncharged note row was counted as a delivered listing: the run's record said one listing delivered when the dataset held none, and the row cap had one fewer slot than you asked for. Nothing was ever charged for it. Counts and caps are right again; the note row is bookkeeping and always was.
- **The run log no longer opens by saying nothing was set on a run where something was.** On a filter-only sample run the first line of your run log still read "Nothing was set — running the default sample"; it now says your settings were kept and names them.

### 1.0.27 — 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 Google Jobs US — 5 listings, charged like any run — so you see real rows. Set one thing first, though — a country, a location, a row cap — and leave **Job searches** empty, and you used to get a single row asking for searches 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" 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.
- **Two things still answer with guidance instead of rows**, unchanged: a field name this actor does not recognise, and a search list you set and left empty. A country Google Jobs has no site for is still refused before anything is fetched.
- No row on such a run opens by saying nothing was set — it 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.26 — 2026-09-04

- **"Country" now reads the country you actually wrote.** `United States`, `USA`, `uk`, `United Kingdom`, `Germany` and `Canada` all land on the country they name; only the two-letter code used to be accepted, so an ordinary spelling was refused as a typo and every search in the run was dropped with it.
- **A country this actor cannot pin Google to no longer swallows your whole search list.** Every search 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 that this actor cannot pin Google results to says so and names the nearest countries 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 results are pinned one country 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`, `country` 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 reports the searches you asked for**, counted before anything was refused, instead of the number of refusals.

### 1.0.25 — 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 `google-jobs-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, no bite out of your row cap — and the status line says how many. The **search fee** follows the same rule: a search that returns only listings you already have pays none. 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.
- **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. A run stopped that way now says so in one short sentence on the run page and keeps the full explanation on its row, where nothing is cut.
- A search whose every listing you already have is reported as a real search that ran — never as an unreadable panel — and no longer asks you to open an issue.

### 1.0.24 — 2026-09-04

- **A search that comes back walled now gets its second look on the smallest runs too.** A run capped at one result could previously afford only a single fetch per search, so a page that answered with a block instead of jobs was reported rather than retried. The retry the run already budgets is now always affordable.

### 1.0.23 — 2026-09-04

- **A run that stops before it starts now reports its outcome too** — a run refused for its memory setting, and a run stopped because this build was missing a credential of ours, now reach us the same way every other run does: counts and reason codes only, never your input or your rows.

### 1.0.22 — 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.
- **A run that cannot bill gets a bounded page of results** instead of unlimited vendor fetches.
- **Stop guidance names the run-wide row cap first.**

### 1.0.21 — 2026-09-04

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

### 1.0.20 — 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.19 — 2026-09-04

- **The run page points at the Issues tab on every ending that went wrong**, including a run refused before it started.

### 1.0.18 — 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.17 — 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.16 — suite notes name the three delivery-conditional fees

Copy only: the jobs-suite and shelf blocks at the bottom of this page now say what the whole shelf's pricing contract is in the same words on every actor — nothing charged for starting a run, charged only on delivery, and the three actors that add a small delivery-conditional fee from 16 September 2026 (this actor's search fee among them). What the actor does and what it charges are unchanged from 1.0.15.

### 1.0.15 — one search fee per search that returns listings, from 16 September 2026

From 16 September 2026 each search that returns at least one job listing carries one search fee of $0.004, shown on that search's own row with `charged: true` and counted in the summary row's `chargedEvents` next to the listings. The per-listing prices and the "from $1.00 per 1,000" header are unchanged. A search that returns nothing — an empty panel, an access check, an unreadable page — pays no search fee, and a search the run never fetched because your cap, your deadline or our own data budget bound first pays nothing at all: the run now checks that your cost cap can cover the fee plus one listing before it fetches a search, and stops cleanly with the usual `resumeCursor` when it cannot. The same search is never charged its fee twice, including across a run that was moved between servers. Until the 16th this build behaves exactly like the previous one.

### 1.0.14 — 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. 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.13 — 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 Google Jobs US, 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 Google does not answer at that moment, you still get one uncharged row explaining it, never an empty result. Real searches are unchanged.

### 1.0.12 — 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.11 — 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.10 — a bracketed note after the state no longer costs you the country

- **"Austin, TX (Hybrid)" now reads as Austin, TX, 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 instead of the whole tail being treated as one unknown fragment.

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

- **`location.country` is now a real two-letter country code, or empty — never a state.**
  Google's panel prints "Seattle, WA" and "Chicago, IL", and until now the piece after the
  comma was shipped as the country: `"WA"`, `"IL"` — and `"IL"` is Israel's country code, so
  a buyer filtering by country could not see the trap. Those now read country `"US"` with the
  state in `location.region`, a postcode gets its own `location.postcode` field, and the
  country of the search decides the cases that are genuinely ambiguous. A line naming its own
  country keeps it ("Spain", "Netherlands", "United States"), and a line naming no country we
  can prove leaves the field empty rather than guessing — Google's untouched line is always in
  `location.raw`.
- **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 Country dropdown included, which needed its list of
  choices checked by the actor rather than by the platform to get there. It is the same
  dropdown with the same choices, and a country we cannot pin Google results to is still
  reported on its own uncharged row rather than quietly swapped for another. `queries` is
  still required.

### 1.0.8 — 2026-08-31

- **A search that comes back blocked or unreadable is now fetched again before it reports a
  miss.** Google's access checks are intermittent, and a single one used to be enough to turn a
  search into an uncharged "please re-run" row on your first run. Each search now gets a second
  attempt on our side, so a search that would have reported a block frequently delivers its full
  panel instead. The second attempt is on us, there is still no result fee when both come up
  short, and your `maxItems` and `maxRunSeconds` are unaffected.
- **A search that ran and matched nothing now says why on its own row.** The row already read
  `outcome: empty` and `delivered: 0`, but `missReason` was left blank — so a genuine empty
  answer was indistinguishable from an ordinary delivery of zero in the one column you reconcile
  a search against. It now reads `CLEAN_EMPTY`, matching what this page has always documented.
  Uncharged either way.
- **A panel whose job cards we cannot read is no longer reported as an empty search.** If Google
  changes the panel's markup, the page still arrives looking healthy and our reader finds nothing
  in it. That used to ship as "returned nothing" — a permanent-sounding verdict about your search
  for a problem that is ours. It now ships as an uncharged, re-runnable row that says the page
  could not be read, and it is listed in the run's errors.
- API note: sending `null` for a numeric setting has meant "use the default" since 1.0.5. The
  paste-block on this page still said not to send null; it now matches what the actor does.

### 1.0.7 — 2026-08-30

This page now states the price up front — from $1.00/1,000 job listings, all-inclusive pay per
event, no start fee, charged only on delivery — the same figure the store header and the Pricing
tab already showed. Nothing about what this actor delivers or charges changed.

### 1.0.6 — 2026-08-30

Suite links now point at the full live shelf — every actor named in this README is a live
store link. Nothing about what this actor delivers or charges changed.

### 1.0.5 — 2026-08-30

- **Finishing an interrupted run's billing can never risk a double charge.** A run that
  resumes after an interruption first settles anything its earlier attempt delivered but had
  not yet billed. If that settlement is itself interrupted part-way, 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 anything genuinely unbilled is finished by the
  next resume. As always, `charged` never exceeds what was delivered.
- **Every kind of account-side refusal from our data-access provider now stops the run's
  searches straight away**, with the same clean stop that names the limit as ours, says
  nothing was charged for the searches it stopped, and asks you to re-run. Previously one
  refusal shape could still be retried before the stop engaged.
- **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.4 — 2026-08-29

- **When the limit that stopped a run is ours, the run now says so.** If our own data access
  runs out mid-run, the summary used to report it the same way it reports your cost cap being
  reached — so the advice was to raise your maximum total charge, which could not have helped.
  That stop now names itself as ours, says nothing was charged for the searches it stopped, and
  asks you to re-run instead of to change a setting.
- **`charged: true` can no longer appear on a row that was not billed.** Right at a cost cap it
  was possible for a listing to ship marked as charged while the charge itself was declined. The
  run now checks that a listing can actually be billed before delivering it, so the `charged`
  column always reconciles against the invoice.
- **A search stopped before it could deliver is no longer lost.** When `maxItems` or the run
  clock ran out between fetching a search and reading its first listing, that search vanished:
  no row, and missing from the summary's `resumeCursor` too. It now ships its own uncharged row
  and stays in the resume list, so you can pick it up on the next run.
- **The summary's `chargedEvents` now covers the whole run, not just the last server.** On a run
  that moved between servers it reported only the final server's charges, so it disagreed with
  the `delivered` count in the same row and read like an overbilling.
- **A run moved between servers now records what each part of it delivered.** Where a search was
  interrupted, only the first attempt's partial figures used to be written, and the listings the
  run delivered afterwards had no search row at all.
- **The run no longer starts a search it cannot finish before the run timeout.** A search begun
  too close to the timeout could be cut off mid-flight, ending the run with no summary row. It
  now stops cleanly first, with the summary and resume cursor intact.
- **One malformed job card can no longer fail the whole run.** A single unreadable link in
  Google's panel used to end the run early and lose every remaining search; that card is now
  delivered like any other.
- README: the full steadyfetch suite shelf, and a link to a live example dataset from a real run.

### 1.0.3 — 2026-08-29

- **A typo in an input field name now gets a helpful pointer instead of sample rows.** If you
  send your searches under a field this actor does not have — `query` instead of `queries`, say —
  the run used to look identical to an empty run and answered with the uncharged sample rows, so
  a typo looked like it had worked. It now returns one uncharged row that names the field it did
  not recognise, names the field your searches belong in, and shows the exact shape to send.
  Sample rows are reserved for a genuinely untouched input, and nothing is charged either way.

### 1.0.2 — 2026-08-28

- First release. One search returns Google's full jobs panel — about 90–130 job cards
  where Google has that many; niche searches return every card Google has.
- Every row: title, company, location, source board (`via`), posted age, salary when
  Google shows one, employment type, the job's full description text, and the real
  apply links — every board Google's "Apply" buttons point at, in order.
- The same job reached by two searches is delivered and charged once.
- You are never charged for a job that was not delivered: blocked or unreadable pages
  ship as uncharged re-runnable rows, an empty panel is an uncharged real answer, and
  `maxItems` / `maxRunSeconds` stop the run cleanly with a summary row and a resume
  cursor.
- A run that is interrupted or moved between servers continues from its own dataset:
  nothing already delivered is fetched, delivered or charged again.
- With no input, the run returns uncharged sample rows showing the exact output shape.
