Scrape Upwork jobs with full client intelligence — country, total spent, payment-verified, rating, reviews, and exact applicant count. 14 filters and incremental mode that reliably emits only new or changed listings across runs.
Large searches now run reliably even on the smallest memory setting. Results are saved in
smaller batches instead of all at once, and the Actor holds less data in memory while it
filters them, so a run with several thousand results no longer stops partway through.
When a run is stopped, by you or because it reached your maximum cost per run, the Actor
now shuts down cleanly and reports how far it got, instead of ending without a word.
Unreleased — 2026-10-06
Fixed
A run with three or more searches no longer ends empty within seconds when a saved Upwork
session has stopped working. The Actor now renews the session and carries on with every
search, as it already did for runs with one or two searches.
Unreleased — 2026-10-05
Fixed
A run with a short time limit no longer fails when Upwork is slow to open a session. It
used to keep trying past the point where the run is stopped, and ended as failed with no
explanation. It now stops trying in time and ends with a message telling you to run it
again shortly.
While Upwork is slow to open a session, the log now says so about once a minute, instead
of showing nothing for several minutes.
When you start several runs at once and Upwork is slow to open a session, the other runs
now pick up the session as soon as one of them gets it, instead of each waiting for its own.
While Upwork is slow to open a session, the run's status line now says so, so a slow start
no longer looks like a stuck run.
Runs that reuse a saved Upwork session start searching straight away. When the saved
session is getting old but still works, the Actor now uses it immediately and renews it
in the background, instead of making the run wait (sometimes several minutes) for a new
one first.
Unreleased — 2026-10-03
Fixed
Search URLs with a custom budget now work. When you type your own minimum or maximum in
Upwork's budget filter (for example "$110 and up"), the copied URL used to be refused as
unreadable. The Actor now applies that budget exactly as Upwork does.
An hourly rate written with a long dash ("10—100"), a dollar sign or spaces is now read
correctly. Before, Upwork ignored it and the run searched every rate.
A budget or hourly rate that cannot be read at all now stops the run with a message that
shows the value and the format to use, instead of searching without that filter and
charging for the extra results.
Unreleased — 2026-10-02
Fixed
Fewer first runs finish with no jobs when Upwork refuses most connections. The Actor now
keeps trying for up to ten minutes before giving up (it stops early if the run's own time
limit is close, so the run can still report what happened).
Category names from Upwork's homepage now work: "Development & IT", "Writing & Translation",
"Admin & Customer Support" and "Finance & Accounting" each search all the categories they
cover. Before, they were not recognised and the run returned no jobs.
A run no longer stops when the search URL field holds an Upwork page that is not a
search and carries no filters (such as the Find Work page) while a search term is
filled in. The URL is ignored with a note in the log, and the search term and filter
fields are searched instead.
Unreleased — 2026-09-30
Fixed
Fewer runs finish with no jobs when Upwork is slow to accept a new connection. This
mostly affected an account's very first run; the Actor now keeps trying longer before
it gives up.
Unreleased — 2026-09-28
Fixed
Search URLs copied from Upwork with a multi-word client location ("United States",
"United Kingdom") now return jobs. Upwork writes these names encoded twice in its own
URLs, and the Actor read them as unknown countries and returned no jobs.
Unreleased — 2026-09-25
Fixed
Country names in Client Location now work in any letter case. Upwork only matches the
exact spelling, so "germany" returned no jobs while "Germany" returned over a thousand.
Lower- or upper-case names and three-letter codes ("DEU") are now accepted.
Category filters return the right jobs. Most category names this Actor accepted did not
match Upwork's current categories and returned no jobs, and a few returned jobs from a
different category ("Web Development" returned data-science jobs). All names now follow
Upwork's own category list, top-level names such as "Accounting & Consulting" are
accepted, matching ignores letter case, and every name accepted before still works.
A category name that Upwork does not recognise is now reported in the run log instead
of silently producing an empty run.
When a search returns no jobs and the search term contains commas, the log now explains
that Upwork reads commas as AND and shows how to match any of the terms with OR.
Changed
Max Results now defaults to 100 (was 50; the form prefilled 10).
0.6.81 — 2026-09-01
Fixed
Restored full results. Upwork withdrew the interface this Actor used to authenticate, so
from 31 August runs returned no jobs. Authentication now uses the route Upwork's own site
uses, and runs deliver the complete field set again, including client country, total spent,
payment-verified status, rating, review count and applicant count. No input changes are
needed and saved tasks keep working as before. If you run in incremental mode, the first
run after this fix delivers the jobs posted during the outage.
A run that cannot reach Upwork at all now says so. When an interface this Actor depends
on is withdrawn by Upwork, the run previously logged only "upstream temporarily
unavailable", which reads as a passing blip that will fix itself. The log now names the
real cause and states that it is not a problem with your input and that we are notified
automatically. Brief upstream blips are unchanged, and the run still finishes rather
than failing.
Unreleased — 2026-08-27
Fixed
Incremental mode now keeps your existing history wherever it can. A run started with a
restricted API key previously moved to a fresh store unconditionally, which meant
re-collecting everything once. It now uses your existing store whenever it is allowed to,
and only starts a new one if access is genuinely refused.
Updated the underlying crawler library. A single failed storage access could previously
leave a run hanging until its time limit instead of failing or recovering.
0.6.6 — 2026-08-13
Fixed
Incremental mode no longer fails with an unexplained error when it cannot open its saved state. Runs started with a restricted API key could not open the stored state that incremental mode depends on, and the run ended with a generic "please try again later" that never cleared however often it was retried. Such a run now stops with a specific message naming what to change, and incremental runs started this way use stored state the run is allowed to create, so incremental mode works rather than failing. Runs with incrementalMode off were never affected. Affected accounts rebuild their baseline once, which means one run of already-seen jobs.
0.6.5 — 2026-08-03
Fixed
Runs no longer log Attempting to charge for an unknown event 'detail-enrichment'. Detail enrichment was listed as a separately billed $0.003 addon, but that event was never actually registered for this Actor, so the charge was rejected on every attempt and the warning was written into the run log instead. Nobody was ever billed for it. The addon has been withdrawn rather than activated: detail enrichment stays covered by the standard per-result fee, the pricing table no longer advertises a charge that does not exist, and no run's cost changes.
0.6.4 — 2026-08-02
Fixed
A rejected sessionToken is no longer a silent downgrade. Previously, when Upwork rejected the supplied token, detail enrichment was switched off for the rest of the run and the only trace was a single warning line in the log — the run finished as a success with items that were quietly missing every detail-only field (screening questions, attachments, client industry, company size, activity, allowed countries, work history). The run now reports the rejection as an error, sets a run status message explaining which fields are absent and why, and records the condition in the run summary.
Newest-first runs are now genuinely newest-first. Upwork's own recency ordering is only approximate — a job posted hours earlier can appear above a newer one — so results are re-sorted by publish time before they are written. The sort is applied before the maxResults cap, so a capped run keeps the newest jobs rather than the first ones Upwork happened to list. Runs that stream results in batches are ordered within each batch.
Corrected all current guidance for obtaining the token, including the example task and detail-client diagnostics. The previously documented oauth2_global_js_token cookie carries a token issued by a different client that has no access to the detail fields. The value that works is the authorization request header on a signed-in upwork.com request. Values pasted with a leading bearer or as a whole name=value pair are accepted and cleaned up automatically.
Fixed
When the Upwork job search backend stays unavailable through every retry, the run now reports it as a temporary upstream fetch error instead of a generic unexpected failure, so retrying later is clearly the right next step.
0.6.2 — 2026-05-26
Fixed
Brief network timeouts on the initial token request no longer fail the run outright. The retry layer now also recovers from transient thrown errors (timeouts, dropped connections), not just from non-success HTTP status codes.
0.6.1 — 2026-05-25
Fixed
Upstream-transient GraphQL errors from the Upwork job search backend (e.g. brief "search failed" responses from the upstream search index) are now retried with backoff before failing the run. These previously surfaced as immediate run failures even when a retry seconds later would have succeeded.
Standardized notification formatting and delivery across all supported channels.
Run metadata in notifications now includes the human-readable search label.
0.5 — 2026-04-24
Deploy-hardening release.
Changed
Renamed the enrichment billing event from apify-actor-detail-enrichment
to detail-enrichment (event names with apify- prefix are reserved by
Apify and cannot be registered on third-party actors).
Input form titles now consistently start with an emoji prefix across all
38 fields; schema auto-patches missing emojis on known field names on
each pre-push run via the shared tooling.
README regenerated from a fresh v0.5 sample so the live store page now
shows the rich field set including client intelligence and enrichment.
Added
Per-module test files: apiClient, categories, detailClient,
detailTransform, filters, notifications, regions, scoring —
brings total to 9 test files / 84 tests.
Country-code normalization (clientCountryCode — always ISO-3) and full
ISO-3 mapping for the countries Upwork emits.
Budget currency defaults to USD when a budget amount is present (the
source response never populates currency on search-surface results).
Fixed
clientCountry canonicalized to full country name across responses so
consumers can match consistently (Upwork mixes ISO-3 codes and names in
roughly an 18/82 split).
Country alias coverage for Upwork's non-standard spellings: Congo DRC,
Puerto Rico, Palestinian Territories, Ivory Coast, Sierra Leone, Antigua
and Barbuda, Gambia, Suriname, French Polynesia, Bermuda.
Fixed authenticated detail enrichment failing before any detail fields
could be returned.
0.4.0 — 2026-04-24
Added — notifications
Per-platform webhook/bot integrations, opt-in via separate input fields.
Fires in parallel after the dataset is pushed; failures on one platform
don't affect the others.
Telegram:telegramToken + telegramChatId. Markdown-formatted
messages with title, rate/budget, client country, applicant count, and
link per job.
Discord:discordWebhookUrl. Rich embeds color-coded by
customJobScore (green for ≥4, blue otherwise), with fields for
budget, experience level, client, proposals, and score.
Slack:slackWebhookUrl. Block Kit formatting with linked titles
and one-line metadata per job.
notificationLimit caps per-batch dispatch (default 5, max 20).
includeRunMetadata prepends a run summary line when true (default).
Pairs naturally with incrementalMode — only NEW/UPDATED jobs trigger
alerts, preventing duplicate notifications on recurring runs.
Added — README cookbook
Comprehensive user-facing README.md with minimal-start example, 7
use-case recipes (low-competition, geographic, URL-mode, real-time
alerts, LLM pipeline, custom DSL), full output sample with field
descriptions, session-token extraction guide, pricing table,
filter-reference tables, region groupings, and troubleshooting.
0.3.2 — 2026-04-24
Cookie-mode hardening after live empirical testing.
Fixed
Authenticated detail enrichment no longer fails before any detail fields
can be returned.
Added
sessionToken input description now explicitly warns that the
oauth2v2_int_XXXX format is rejected with permission errors — only JWT
Bearer tokens from the oauth2_global_js_token cookie have detail-page
scope. Live probes confirmed that OAuth2 clients issuing oauth2v2_int_*
tokens are accepted as authenticated (HTTP 200) but lack scope for every
field under jobAuthDetails.
0.3.1 — 2026-04-24
Data-quality patch, discovered during a 1000-item live audit.
Added
clientCountryCode output field — canonical ISO-3 alpha code (USA, GBR,
DEU, etc.) derived from Upwork's mixed country representation.
normalizeCountry() + deriveCountryCode() helpers in src/regions.ts.
Source data can contain either ISO-3 codes (~18% — ZAF, CHE, ARE, USA)
or full country names (~82%). We now canonicalize clientCountry
to the full name in all outputs, and always populate clientCountryCode
with ISO-3 when resolvable.
ISO-3 mapping table covering 100+ countries with alias support for
Upwork's non-standard name spellings (Congo DRC, Puerto Rico, Palestinian
Territories, Ivory Coast, etc.).
Fixed
budgetCurrency was null in 100% of fixed-price jobs — Upwork's search
source doesn't populate isoCurrencyCode on fixedPriceAmount. Now defaults
to "USD" when a budget amount is present (Upwork payment layer handles
FX, so search-surface budgets are always USD).
0.3.0 — 2026-04-24
Adds opt-in cookie-auth mode (sessionToken + enrichDetails). Users who
supply their own Upwork session token (oauth2_global_js_token cookie) unlock
a per-job detail fetch that enriches output with ~30 additional fields.
sessionToken (secret) — Bearer value from oauth2_global_js_token cookie.
enrichDetails — opt-in boolean.
Concurrency control for parallel detail fetches (default 5, max 30).
Added — pricing
New PPE event apify-actor-detail-enrichment ($0.003/enriched job).
Only charged when detail data is successfully returned. Paired with
incrementalMode, only NEW/UPDATED/REAPPEARED jobs trigger the charge —
UNCHANGED jobs serve their cached enrichment from state.
Added — state
JobStateEntry.enrichment + enrichedAt persist enriched payloads across
runs. UNCHANGED jobs automatically re-emit their cached enrichment.
Security
Auth errors (401/403) gracefully disable enrichment for the rest of the
run, emit a warning, and continue without charging.
Detail enrichment avoids returning account-owner profile data in scraped
output.
0.2.0 — 2026-04-24
Major release. Expands the search data surface with richer client intelligence.
Adds 14 filters, 5 post-fetch filter types, URL-mode input, and a composite
quality score.