Scrape Upwork job search results: title, budget, skills, experience level and posting time, from keywords or search URLs. Public search first. MCP-ready.
Own route first: Upwork's public (signed-out) job search via Apify's unblocking proxy (sort, contract type and tier applied by Upwork; minHourlyRate applied to rows). Client fields, proposals, connects and postedAtRaw are null on public rows. The signed-in fallback route serves searches the public reader could not serve, searches with paymentVerified, minClientSpend or cookies, and pasted URLs with payment_verified/hourly_rate/client_hires/amount. minClientSpend and paymentVerified are now enforced on fallback rows before saving (rows that fail or have unknown values are dropped, not charged). Rows are written and charged in one SDK call; a row refused at the caller's charge limit is no longer counted. The Actor no longer charges apify-actor-start itself; OUTPUT reports the platform's per-GB start count. OUTPUT/RUN_SUMMARY add routeServed and per-route counts. A verified empty public search ends VALID_EMPTY (was UPSTREAM_FAILED). sortBy relevance uses Upwork's signed-out default order instead of a login-walled sort parameter. Fallback rows now carry the canonical job URL (https://www.upwork.com/jobs/~id ) and a plain-text title.
1.2 — 2026-09-17
Managed signed-in Upwork route. PPE job-found $0.035. Honest outcomes.