Upwork Jobs Feed: Job Scraper and Alerts avatar

Upwork Jobs Feed: Job Scraper and Alerts

Pricing

from $0.95 / 1,000 job delivereds

Go to Apify Store
Upwork Jobs Feed: Job Scraper and Alerts

Upwork Jobs Feed: Job Scraper and Alerts

Upwork job scraper for new job posts with the client details that matter: payment verified, country, total spent, rating and exact applicant count. Filter, schedule, get alerts.

Pricing

from $0.95 / 1,000 job delivereds

Rating

0.0

(0)

Developer

Cloudastra Technologies

Cloudastra Technologies

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Upwork Jobs Feed

An Upwork job scraper built for job alerts. New Upwork job posts, minutes after they go live, with the client details you use to decide whether a job is worth a proposal: payment verified, country, total spent, rating, number of reviews and the exact number of applicants. Filter on any of them, run it on a schedule, and get only the jobs you have not seen before, in your dataset or straight to Telegram, Slack, Discord, WhatsApp or a webhook.

What you get

  • One row per new job, newest first: title, full description, link, job type, budget or hourly range, experience level, skills, project length, workload, and when it was published.
  • The client panel for every job: country, total spent on Upwork, payment verification, rating, number of reviews, and whether the client hides their spend.
  • The applicant count as a number, not a bucket like "10 to 15".
  • Filters on keywords, client location, client spend, rating and reviews, payment verification, job type, experience level, budget, hourly rate, workload, project length, number of proposals and publish date, plus your own rules on any output field.
  • Up to 10 searches in one run, each job delivered once.
  • Incremental mode: schedule it and receive only jobs that are new or changed since your last run.
  • Notifications to your own Telegram, Slack, Discord, WhatsApp or webhook.

Who it is for

  • Freelancers and agencies who want to see good jobs early, while the applicant count is still low, without refreshing Upwork all day.
  • Lead and sales teams who track which kinds of clients are hiring for which skills.
  • Analysts who watch demand, rates and budgets for a skill or a market over time.
  • Builders of job alert bots and dashboards who need a clean, filterable feed in JSON, CSV or Excel, or through n8n, Make or Zapier.

Limitations, in plain language

Read this before you buy. It is here so you do not need a refund.

It depends on Upwork's public job search, and it can stop working if Upwork changes it. This actor reads the same public job listings anyone can browse on Upwork. It is not an official Upwork product and does not use an official Upwork API. If Upwork changes or restricts its public search, runs can fail until we adapt, and we cannot promise how long that takes. When that happens a run fails with a message saying so, rather than returning an empty list that looks like a quiet hour.

New public job posts only. You get jobs that are open and publicly listed. Invite-only and private jobs are shown by Upwork to invited freelancers only, so no tool outside Upwork sees them. Closed jobs, past contracts, freelancer profiles and messages are not included.

A field Upwork does not report is null. Nothing is guessed or filled in. For example, a fixed price that Upwork lists without a currency has a null currency, not "USD", and a client who hides their spend has a null total spent. Filters treat these cases the way each filter's description says, so read the description before relying on one.

Your use of the data is your responsibility. Upwork's Terms of Use restrict automated access to its site. Check that the way you use these jobs fits those terms and the law where you work.

No client names, no contact details, no job attachments. Upwork does not show a client's name or contact details in its public job listings, and this actor does not log in to Upwork.

Search results are capped by Upwork. Upwork serves about 5,000 results per search, so a very broad search over a long time window will not reach every job ever posted. The actor is built for recent jobs: the last minutes, hours or days.

Upwork's order is only roughly newest first. A reposted job is sometimes listed at its original date, below the newest jobs. Set Minimum pages on an occasional run to read deeper and catch those.

There is an hourly page limit per account (see below). A run that reaches it delivers what it read and finishes successfully, so check the status message if a run returns fewer jobs than you expected.

Now and then a run cannot reach Upwork's search. It then fails with a message telling you to retry in a few minutes. On a schedule, keep maxAgeMinutes longer than the time between runs, so the next run also covers what a failed one missed. With several searches in one run, a run where some searches worked and others could not be completed finishes successfully with the jobs it found, and its status message says how many searches were completed.

A run may start a second run of this actor in your account. Some searches need more memory than a normal run has. The run then starts one more run of this actor in your account, with 4 GB of memory, waits for it and delivers its jobs in the run you started. You will see that second run in your list of runs, and it is billed to you like any other run, within the maximum charge you set for the run you started. If the run you started stops, that second run stops within about a minute.

When the service is busy, a run may be turned away. It then fails at once with "The service is busy, try again in a few minutes.", before reading or delivering anything.

Run it with 256 MB of memory, the default. A run started with more memory fails at once with "Run this actor with 256 MB of memory.", before reading or delivering anything, so you are not billed for memory it cannot use.

Lists in the input have limits. Keyword, location and custom filter lists take up to 100 entries of up to 500 characters each; a search query takes up to 1,000 characters.

Your maximum charge per run is respected. A run stops delivering once the next job would go over the maximum you set for it, finishes successfully and says so in its status message. Jobs it did not deliver are offered again by the next run in incremental mode.

Notifications are best effort. Each channel gets one attempt per run. A channel that fails (a wrong token, a deleted webhook) is reported in the log and never fails the run; the jobs are still in your dataset.

Quick start

Search for new Python jobs from the last hour and keep at most 50:

{
"query": "python",
"maxAgeMinutes": 60,
"maxResults": 50
}

A scheduled alert for well-funded clients outside a few countries, delivered to Telegram, new jobs only:

{
"query": "react OR next.js",
"maxAgeMinutes": 30,
"verifiedPaymentOnly": true,
"minClientTotalSpent": 1000,
"excludeLocations": ["IN", "PK", "BD"],
"jobType": "hourly",
"hourlyRate": "40-",
"incrementalMode": true,
"stateKey": "react-alerts",
"telegramToken": "123456:ABC-your-bot-token",
"telegramChatId": "-1001234567890"
}

Schedule the second one every 15 or 30 minutes in Apify and you have a job alert.

Input

Every field is optional. An empty search returns every new job on Upwork.

FieldDefaultWhat it does
queryemptyUpwork search text. Upwork matches ALL the words; join alternatives with OR ("landing page" OR shopify). Commas mean AND. Empty means every new job.
queriesnoneInstead of query: up to 10 searches in one run, each a text or {"query": "...", "stateKey": "..."}. See "Several searches in one run".
sortrecencyrecency or newest (newest first), relevance, or oldest (the same jobs, delivered oldest first).
maxAgeMinutes0Only jobs published in the last N minutes. 0 means no limit.
maxResults100Most rows to deliver per search, newest first, up to 5,050. 0 means no cap. A job several searches match counts in each but is delivered once.

Filters

Filters run on what Upwork returned. A job that fails a filter is never written to your dataset.

FieldWhat it keeps
excludeKeywordsDrops jobs containing any of the keywords: {"keywords": ["wordpress"], "matchTitle": true, "matchDescription": false, "matchSkills": true}. Case-insensitive. A match flag you leave out counts as true.
includeKeywordsKeeps jobs containing at least one keyword. Same shape.
excludeLocationsDrops clients in these countries or regions: a country name, a 2 or 3 letter code ("US", "USA"), or a region ("europe", "south_america", "english_speaking"). Regions follow the UN M49 list. Jobs with no reported country are kept.
locationKeeps only clients in these countries or regions. Same format. Jobs with no reported country are dropped.
verifiedPaymentOnlyOnly clients whose payment method Upwork reports as verified.
minClientTotalSpentOnly clients who have spent at least this much (USD). A client who hides their spend, or has none reported, is kept.
minClientRatingOnly clients rated at least this (0 to 5). Unrated clients are kept.
minClientReviewCountOnly clients with at least this many reviews.
jobTypehourly or fixed.
experienceLevelEntryLevel, IntermediateLevel or ExpertLevel.
workloadas_needed, part_time or full_time. Hourly jobs only.
durationweek (less than 1 month), month (1 to 3 months), semester (3 to 6 months) or ongoing (more than 6 months).
budgetFixed-price range: 0-99, 100-499, 500-999, 1000-4999 or 5000-. Hourly jobs are dropped.
hourlyRateHourly jobs whose posted range overlaps yours: "20-60", or "50-" for 50 and up. Jobs without a range are dropped.
proposalsProposals already sent: 0-4, 5-9, 10-14, 15-19 or 20-49. Jobs that do not report it are kept.
fromDate, toDatePublished on or after, on or before. A date (2026-10-01) or an ISO-8601 time. A bare toDate covers the whole day.
customFiltersYour own rules on any output field: [{"field": "clientCountry", "op": "equals", "value": "United States"}]. op is includes, notIncludes, equals, notEquals, gt, gte, lt or lte. Text matching ignores case. An unknown field or op stops the run with an error before anything is read.

Incremental mode

FieldDefaultWhat it does
incrementalModeoffDeliver only jobs that are NEW or UPDATED since the last run with the same state key.
stateKeyderivedNames the remembered search. Empty means it is derived from your search and filters, so changing a filter starts a fresh memory.
emitUnchangedoffAlso deliver jobs seen before whose content has not changed.
retentionDays7How long a job is remembered, 1 to 30 days.

Output

FieldDefaultWhat it does
descriptionFormattextdescription is always plain text. html adds descriptionHtml, markdown adds descriptionMarkdown, all adds both.
descriptionMaxLength0Cut descriptions to this many characters. 0 keeps the full text. Filters always see the full text.
compactoffOnly the fields needed to decide on a job: title, link, budget, client, skills, applicants.
excludeEmptyFieldsoffLeave out fields that are null, empty text or an empty list.

Paging

FieldDefaultWhat it does
maxPages20Most pages read per search, 50 jobs each. Pages read count toward the hourly limit.
minPages0Read at least this many pages before the time window may stop paging. Use it on an occasional run to catch reposted jobs listed at their original date.
orderToleranceMinutes15Upwork's order is only roughly newest first, so paging stops only once a whole page is this much older than your time window.

Notifications

FieldWhat it does
telegramToken, telegramChatIdYour bot token from @BotFather and a chat ID or channel ID (channel IDs start with -100). Add the bot to the channel as an admin that can post.
slackWebhookUrlA Slack incoming webhook.
discordWebhookUrlA Discord channel webhook.
whatsappPhoneNumberId, whatsappAccessToken, whatsappToMeta's WhatsApp Cloud API. Meta delivers only within 24 hours of the recipient's last message to your business number.
webhookUrl, webhookHeadersOne JSON POST per run, {"metadata": ..., "items": [...]}, with every delivered job. For n8n, Make, Zapier or your own backend. One attempt, 15 second timeout.
notificationLimitMost jobs per Telegram, Slack, Discord or WhatsApp message, 1 to 20 (default 5). The webhook always gets all of them.
notifyOnlyChangesIn Incremental mode, leave jobs marked UNCHANGED out of notifications.
includeRunMetadataStart each message with the search and the number of jobs (on by default).

Tokens, webhook URLs, webhook headers and the WhatsApp recipient are secret inputs, so Apify stores them encrypted. The actor never writes them to your dataset or log.

Output

One row per job. Example (invented values):

{
"jobId": "1846201937455128576",
"title": "Python developer for a data pipeline in FastAPI",
"description": "We have a small FastAPI service that loads CSV exports into Postgres...",
"url": "https://www.upwork.com/jobs/~021846201937455128576",
"jobType": "HOURLY",
"experienceLevel": "IntermediateLevel",
"budgetAmount": null,
"budgetCurrency": null,
"hourlyBudgetMin": 35,
"hourlyBudgetMax": 60,
"engagementType": "PART_TIME",
"engagementDuration": "1 to 3 months",
"engagementDurationWeeks": 9,
"skills": ["Python", "FastAPI", "PostgreSQL"],
"publishTime": "2026-10-16T09:12:44.000Z",
"totalApplicants": 3,
"clientCountry": "Germany",
"clientCountryCode": "DE",
"clientTotalSpent": 12450,
"clientPaymentVerified": true,
"clientRating": 4.86,
"clientReviewCount": 21,
"clientHasFinancialPrivacy": false,
"searchIndex": 0,
"searchIndexes": [0],
"scrapedAt": "2026-10-16T09:14:02.511Z",
"source": "upwork.com"
}

All fields:

FieldMeaning
jobIdUpwork's id for the job.
title, descriptionAs posted. description is plain text.
descriptionHtml, descriptionMarkdownOnly when you ask for them in descriptionFormat. Made from the plain text.
url, portalUrlThe job's page on Upwork (the same link twice, for tools that expect either name).
jobTypeHOURLY or FIXED.
experienceLevelEntryLevel, IntermediateLevel or ExpertLevel.
budgetAmount, budgetCurrencyFixed price and its currency.
hourlyBudgetMin, hourlyBudgetMaxPosted hourly range.
weeklyRetainerBudgetWeekly retainer, when the client set one.
engagementTypeWorkload on hourly jobs: AS_NEEDED, PART_TIME or FULL_TIME.
engagementDuration, engagementDurationWeeksProject length as Upwork labels it, and in weeks.
skills, skillsDetailedSkill names, and the same skills with Upwork's ids and which were highlighted.
publishTime, createTime, sourcingTimestampWhen the job was published and created, and Upwork's own sourcing time for it.
totalApplicantsExact number of proposals so far.
personsToHireHow many freelancers the client wants.
enterpriseJob, premiumUpwork's enterprise and premium flags.
clientCountry, clientCountryCodeClient's country name and ISO code.
clientTotalSpent, clientSpentCurrencyWhat the client has spent on Upwork. null when the client hides it.
clientPaymentVerifiedtrue when Upwork reports the payment method as verified, false for any other reported status, null when not reported.
clientRating, clientReviewCountClient's rating (0 to 5) and number of reviews.
clientHasFinancialPrivacytrue when the client hides their spend.
salaryType, salaryMin, salaryMax, salaryCurrencyThe budget in one shape for both job types: a fixed price is both ends, an hourly job its range.
contentHashChanges when the job's title, description, type, experience level, budget, hourly range, length or skills change. Applicant count and client spend do not count as a change.
searchIndexIn a run with several searches, the search (its position in queries, from 0) that found this job.
searchIndexesEvery search index that matched this job, in a run with several searches; each job is delivered once.
changeTypeIn Incremental mode: NEW, UPDATED or UNCHANGED.
scrapedAtWhen the run read the job.
sourceAlways upwork.com.

Export the dataset as JSON, CSV, Excel or XML from Apify, or read it through Apify's API and integrations.

Several searches in one run

Put up to 10 searches in queries instead of query. They share one run, one set of filters and one dataset. A job that more than one search finds is delivered once, and searchIndexes lists every search that matched it. Give a search its own stateKey to keep its Incremental mode memory separate:

{
"queries": [
"python",
{"query": "react OR vue", "stateKey": "frontend"},
"\"data engineer\""
],
"maxAgeMinutes": 60
}

One run with three searches is cheaper and quicker to schedule than three runs.

Incremental mode

Turn on incrementalMode and schedule the actor. Each run delivers only jobs it has not delivered before for the same state key (NEW), plus jobs whose content changed since (UPDATED). Jobs are remembered for retentionDays (default 7, up to 30). Memory is kept separately for each Apify account, so nobody else's runs affect yours.

Leave it off if you de-duplicate on your side, or if you want every matching job in the time window on every run.

Hourly page limit

Each Apify account can read up to 60 result pages an hour with this actor. A page holds up to 50 jobs, so that is up to 3,000 jobs an hour before your filters. Pages read count toward it, and a page that had to be requested again can count more than once, so a slow hour can use the limit up sooner.

When a run reaches the limit it stops reading, delivers what it has read, finishes successfully and says so in its status message. The next run carries on. To stay under it, run less often, narrow your search, or lower maxPages.

There is also an overall limit shared by everyone using this actor. In a busy hour a run can reach it before your own 60 pages, and the status message reads the same. There is a limit on how many runs each account can start in an hour as well; a run over it is turned away as busy (see Limitations).

Pricing

$0.95 per 1,000 jobs delivered, plus $0.002 per run. No monthly fee. Every filter, incremental mode and every notification channel is included.

  • You pay for the jobs that reach your dataset, after your filters. A job your filters drop is never charged.
  • A job that matches several of your searches in one run is delivered, and charged, once.
  • A run that finds nothing new costs $0.002.
  • The rare 4 GB second run (see Limitations) is charged one run start per GB of memory, so $0.008. Its jobs are delivered and charged in the run you started, not twice.
  • Set a maximum charge per run and the run stays within it.
UseAbout
One search, 50 jobs$0.05
3 searches in one run every 5 minutes, about 2 new jobs a run$1.12 a day
The same schedule on a day with nothing new$0.58 a day

FAQ

Is this a replacement for Upwork's job RSS feeds? Yes. Upwork retired its job RSS feeds in 2024. Schedule this actor every few minutes with incremental mode on and a Telegram, Slack, Discord, WhatsApp or webhook channel, and you get the same stream of new jobs, with client details and filters RSS never had.

How fresh are the jobs? As fresh as Upwork's own listings at the moment the run reads them. For alerts, schedule the actor every 5 to 30 minutes with a maxAgeMinutes a little longer than the schedule.

Why did a run return fewer jobs than I expected? Check the status message. The usual reasons are your filters, the time window, maxResults, or the hourly page limit. Jobs with a field Upwork did not report are kept or dropped as each filter's description says.

Why is a field null? Because Upwork did not report it for that job. The actor never fills in a value it was not given.

Does it need my Upwork account or an API key? No. It does not log in to Upwork, and you do not need any third-party API key. Notifications use your own Telegram, Slack, Discord, WhatsApp or webhook details, which are optional.

Can I see invite-only jobs, client names or freelancer profiles? No. See Limitations.

How many jobs does a run return? Up to maxResults (default 100, at most 5,050) after your filters. How many are available depends on the search and the time window.

Can I use it from n8n, Make, Zapier or my own code? Yes. Use the webhook input to receive each run's jobs as one JSON POST, or start runs and read datasets through Apify's API and integrations.

What does the status message say? How many jobs were delivered and from how many searches, for example "12 job(s) delivered from 3 searches.", and whether the hourly limit was reached. A failed run says whether to retry.

Does the actor keep my data? Incremental mode remembers the ids and content hashes of jobs it delivered, per Apify account and state key, for retentionDays. Each run's delivered rows and its input (with secret fields masked) are also kept on our side for 30 days so that we can look into a failed run, and are then deleted.