Upwork Job Scraper | Client Intel & Proposals avatar

Upwork Job Scraper | Client Intel & Proposals

Pricing

from $1.50 / 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 $1.50 / 1,000 results

Rating

0.0

(0)

Developer

Corvuslab

Corvuslab

Maintained by Community

Actor stats

0

Bookmarked

4

Total users

0

Monthly active users

12 days ago

Last modified

Share

What does Upwork Scraper do?

Scrape Upwork job listings and get the complete client-quality panel on every job — country, city, timezone, lifetime spend, payment verification, rating, review count, total hires, hours billed, industry, company size and the average hourly rate the client actually pays — plus exact proposal counts, full descriptions, freelancer requirements and incremental change tracking.

No Upwork account, cookie or login needed. Every public job is fully enriched at no extra per-job fee.

📚 What data can you extract from Upwork?

Every job comes back as a structured record with 80+ fields, organised into five groups:

GroupWhat you get
Job basicsTitle, URL, type (hourly/fixed), experience level, budget range, engagement duration, project shape (ongoing/one-time/complex), skills, category, tools, deliverables, deadline
Client intelligenceCountry, city, timezone, lifetime spend, rating, review count, payment-verified flag, total hires, active hires, hours billed, jobs with hires, avg hourly rate paid, member-since date, industry, company size
Competition signalsExact proposal count, hires, interviews, invitations sent, unanswered invites, positions to hire, last client activity
Freelancer requirementsMin JSS, min hours billed, min hours/week, English level, allowed countries/regions/timezones, Rising Talent only, portfolio required, freelancer type
DescriptionFull text in plain text, HTML and/or Markdown — configurable per run

✨ 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 shows their average hourly rate across past contracts. Spot low-paying clients before spending Connects.
  • 🧩 Project shape — every job tagged Ongoing, One-time or Complex project.
  • 📉 Exact proposal counts — the real number, not a "5 to 10" bucket, plus hires, interviews, invitations sent and unanswered.
  • 🎯 Low-competition filtering — combine proposal brackets with client-quality gates to surface jobs worth writing a proposal for.
  • 📌 Direct job URLs — paste specific Upwork job page URLs to scrape individual listings without running a search.
  • 🔍 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 — a lean data path rather than page-by-page rendering, with detail pages fetched in parallel, so large runs stay quick and inexpensive.

🚀 How to scrape Upwork

  1. Click "Start" on this actor page, or open it from the Apify Console.
  2. Enter a search query — type keywords like "python developer" or "shopify", or leave blank to browse all jobs.
  3. Set your filters — narrow by job type, budget, experience level, category, client quality (verified payment, minimum spend/rating/reviews) and competition level (proposal brackets).
  4. Click "Start" — results appear in the Output tab within seconds.
  5. Export your data — download as JSON, CSV or Excel, or connect directly to Google Sheets, Zapier, Make, n8n or your own API.

That's it. No proxy setup, no login, no cookies — the Actor handles all of that for you.

Quick-start 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"]
}

This finds up to 100 React developer jobs, sorted newest first, hourly only, paying $40+/hr, from payment-verified clients who have spent at least $5,000 on Upwork, with fewer than 10 proposals — the sweet spot for low-competition, high-quality leads.

⚙️ Input

FieldTypeDescription
querystringKeywords, e.g. "python". Leave blank to browse everything.
queriesarraySeveral keyword searches in one run.
searchUrlsarrayUpwork search URLs pasted from your browser; filters are parsed automatically.
startUrlsarrayIndividual Upwork job page URLs to scrape specific listings directly.
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.

🎯 Job filters

FieldTypeDescription
jobTypeenumhourly or fixed.
experienceLevelarrayentry, intermediate, expert — select one or more.
categoryenumTop-level Upwork category, or a raw category UID.
budgetenumFixed-price bracket: 0-99, 100-499, 500-999, 1000-4999, 5000+.
hourlyRatestringHourly range in USD, e.g. "30-80", or "50-" for 50 an hour 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, e.g. "United States", "Europe".
skillsstringUpwork skill tags, comma-separated.

🏅 Client quality

These read the client-intelligence panel, so they switch detail enrichment on automatically.

FieldTypeDescription
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.

🧮 Keyword and date filters

FieldTypeDescription
allWords / anyWords / exactPhrase / noneWords / titleSearchstringUpwork's native AND / OR / exact / NOT / title-only search.
includeKeywords / excludeKeywordsarrayKeep or drop jobs matching these terms (post-filter).
keywordMatchTitle / keywordMatchDescription / keywordMatchSkillsbooleanRestrict keyword matching to specific fields. None on = all.
fromDate / toDatestringDate window (YYYY-MM-DD; toDate inclusive).
maxAgeMinutesintegerOnly jobs posted within N minutes before the run.
customFiltersarrayRule-based filters on any output field. Operators: includes, notIncludes, equals, notEquals, gt, gte, lt, lte, exists, notExists.

📄 Output

FieldTypeDescription
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 — ideal for AI agents and LLM pipelines.
excludeEmptyFieldsbooleanDrop null/empty fields from each record.

🔁 Incremental monitoring

FieldTypeDescription
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.

🔔 Notifications

Credentials are stored encrypted and never written to the run log. A built-in redaction layer scrubs secrets from error messages before they can surface.

FieldTypeDescription
telegramToken + telegramChatIdstringTelegram bot token + chat ID.
slackWebhookUrlstringSlack Incoming Webhook URL.
discordWebhookUrlstringDiscord channel webhook URL.
webhookUrl + webhookHeadersstring / objectPOST structured JSON anywhere with optional headers. Ideal for n8n, Make, Zapier.
notificationLimitintegerMax jobs listed per message (default 5).
notifyOnlyChangesbooleanOnly alert on changed jobs (needs incremental mode).

⚙️ Advanced

FieldTypeDescription
sessionTokenstringOptional. Unlocks the ~10% of jobs whose details are account-gated.
proxyConfigurationobjectOptional Apify proxy settings — the default is enough for this actor.

📤 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,
"budgetCurrency": "USD",
"engagementType": "FULL_TIME",
"engagementDuration": "3 to 6 months",
"projectType": "Ongoing project",
"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 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 — perfect for feeding into LLM agents or MCP pipelines.

♻️ How to monitor Upwork with incremental mode

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 request and the cost.

First run (baseline)Second run (nothing changed)
Jobs foundallall
Detail pages fetchedallnone
Records emitted & chargedallnone

Your cost after the baseline scales with how much the market changes, not with how many listings you monitor:

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

Setting up scheduled monitoring

  1. Configure the scraper with your search and filters.
  2. Turn on Incremental mode.
  3. Run it once — this creates the baseline.
  4. Go to the Schedules tab and set a recurring run (e.g. every 30 minutes).
  5. Optionally add a notification channel (Telegram, Slack, Discord or webhook) with Notify only about changes on.

From now on each run bills only the run-start fee plus the jobs that actually changed. Combine with maxAgeMinutes for near-real-time alerts.

🔔 How to set up Upwork alerts

Get alerted the moment matching jobs appear. Fill in only the channels you want:

ChannelWhat to configure
✈️ TelegramtelegramToken (from @BotFather) + telegramChatId
💬 SlackslackWebhookUrl (Incoming Webhook URL)
🎮 DiscorddiscordWebhookUrl (channel webhook URL)
🪝 WebhookwebhookUrl receives structured JSON — ideal for n8n / Make / Zapier

Every channel fires independently. A broken channel cannot stop the scrape or the other channels.

Pair Incremental mode + Notify only about changes to hear only about genuinely new work, without duplicates across runs.

💡 What can you do with Upwork data?

Find work before it gets crowded

Sort by newest, filter to 0–4 proposals and payment-verified clients with real spend history. Apply for jobs while the competition is still thin.

Qualify clients before you spend Connects

Lifetime spend, rating, review count, hire rate, average hourly rate paid and last-activity time — all on every job. Know whether a client is worth a proposal before you invest.

Market and rate research

Track hourly ranges and budgets by skill, category or region over time. Benchmark your rates against what clients are actually posting.

Skill-demand analysis

Measure which technologies, frameworks and tools are being hired for right now. Export skills data to build trend dashboards.

Agency lead generation

Build outreach lists from client country, industry, company size and spend. Focus on clients who hire often and pay well.

Feed AI agents and LLM pipelines

Compact JSON straight into an LLM context, MCP tool or vector store. The excludeEmptyFields and descriptionMaxLength options keep token costs low.

Monitor competitors

Track when specific types of jobs appear, what clients are paying, and how quickly positions fill. Schedule recurring runs with notifications for instant awareness.

💰 How much does it cost to scrape Upwork?

This Actor uses Apify's pay-per-event model: a small fee when a run starts, plus a per-record fee. See the Actor's Pricing tab for the current numbers — they're rendered live, so this page never goes stale.

Two things cut your bill further:

  • Site-side filters — records you filtered out are never fetched and never billed.
  • Incremental mode — after the baseline run you pay only for what actually changed.
Daily churnof 1,000 trackedbillable recordsyou save
5 %1,0005095 %
15 %1,00015085 %
30 %1,00030070 %

One per-record fee buys the whole record — the full client panel, the exact proposal count, the freelancer requirements and the complete description in up to three formats. There is no enrichment add-on, no per-field surcharge and no login to set up. You only pay for jobs you actually receive: the server-side filters (allWords, noneWords, exactPhrase, titleSearch) discard non-matching jobs before they are billed.

🔌 Integrations & export

Export your data anywhere:

  • JSON, CSV, Excel — download directly from the Output tab
  • Apify API — stream results programmatically via the REST API or the JavaScript / Python SDK
  • Google Sheets — connect via the Apify integration
  • Zapier / Make / n8n — use the generic webhook or connect through Apify's built-in integrations
  • Slack / Discord / Telegram — built-in notifications, no middleware needed
  • AI agents — use this actor as an MCP tool for Claude, ChatGPT or any MCP-compatible agent

🔗 Using the API

Run this Actor from your own code. Example with the Apify Python client:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("corvuslab/upwork-job-scraper").call(run_input={
"query": "example",
"maxResults": 50,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)

It also works with the JavaScript/TypeScript client, the Apify CLI and the REST API.

❓ 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 ~10% 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.

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 work across one proxy IP per concurrent worker and lets faster IPs take more of the load. Transient failures are retried with backoff. Raise detailConcurrency for more parallelism on 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 enriched client panel instead. Detail enrichment switches on automatically when you use these filters.

Can I scrape specific job listings by URL? Yes. Paste individual job page URLs into the startUrls field (e.g. https://www.upwork.com/jobs/~022082001692817092210). These are fetched directly through the detail API without needing a search.

Is my data kept private? All credentials (notification tokens, webhook URLs, session tokens) are marked as secret inputs — they are encrypted at rest, masked in the Apify UI, and never appear in run logs. A built-in redaction layer scrubs secrets from error messages before they can surface anywhere.

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.


Keywords: upwork scraper · upwork.com scraper · upwork jobs api · scrape upwork jobs · freelance jobs scraper · upwork job feed · upwork data extraction · upwork client data · proposal count scraper · freelance market research · upwork lead generation · job monitoring · export upwork to CSV · export upwork to Excel · upwork MCP tool · upwork AI agent · upwork job alerts · upwork notification bot