Upwork Job Scraper
Pricing
from $35.00 / 1,000 job returneds
Upwork Job Scraper
Scrape Upwork job search results: title, budget, skills, experience level and posting time, from keywords or search URLs. Public search first. MCP-ready.
Pricing
from $35.00 / 1,000 job returneds
Rating
0.0
(0)
Developer
Khadin Akbar
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
2
Monthly active users
9 days ago
Last modified
Categories
Share
What is Upwork Job Scraper — Keyword & URL Job Search?
Upwork Job Scraper helps freelancers, agencies, and AI agents pull structured Upwork job-search rows — title, description, budget or hourly range, skills, experience level, duration, engagement, posting time, and permalinks — from keywords or Upwork nx/search/jobs URLs. It runs on Apify (API, schedules, integrations, MCP) so you can monitor niches without manual browsing.
Two routes, and what each row contains
-
Public search reader (primary). The Actor reads Upwork's public job search page — the results a signed-out visitor sees, about 10 jobs per page — through Apify's unblocking proxy. Sort, contract type and experience level are applied by Upwork (for
sortBy: relevancethe public search uses Upwork's default best-match order);minHourlyRateis applied to the returned rows. These rows havesource: "upwork-public-search". -
Signed-in fallback route. A managed, signed-in job source. A search uses it only when:
- the public reader could not serve that search (blocked, or sent to a login page), or
- you set
paymentVerified: trueorminClientSpendto1k/10k, or - you supply
cookies, or - a pasted
startUrlsURL carries a signed-in filter parameter:payment_verified,hourly_rate,client_hiresoramount.
On fallback rows,
paymentVerifiedandminClientSpendare checked again before saving; rows that fail them, or whose client spend is unknown, are dropped and not charged.
Fields that are null on public-search rows. Upwork hides these from signed-out visitors, so the public reader leaves them null instead of guessing:
| Field | Public-search rows | Fallback rows |
|---|---|---|
clientCountry, clientCity | always null | filled when Upwork shows them |
clientSpend | always null | filled (USD) |
clientPaymentVerified | always null | filled |
clientRating, clientHires, clientReviewsCount | always null | filled when shown |
proposalsRange | always null | filled when shown |
connects | always null | usually null |
postedAtRaw | always null | relative time text when shown |
budget, hourlyRange and fixedPrice are null on either route when the posting states no budget or rate. If you need client signals on every row, set paymentVerified: true or a minClientSpend threshold (both also filter the results); otherwise most rows come from the public search and carry no client data.
Optional caller session cookies are used only by the fallback route for a single run; they stay out of the dataset, OUTPUT, and logs.
Upwork is a trademark of its owner. This independent Actor is not affiliated with, associated with, or endorsed by Upwork Global Inc.
Best fit
Use this Actor when you need fresh Upwork job cards for monitoring, lead lists, or agent workflows. For multi-board aggregation across Indeed/LinkedIn/Glassdoor, route to Jobs Scraper (multi-board). For Indeed-only detail enrichment, use Indeed Job Scraper (Indeed detail). For Google Jobs SERP cards, use Google Jobs Scraper (Google Jobs SERP).
Workflow story
A freelance agency lead wants new javascript developer posts on a schedule. They save this Actor with searchQueries: ["javascript developer"], sortBy: "recency", and a bounded maxResults. Each execution returns normalized rows with title, budget, skills and permalink, then Zapier/Make routes the best matches into Slack. When the niche is quiet, the Actor completes with VALID_EMPTY and zero job charges instead of inventing rows.
How to scrape Upwork jobs
- Open this Actor in Apify Console
- Enter one or more
searchQueriesor an Upworknx/search/jobsURL instartUrls - Set
maxResultsfor the quality sample - Start the Actor — download JSON / CSV / Excel from the dataset
Input
See the Input tab. Provide at least one keyword or a valid Upwork search URL.
{"searchQueries": ["javascript developer"],"maxResults": 5,"maxPagesPerQuery": 1,"sortBy": "recency","jobType": "all","experienceLevel": "all","paymentVerified": false}
Optional secret field cookies: JSON array of {"name","value"} session cookies for this run only (excluded from dataset and OUTPUT).
Output
One dataset row per job:
{"jobId": "~022100516022389626945","title": "Looking for Skilled AI Engineers","jobUrl": "https://www.upwork.com/jobs/~022100516022389626945","description": "Build agents...","jobType": "Hourly","budget": "$15-$55 / hr","hourlyRange": { "min": 15, "max": 55, "currency": "USD" },"experienceLevel": "Expert","duration": "1 to 3 months","postedAt": "2026-01-15T12:00:00.000Z","skills": ["Node.js", "AI"],"clientCountry": null,"clientPaymentVerified": null,"clientSpend": null,"source": "upwork-public-search"}
Data fields
| Field | Description |
|---|---|
| jobId | Upwork ciphertext id (~…) |
| title / jobUrl / description | Core posting |
| jobType / budget / hourlyRange / fixedPrice | Compensation |
| experienceLevel / duration / engagement | Scope labels |
| postedAtRaw / postedAt | Relative + ISO timestamps |
| skills | Tagged skills |
| proposalsRange / connects | Marketplace signals (null on public-search rows) |
| clientCountry / clientCity / clientPaymentVerified / clientSpend / clientRating / clientHires / clientReviewsCount | Client signals (null on public-search rows; filled on fallback rows) |
| source | upwork-public-search (public search) or fallback-provider (signed-in fallback route) |
Pricing
Monetization is Pay per event + platform usage (usage paid by the run user). The live Pricing tab in Apify Console is the current source of truth for event prices.
Typical events:
| Event | Role |
|---|---|
apify-actor-start | Charged by the platform at run start: one event per GB of run memory (minimum one) |
job-found | Primary event — one charge per unique job row saved, from either route |
The job price is the same for public-search rows and fallback rows, although public-search rows carry no client data (see the table above). Invalid inputs, empty matches and rows dropped by minClientSpend/paymentVerified are not charged job events. Platform usage, including the unblocking proxy used by the public search, is billed to your account as usual. A search served by the fallback route also runs a separate managed job source; that cost is covered by the Actor owner, not added to your bill.
API example
curl "https://api.apify.com/v2/acts/khadinakbar~upwork-job-scraper/runs?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"searchQueries":["javascript developer"],"maxResults":5,"sortBy":"recency"}'
MCP / agent prompt card
Tool: apify--upwork-job-scraper
Example request:
Search Upwork for recent javascript developer jobs and return title, budget, skills, postedAt and jobUrl. Cap results with maxResults.
After completion: read the default dataset, confirm OUTPUT.outcome is COMPLETE or VALID_EMPTY, and reconcile job-found charges with row count. Provenance is in source: upwork-public-search for the public search reader; fallback-provider for fallback rows. Client fields are null on public-search rows; to get them, set paymentVerified: true or minClientSpend. Scope is public job-search cards — not proposals, messages, or private client PII. Paginate with maxPagesPerQuery only when you need depth beyond the first page.
Limits and recovery
Provide specific keywords or a valid Upwork search URL. Confirm OUTPUT.outcome after each execution. Schedule monitoring with bounded maxResults. Source data is current public job-search cards, read by the own public search reader first and the fallback route only when needed. If every route fails for every search, the run fails with UPSTREAM_FAILED instead of reporting success.
Best results
- Prefer specific niche keywords over broad terms
- Use
sortBy: recencyfor monitoring pipelines - Cap
maxResultsfor cost control - Treat
VALID_EMPTYas a real no-match
Builder's note
I built this Actor's main route as my own reader for Upwork's public job search, which signed-out visitors can see without an account. That page hides client details, proposal counts and connects, so those fields stay null rather than guessed; a signed-in fallback covers the searches whose filters need them.
Legal disclaimer
This Actor retrieves public Upwork job listings for legitimate market research, lead generation, and personal job monitoring. You are responsible for complying with Upwork's Terms of Service, applicable laws, and your own data-use policies. Use this tool for lawful research and monitoring that respects Upwork's Terms of Service, applicable laws, and your own data-use policies. The authors are not affiliated with Upwork.
Support
Open an issue on the Actor's Issues tab. Sibling routing is covered in Best fit above.