Upwork Job Scraper | Client Intel & Proposals avatar

Upwork Job Scraper | Client Intel & Proposals

Pricing

from $2.00 / 1,000 results

Go to Apify Store
Upwork Job Scraper | Client Intel & Proposals

Upwork Job Scraper | Client Intel & Proposals

Scrape Upwork jobs by keyword, budget, experience or client quality. Every job arrives fully enriched — client country, lifetime spend, rating, hires, average rate paid and exact proposal counts — with incremental monitoring and alerts. No add-ons, no Upwork account.

Pricing

from $2.00 / 1,000 results

Rating

0.0

(0)

Developer

Corvuslab

Corvuslab

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Share

Upwork Job Scraper 💼

Extract structured job data from Upwork — with the full client-quality panel (country, lifetime spend, payment verification, rating, review count), exact proposal counts, full descriptions, incremental change tracking and built-in notifications.

No Upwork account, cookie or session token required. The client panel and the exact number of proposals come back on every public job, at no extra per-job fee.

Great for freelancer lead generation, market and rate research, competitive analysis, and feeding live job data into dashboards or AI agents.

✨ Key features

  • 🏅 Client intelligence on every job — country, city, timezone, lifetime spend, payment-verified flag, rating, review count, total hires, hours billed, industry, company size and member-since date.
  • 💵 What the client actually paysclientAvgHourlyRatePaid is their average hourly rate across past contracts, so you can spot a low-paying client before spending Connects. Present for clients with hourly history.
  • 🧩 Project shape — every job is tagged Ongoing, One-time or Complex project.
  • 📉 Exact proposal counts — the real number of applicants, not a "5 to 10" bucket, plus hires, interviews and invitations sent.
  • 🎯 Low-competition filtering — combine proposal brackets with client-quality gates to surface jobs worth writing a proposal for.
  • 🔍 Batch searches — many keyword queries in one run, deduplicated by job ID so overlapping searches never bill you twice.
  • 🔗 Paste-a-URL mode — drop in any Upwork search URL and every filter in it is read and replayed automatically.
  • 🧮 Native Upwork operators — all-words, any-words, exact-phrase, none-of-these and title-only search run server-side, so excluded jobs never reach your bill.
  • 🔁 Incremental mode — recurring runs emit only what changed. Unchanged jobs are recognised before their detail page is fetched, so you skip the work and the cost.
  • ♻️ Repost detection — flag or drop jobs re-listed under a new ID.
  • 🔔 Notifications — push matching jobs to Telegram, Slack, Discord or any webhook.
  • 🪶 AI-friendly outputcompact mode, description truncation and excludeEmptyFields for lean records in LLM and MCP pipelines.
  • Fast and cheap — talks to Upwork's JSON API directly, so there is no browser to boot and no HTML to parse. Each job transfers a few KB instead of a rendered page, and detail pages are fetched in parallel across multiple proxy IPs.

📥 Input

FieldTypeDescription
querystringKeywords, e.g. "python". Leave blank to browse everything.
queriesarraySeveral keyword searches in one run.
searchUrlsarrayUpwork search URLs pasted from your browser; their filters are parsed.
maxResultsintegerMax jobs per search. Upwork caps any search at 5,000; 0 takes the max. Default 50.
sortenumrecency (default), relevance, client_total_charge, client_rating.
jobTypeenumhourly or fixed.
experienceLevelarrayentry, intermediate, expert.
categoryenumTop-level Upwork category, or a raw category UID.
budgetenumFixed-price bracket: 0-99, 100-499, 500-999, 1000-4999, 5000+.
hourlyRatestringHourly range, e.g. "30-80" or "50-" for $50/hr and up.
workloadenumas_needed, part_time, full_time.
durationenumweek, month, semester, ongoing.
contractToHirebooleanOnly jobs that may convert to a full-time role.
locationstringClient country or region in full, e.g. "United States", "Europe".
skillsstringUpwork skill tags, comma-separated.
verifiedPaymentOnlybooleanOnly clients with a verified payment method.
minClientTotalSpentintegerMinimum client lifetime spend (USD).
minClientRatingintegerMinimum client rating, 0–5.
minClientReviewCountintegerMinimum number of client reviews.
clientHiresenum0, 1-9, 10+ previous hires.
proposalsarrayCompetition brackets: 0-4, 5-9, 10-14, 15-19, 20-49, 50+.
excludeLocationsarrayDrop jobs from these client countries.
allWords / anyWords / exactPhrase / noneWords / titleSearchstringUpwork's native AND / OR / exact / NOT / title-only search.
includeKeywords / excludeKeywordsarrayKeep or drop jobs matching these terms.
keywordMatchTitle / keywordMatchDescription / keywordMatchSkillsbooleanLimit keyword matching to certain fields. None on = all fields.
fromDate / toDatestringKeep jobs posted in this window (YYYY-MM-DD; toDate inclusive).
maxAgeMinutesintegerKeep only jobs posted within N minutes before the run.
customFiltersarrayRule-based filters on any output field, e.g. [{"field":"clientTotalSpent","operator":"gte","value":10000}]. Operators: includes, notIncludes, equals, notEquals, gt, gte, lt, lte, exists, notExists.
enrichDetailsbooleanFetch each job's detail page for the client panel and proposal count. Default true.
detailConcurrencyintegerParallel detail requests, 1–30. Each uses its own proxy IP. Default 20.
descriptionFormatenumall, text, html, markdown.
descriptionMaxLengthintegerTruncate descriptions (0 = full text).
compactbooleanCore fields only.
excludeEmptyFieldsbooleanDrop null/empty fields.
incrementalModebooleanEmit only NEW / UPDATED jobs on recurring runs.
stateKeystringStable name for the tracking state (auto-derived if blank).
emitUnchanged / emitExpiredbooleanAlso emit UNCHANGED / EXPIRED jobs.
skipRepostsbooleanDetect and drop reposts.
telegramToken + telegramChatIdstringNotify Telegram (BotFather token + chat ID).
slackWebhookUrl / discordWebhookUrlstringNotify a Slack / Discord incoming webhook.
webhookUrl + webhookHeadersstring / objectPOST structured JSON anywhere, with optional headers.
notificationLimitintegerMax jobs listed per message (default 5).
notifyOnlyChangesbooleanOnly alert on changed jobs (needs incremental mode).
includeRunSummarybooleanPrepend a one-line summary to notifications. Default true.
sessionTokenstringOptional. Unlocks the ~10% of jobs whose details are account-gated.
proxyConfigurationobjectApify proxy settings. Datacenter is plenty.

Example input

{
"query": "react developer",
"maxResults": 100,
"sort": "recency",
"jobType": "hourly",
"experienceLevel": ["intermediate", "expert"],
"hourlyRate": "40-",
"verifiedPaymentOnly": true,
"minClientTotalSpent": 5000,
"proposals": ["0-4", "5-9"]
}

📤 Output

Each job is pushed to the run's default dataset. Example record (abridged):

{
"jobId": "2082001692817092210",
"title": "React/Supabase Developer Needed for SAAS Web Application",
"url": "https://www.upwork.com/jobs/~022082001692817092210",
"jobType": "HOURLY",
"experienceLevel": "ExpertLevel",
"hourlyBudgetMin": 40,
"hourlyBudgetMax": 100,
"budgetAmount": null,
"budgetCurrency": "USD",
"engagementType": "FULL_TIME",
"engagementDuration": "3 to 6 months",
"engagementDurationWeeks": 18,
"projectType": "Ongoing project",
"projectTypeCode": "EMPLOYMENT_POSITION",
"skills": ["React", "JavaScript", "Supabase", "API"],
"category": "Web Development",
"categoryGroup": "Web, Mobile & Software Dev",
"publishTime": "2026-07-28T07:16:01.685Z",
"clientCountry": "Poland",
"clientCity": "Radwanice",
"clientTimezone": "Europe/Berlin (UTC+02:00)",
"clientTotalSpent": 1265.14,
"clientRating": 5,
"clientReviewCount": 5,
"clientTotalHires": 8,
"clientActiveHires": 1,
"clientTotalHours": 214.5,
"clientJobsWithHires": 7,
"clientAvgHourlyRatePaid": 24.98,
"clientPaymentVerified": true,
"clientIndustry": "Automotive",
"clientCompanySize": 1,
"clientMemberSince": "2023-05-19T00:00:00.000Z",
"totalApplicants": 22,
"totalHired": 0,
"totalInterviewing": 0,
"personsToHire": 1,
"lastClientActivity": "2026-07-28T08:14:48.977Z",
"minJobSuccessScore": 0,
"preferredEnglishLevel": "ANY",
"preferredFreelancerType": "ANY",
"description": "…",
"descriptionMarkdown": "…",
"detailsFetched": true,
"searchQuery": "react developer",
"scrapedAt": "2026-07-28T08:27:26.541Z"
}

The Output tab also ships three ready-made views: Overview (title, budget, proposals, client quality), Client intelligence (the full client panel) and Changes (the incremental tracking fields).

In compact mode each record is trimmed to the 17 fields needed to identify, price and triage a job: jobId, title, url, jobType, experienceLevel, budgetAmount, hourlyBudgetMin/Max, skills, publishTime, totalApplicants, clientCountry, clientTotalSpent, clientPaymentVerified, clientRating, searchQuery and changeType.

Incremental fields

With incrementalMode on, every record also carries:

  • changeTypeNEW, UPDATED, UNCHANGED or EXPIRED
  • firstSeenAt / lastSeenAt — when the job was first and most recently seen
  • contentHash — hash of the tracked fields
  • isRepost / repostOfId / repostDetectedAt — set when repost detection matches an earlier listing

🔁 Incremental mode & monitoring savings

Running on a schedule? Turn on incrementalMode. The first run builds a baseline; every run after that emits — and charges for — only the jobs that actually changed.

Unchanged jobs are recognised from the search response alone, before their detail page is fetched, so you skip both the work and the cost.

Two consecutive runs of the same search, where nothing has changed in between:

First run (baseline)Second run (nothing changed)
Jobs foundall of themall of them
Detail pages fetchedallnone
Records emitted & chargedallnone

The second run still checks every job — it just recognises each one as unchanged and charges for none of them.

Because this actor is pay-per-result, your cost after the baseline scales with how much the market actually changes ("churn"), not with how many listings you monitor:

Daily churnRecords emitted per run (after baseline)Approx. saving vs a full re-scrape
5% (stable niche)~5%~90%
15% (moderate)~15%~80%
30% (fast-moving)~30%~65%

Set emitExpired to also receive listings that have dropped off, and emitUnchanged if you want the full set every run. Keep maxResults consistent between scheduled runs — it defines the set of jobs being tracked.

🔔 Notifications

Get alerted the moment matching jobs appear. Fill in only the channels you want; each fires independently when a run finishes.

  • ✈️ TelegramtelegramToken (from @BotFather) + telegramChatId.
  • 💬 SlackslackWebhookUrl (Incoming Webhook).
  • 🎮 DiscorddiscordWebhookUrl (channel webhook).
  • 🪝 Generic webhookwebhookUrl receives structured JSON (run metadata plus a jobs array) with optional webhookHeaders. Ideal for n8n / Make / Zapier.

Pair maxAgeMinutes with a schedule for near-real-time alerts, or incrementalMode + notifyOnlyChanges to hear only about genuinely new work.

💡 Use cases

  • Find work before it gets crowded — sort by newest, filter to 0–4 proposals and payment-verified clients with real spend history.
  • Qualify clients before you spend Connects — lifetime spend, rating, review count, hire rate and last-activity time on every job.
  • Rate and market research — track hourly ranges and budgets by skill or category over time.
  • Skill-demand analysis — measure which technologies are actually being hired for.
  • Agency lead generation — build outreach lists from client country, industry and spend.
  • Feed AI agents — compact JSON straight into an LLM, MCP tool or vector store.

💰 Pricing

Pay-per-event — two events, no add-ons.

EventPrice
Actor start$0.001 per run
Job record$0.002 per job
RunCost
10 jobs$0.021
100 jobs$0.201
1,000 jobs$2.001

$0.002 buys the whole record, not a starting point. Every job arrives with the full client panel — country, city, timezone, lifetime spend, rating, review count, total hires, hours billed, industry, company size, and the average hourly rate that client actually pays — plus the exact proposal count and the freelancer requirements.

There is no enrichment add-on, no per-field surcharge and no session cookie to set up. What you see in the output example above is what a single $0.002 record contains, on a run you can start in one click.

You only pay for jobs you actually receive. Server-side filters (allWords, noneWords, exactPhrase, titleSearch) discard non-matching jobs before they are billed, and with incrementalMode on, a run that finds nothing new costs only the $0.001 start fee.

❓ FAQ

Do I need an Upwork account or login? No. The client panel, proposal counts and descriptions all come from Upwork's public data. The optional sessionToken only adds the minority of jobs whose detail pages are account-gated.

Why is detailsFetched false on some jobs? Around 10% of listings hide their detail page from logged-out visitors. Those records still carry every listing-level field (title, budget, skills, dates); only the client panel is missing. Supply a sessionToken if you need 100% coverage.

Why do some jobs have no clientTotalSpent? Clients can enable financial privacy, which hides lifetime spend from everyone. Similarly, clientIndustry and clientCompanySize are only present when the client has filled in a company profile.

What client data is not included? The aggregate hiring history is complete — total hires, active hires, hours billed, jobs with hires, rating, review count and member-since. The one thing Upwork does not expose publicly is the client's itemised contract history (each past job with its individual feedback text); that sits behind an authenticated scope, so no logged-out scraper can reach it.

Can I get more than 5,000 results from one search? No — Upwork itself refuses paging past 5,000. Split the search (by category, budget bracket or date window) to cover more ground.

Does it handle large runs reliably? Yes. Upwork limits requests per IP, so the actor spreads a run across one proxy IP per concurrent worker and lets faster IPs take more of the load. Transient failures are retried with backoff rather than dropped, so a large run finishes with its client panel intact instead of silently half-empty. Raise detailConcurrency for more parallelism on very large runs.

Why are verifiedPaymentOnly and proposals applied after fetching? Upwork only honours those filters for signed-in users, so this actor evaluates them against the client panel instead. It switches detail enrichment on automatically when you use them.

Is it legal to scrape Upwork? This actor extracts publicly available data. Web scraping of public information is generally considered legal, but you should review the target site's terms of service and ensure your use case complies with applicable laws and regulations, including GDPR where relevant.


This is an unofficial actor and is not affiliated with, endorsed by, or connected to Upwork. "Upwork" is a trademark of its respective owner and is used here only to describe the site this actor works with.