Upwork Jobs Scraper — Listings, Budgets, Skills avatar

Upwork Jobs Scraper — Listings, Budgets, Skills

Pricing

$1.00 / 1,000 per job returneds

Go to Apify Store
Upwork Jobs Scraper — Listings, Budgets, Skills

Upwork Jobs Scraper — Listings, Budgets, Skills

Scrape Upwork job postings by keyword, category, budget, experience level and client location. Every row carries the FULL job description, hourly or fixed budget, required skills, duration and workload. Clears Upwork's Cloudflare challenge for you. No Upwork account needed.

Pricing

$1.00 / 1,000 per job returneds

Rating

0.0

(0)

Developer

Scrapers Delight

Scrapers Delight

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

💼 Upwork Jobs Scraper — every public job posting, with the full brief

Turn Upwork's job marketplace into a clean, filterable dataset. Give it a keyword — or nothing at all — and it walks Upwork's search results and returns one row per job posting, carrying the complete job description (not the two-line preview), the hourly rate range or fixed budget, the required skills, the experience level Upwork asks for, the project length and the weekly workload.

Upwork sits behind a Cloudflare managed challenge: a plain HTTP request gets a 403 and a 344 KB "Checking your browser" page, and so does a stock headless browser. This actor clears that challenge with a real browser once per run, then does the actual crawling over cheap plain HTTP. No Upwork account, no cookies of your own, no CAPTCHA service.

Read "Known transport limitation" below before you run this on Apify. The clearance step is currently blocked from inside Apify's Linux container for Upwork's job-search rule, and the actor says so loudly rather than returning an empty dataset. The scraper is proven end to end where a desktop browser is available.


🎯 What you get

  • 📝 The full job description — median 1,002 characters, up to 10,777 measured. Upwork's search page clamps the text to two lines; this returns the whole brief, so you can run your own keyword, budget or red-flag filters over it.
  • 💵 Real budget numbershourlyRateMin / hourlyRateMax for hourly contracts, fixedBudget for fixed-price projects, as separate numeric fields you can sort and chart.
  • 🧠 The skill list Upwork attached to the posting — a mean of 6.2 tags per job, plus matchedSkills, the subset your keyword actually hit.
  • 🧭 Experience level, project length and hours per week — resolved into human labels ("Expert", "3 to 6 months", "30+ hrs/week"), not Upwork's internal i18n keys.
  • ⏱️ Posting timestampspostedAt, createdAt and renewedAt in ISO 8601, so a scheduled run turns straight into a "new since last check" feed.
  • 🔗 A canonical job URL for every row.

🔍 What you can filter on

Everything below is applied server-side by Upwork, so you only pay for rows you actually want:

FilterValues
Keywordsany number; Upwork's search syntax (quotes, OR, NOT) works
Job typehourly · fixed price
Experience levelEntry Level · Intermediate · Expert
Categoryall 12 Upwork top-level categories
Client locationcountry, sub-region or continent
Client hire historyno hires · 1–9 hires · 10+ hires
Hours per weekunder 30 · 30+
Project length<1 month · 1–3 · 3–6 · 6+ months
Hourly ratemin / max USD
Fixed budgetmin / max USD
Sortnewest first · most relevant

You can also paste any Upwork search URL into startUrls and the actor will crawl exactly that search, filters and all.


📊 Measured field coverage

Not estimates — the numbers below come from parsing 294 unique live job postings across six different searches (python, shopify developer, video editor, data entry, and the unfiltered newest-jobs feed) on 2026-09-02:

FieldFilledNote
jobId, jobKey, title, url100%
description, descriptionLength100%mean 1,612 chars · median 1,002 · max 10,777
jobType100%180 hourly / 114 fixed price in the sample
experienceLevel100%Intermediate 181 · Expert 85 · Entry Level 28
duration100%
postedAt, createdAt100%ISO 8601
skills98.6%mean 6.2 skills per job
currency81.3%present whenever a rate or budget is
matchedSkills71.1%your keyword's own skill hits
workload61.2%hourly jobs carry it; fixed-price ones mostly do not
hourlyRateMin / hourlyRateMax42.5%= 125 of 180 hourly jobs advertised a rate
fixedBudget38.8%= 114 of 114 fixed-price jobs — every one
renewedAt3.4%only re-posted jobs have one

⚠️ Honest limits — read before you buy

No client spend, hire rate or payment-verification data. Upwork shows those only to signed-in users. On the logged-out search payload they are present as nulls on every single row — 0 of 300 measured. Rather than ship a column that is always empty, this actor does not emit them at all. The one client signal you can filter on is hire history (clientHires), which Upwork does apply server-side. If you need spend and hire-rate columns, no logged-out scraper can honestly give them.

Three Upwork filters do nothing for logged-out visitors and are therefore not offered here: "payment verified", "number of proposals" and "contract-to-hire". Each returned the unfiltered 2,444-row baseline when tested. They are omitted rather than shipped as knobs that silently no-op.

Residential proxies are required, not recommended. Cloudflare's challenge was cleared from residential IPs and never from datacenter or static IPs. Measured 2026-09-02, same browser, same minute: Apify RESIDENTIAL (US) cleared in 6.2 s / 8.8 s / 9.2 s; Apify auto (datacenter) and a static-IP group both sat on "Just a moment…" until they were abandoned at 60 s. The input therefore defaults to RESIDENTIAL + country US.

⚠️ Known transport limitation — read this before running. The clearance step needs a browser whose fingerprint Cloudflare accepts, and Apify's Linux container currently does not have one for Upwork's job-search rule specifically. Measured on 2026-09-02 across 12 configurations — real Google Chrome and bundled Chromium, headed under Xvfb, with and without the sandbox, with software WebGL on and off, at 2/4/8 GB, on US / unpinned / GB residential IPs and on the platform's own egress, with 90 s of patience plus mouse movement — the container never obtained cf_clearance for /nx/search/jobs/. It does clear upwork.com's homepage (HTTP 200 + cf_clearance in 20 s), and the identical code on a Windows desktop Chrome clears the job search through the same Apify residential pool in 6–9 s, so the wall is the container's browser fingerprint, not the IP and not the parser. Two ways round it today:

  • Bring your own clearance (clearance input): mint cf_clearance in your own browser and pass it with the exact User-Agent that solved it, plus a proxy that egresses from that same IP. A cf_clearance is bound to the (IP, User-Agent) pair, so all three must line up — and note that Apify's residential sessions do not hold an IP long enough to bridge this reliably (a pinned session's exit IP changed within minutes in testing); this route needs a sticky/ISP proxy.
  • Run it locally or on your own box, where a normal desktop Chrome is available. The scraper itself is fully proven there: 60 rows in 15 s over Apify residential, one 8.8 s clearance, 2 HTTP requests.

A block is never reported as "no jobs". If Cloudflare blocks everything, the run fails with the challenged-page count and sample URLs in the message. A genuinely empty search exits successfully with zero rows. Those two outcomes are never conflated.


🚀 Example inputs

Fresh leads for a Python freelancer, cheapest possible run

{
"searchQueries": ["python", "django", "fastapi"],
"jobType": "hourly",
"hourlyRateMin": 40,
"experienceLevel": ["expert"],
"sortBy": "recency",
"maxItems": 200
}

Serious buyers only — clients who have hired before, decent budgets

{
"searchQueries": ["shopify"],
"clientHires": "10-",
"fixedBudgetMin": 1000,
"maxItems": 500
}

Market research: the whole marketplace, newest first

{
"searchQueries": [],
"sortBy": "recency",
"maxItems": 5000
}

Reuse a search you already built on Upwork

{
"startUrls": [
{ "url": "https://www.upwork.com/nx/search/jobs/?q=react&sort=recency&contractor_tier=3&t=0" }
]
}

💰 Pricing

$1.00 per 1,000 jobs ($0.001 per job returned) — pay per event, nothing else. No monthly fee, no charge for starting a run, no charge for pages that came back blocked. Jobs that appear under more than one of your keywords are deduplicated before billing, so you never pay for the same posting twice. Rows are delivered and billed together, so a spend cap stops the run cleanly instead of handing you rows you paid for twice or rows you never paid for.


❓ FAQ

Do I need an Upwork account? No. This reads only what Upwork serves to logged-out visitors.

How many jobs can I get from one keyword? As many as Upwork matches. Deep pagination is verified: on a 2,444-result search, page 49 at 50 rows/page returned offset 2,400 — the tail of the result set, not a truncated first 1,000. The unfiltered marketplace measured 115,014 open jobs.

How fast is it? Upwork serves 50 jobs per request. One browser clearance (~4–5 s measured) is minted at the start of the run; everything after that is plain HTTP.

Can I run it on a schedule to get only new jobs? Yes — sort by recency, keep maxItems small, and diff on jobId or postedAt between runs.

Why does it use a browser at all if it then uses plain HTTP? Because Cloudflare's managed challenge can only be solved by a real browser, but the resulting clearance cookie can be replayed by a cheap HTTP client. Measured: one clearance served 8 of 8 consecutive search pages with zero re-challenges.

Does it work without a residential proxy? No — see "Honest limits" above.

Why did my Apify run fail with "Cloudflare blocked every request"? That is the known transport limitation above: the clearance step cannot currently be completed from inside Apify's container for Upwork's job-search rule. The run fails loudly on purpose rather than handing you an empty dataset that looks like "Upwork has no jobs".

Is the description the full text? Yes — the whole posting, HTML highlight markup stripped.

What happens if Upwork changes their page? The actor never reports a parse failure as an empty result: unreadable pages are counted and named in the run's status message.

Can it scrape freelancer profiles too? No. One site, one job: this actor does job postings.

What about proposals count / connects required? Not exposed to logged-out visitors.


This actor collects publicly visible job postings — the same pages any visitor sees without signing in. It does not log in, does not create accounts, does not solve CAPTCHAs on a human's behalf, and collects no personal data about freelancers. Upwork's robots.txt disallows /nx/ for crawlers (Disallow: /nx/), which is a crawler-etiquette directive, not a technical or legal access control; you are responsible for your own use of the data and for complying with Upwork's Terms of Service and any applicable law in your jurisdiction. Use the output for market research, rate benchmarking and finding work — not for spamming clients.