Upwork Job Scraper (Pay Per Result)
Pricing
from $6.50 / 1,000 jobs
Upwork Job Scraper (Pay Per Result)
Scrape recent and relevant Upwork.com jobs, bypasses captchas.
Pricing
from $6.50 / 1,000 jobs
Rating
0.0
(0)
Developer
Matthew James
Maintained by CommunityActor stats
4
Bookmarked
77
Total users
4
Monthly active users
5 days ago
Last modified
Categories
Share
Upwork Job Scraper
Upwork Job Scraper
Upwork Job Scraper is designed to scrape job listings from Upwork and return structured data to help you analyze Upwork job postings.
Features
- Custom Cookies: Use your cookies to get more detailed scrape data.
- Custom Proxies: Use Apify's proxies or your own.
- Detailed Data (Cookied Scrapes): Get more insight into job postings when you use your cookies.
- Customizable Search: Use specific URLs or define search queries with advanced filters.
- Flexible Pagination: Control the number of jobs per page and the number of pages to scrape.
- Advanced Filtering: Refine job searches based on experience level, project length, hours per week, hourly rates, and fixed rates.
Notes
- For best results use RESIDENTIAL PROXIES and COOKIES
- Custom search URL: will override any other filters set within the input configuration, make sure this is blank if you're using the input filters.
- Search query: is ignored when a custom search URL is provided.
- Cookies: Export the full Upwork cookie set from a logged-in browser session for detailed results. Apify encrypts saved cookie input. If the Upwork authentication cookies are incomplete, the scraper will continue with standard results. Cloudflare clearance cookies are challenge state and are not required to recognize an authenticated Upwork session. Make sure to rotate fresh cookies at least once every 24 hours (since they expire).
- Cookieless Scrapes: Will not yield as many results and pages as cookied scrapes.
- Filter compatibility: Authenticated and cookieless runs use the same input filters. Authenticated runs send supported filters upstream. Cookieless runs apply the filters to each returned page because the visitor GraphQL input contract is smaller.
- Values that can't be retrieved will be
null.
Invalid inputs fail before the browser starts. A custom URL must be an HTTPS Upwork job-search URL. Page counts must be between 1 and 100, custom URL page sizes must be between 1 and 50, budgets cannot be negative, and each minimum budget must not exceed its maximum.
Cookieless Scrape Data Headers
- Date Scraped
- Job ID
- Time Posted
- Project Payment Type
- Budget
- Skill Level
- Skills
- Title
- URL
- Description
- Project Length
- Weekly Hours
Cookieless Scrape Result Example
{"Date Scraped": "2025-01-16T12:09:25.591Z","Job ID": "7769492930342982627","Time Posted": "2025-01-16T12:07:35.591Z","Project Payment Type": "Hourly","Budget": "$20.00 - $40.00","Skill Level": "Entry Level","Skills": ["Administrative Support", "Microsoft Excel", "Microsoft Word", "Google Workspace", "Data Entry"],"Title": "Administrative Assistant Needed for Letter Mailing in New York, USA","URL": "https://www.upwork.com/url","Description": "Lorem ipsum","Location": null,"Total Spent": null,"Feedback": null,"Proposals": null,"Project Length": "3 to 6 months","Weekly Hours": "Less than 30 hrs/week"}
Cookied Scrape Additional Data Headers
- Location
- Total Spent
- Feedback
- Proposals
Cookied Scrape Result Example
{"Date Scraped": "2025-01-18T10:16:41.649Z","Job ID": "7769492930342982627","Time Posted": "2025-01-17T10:12:21.649Z","Project Payment Type": "Hourly","Budget": "$7.00 - $25.00","Skill Level": "Intermediate","Title": "Web Scraping Specialist for Real Estate Data","URL": "https://www.upwork.com/jobs/url","Description": "Lorem ipsum","Location": "United States","Total Spent": "$4,588.28","Feedback": 4.99,"Proposals": 8,"Project Length": "Less than 1 month","Weekly Hours": "Less than 30 hrs/week","Skills": ["Python","Statistical Analysis","Microsoft Excel","Machine Learning","CI/CD","Data Engineering","Deep Learning"]}
Other Actors
For heavy users: https://apify.com/matthewjames/upwork-job-scraper For light users: https://apify.com/matthewjames/upwork-job-scraper-pay-per-result
Deployment and billing
This repository is the shared source for both Actors. Both Apify Actors automatically deploy from each push to main, so every production change must remain compatible with both products.
The Actors use the same input, scraping, filtering, and default-dataset output code. Apify configuration supplies the billing difference:
- the monthly Actor uses its monthly rental configuration;
- the pay-per-result Actor uses the
apify-default-dataset-itemevent; - each saved job is one default-dataset item, so filtering happens before pay-per-result billing;
- the runtime does not need a separate billing branch or an explicit
Actor.charge()call for dataset items.
After a push to main, verify that both Apify builds use the pushed commit and finish successfully.
Private staging deployment
Use the private akzidenzgrotezk/apify-upwork-job-scraper-staging repository for hosted validation before a production release. Its staging branch automatically builds the private matthewjames/upwork-job-scraper-staging Actor. A staging build does not start an Actor run.
The local remotes have separate purposes:
staging-originreceives thestagingbranch and builds only the private staging Actor;originis the production repository, andorigin/mainbuilds both customer-facing Actors.
Push staging work with git push staging-origin staging. Do not push test branches to origin. The production repository webhooks rebuild both production Actors for every repository push, even when their configured source remains main.
After hosted staging validation succeeds, move the validated commits to main. Then push main to origin and verify both production builds.
Visitor runtime behavior
Unauthenticated runs still start at the Upwork homepage. The homepage initializes the visitor session before the Actor opens the prepared search URL.
If the search navigation returns an access-control status, the Actor waits for the browser challenge cookie to change. The Actor then builds minimal GraphQL headers from the browser session. A ten-second cookie fallback remains for sessions that do not expose a clear challenge-state change.
After session setup, visitor mode blocks all new browser traffic except the expected visitorJobSearch query. The gate must be armed once for each requested page. This design prevents background browser traffic from continuing during API pagination and prevents unintended GraphQL operations.
Visitor GraphQL requests use only the proven shared variables for query text, sort order, highlighting, and paging. Authenticated requests also send the captured experience and budget fields upstream. Both modes apply every shared filter locally before saving results. Project length and weekly hours remain local in both modes.
If a later page invalidates the browser session, the retry resumes from the unfinished page. The Actor keeps a serializable page checkpoint and skips job IDs that were already saved. A retry does not restart completed pages or add another dataset item for the same saved job.
Only a valid empty results array means pagination is exhausted. A missing or invalid results path triggers retry handling instead of silently returning an incomplete dataset. A nonempty page shorter than the requested page size is saved and treated as the final page, so the Actor does not make an unnecessary empty-page request.
Hosted validation on August 22, 2026 confirmed this behavior in a ten-page visitor run. Pages 3 and 5 each retried from the failed page, no completed page restarted, and the run saved 308 jobs before Upwork reported no more results on page 8.
Private staging validation on August 22, 2026 confirmed the short-page optimization on build 0.1.5 and run WoMtATUYqboDDoURi. Pages 3 and 7 retried from the failed page, the run saved 307 unique jobs with the unchanged 16-field contract, and page 7 returned seven jobs. The Actor completed without requesting page 8. The run output linked to the schema-backed default dataset.
Production unauthenticated validation on August 22, 2026 confirmed the final visitor path on monthly build 3.0.29, commit c66d898b4ebf49219d139a748ba9667d9e51280f, and run Z7sJViAwHVMLJwvdr. The residential run used no cookies and saved 500 unique jobs across ten requested pages in approximately 61 seconds. Transient failures on pages 3 and 6 resumed from those pages without restarting completed work or creating duplicate dataset items. Every row preserved the 16-field contract and required core fields. Authenticated-only client fields remained null as expected.
Authenticated staging validation on August 22, 2026 confirmed cookie handling on build 0.1.7 and run 09y5xcIndenwskcL7. Five sequential pages saved 250 unique jobs without retries or duplicates. All 16 fields were present, and authenticated client fields were populated when Upwork supplied them. The validated cookie export did not contain cf_clearance; the Actor correctly treated that Cloudflare cookie as optional challenge state.
Authenticated filter validation on August 22, 2026 confirmed the final filter contract on private staging build 0.1.11 and run EluVjWm3Y81W6vhK9. One authenticated request returned ten jobs and saved three jobs after local filtering, with zero retries. Every saved row matched the selected experience, project length, weekly hours, and matching job-type budget range. All rows preserved the 16-field contract, had unique job IDs and URLs, and contained the authenticated client fields supplied by Upwork.
The crawler is explicitly limited to one request handler at a time with minConcurrency: 1 and maxConcurrency: 1. One initial browser request owns the complete GraphQL page loop, and every page and retry runs sequentially.
Output contract
The Actor output links to the run's default dataset. The dataset schema documents and displays the existing 16 fields without renaming or removing fields. Each saved job remains one default-dataset item for monthly and pay-per-result compatibility.
Local production-path capture
Use npm run capture:local for one bounded, unauthenticated diagnostic run through the current Actor flow.
The command reads storage/key_value_stores/default/INPUT.json and .env.local. It stops before launch unless the input meets these limits:
- one page;
- 1 to 10 jobs;
- start page 1;
- no cookies;
- Apify
RESIDENTIALproxy group; - no custom proxy URLs.
Capture mode uses the existing crawler, resource routing, mode detection, search navigation, GraphQL session setup, replay, filtering, and dataset output. Capture mode adds no request blocks. The normal visitor request gate starts only after session setup. Capture mode forces zero retries so one command cannot rotate through several proxy sessions.
Evidence is saved under tmp/captures/<run-id>/. The attempt summary and manifest contain sanitized shapes and counts. Files under raw/ can contain request bodies, response bodies, cookies, tokens, job data, and proxy details. Keep the full directory local. The directory is ignored by Git.
This command does not deploy or change the Actor hosted on Apify. Normal runs do not enable capture behavior.