Upwork Jobs Scraper | Skills, Budgets & Filters avatar

Upwork Jobs Scraper | Skills, Budgets & Filters

Pricing

$3.00 / 1,000 jobs

Go to Apify Store
Upwork Jobs Scraper | Skills, Budgets & Filters

Upwork Jobs Scraper | Skills, Budgets & Filters

Find public Upwork jobs by search query, job type, experience and rate filters. Export titles, descriptions, skills, budgets and job links for freelance research. Source availability and filter coverage may change.

Pricing

$3.00 / 1,000 jobs

Rating

0.0

(0)

Developer

Nick McNemar

Nick McNemar

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Find freelance jobs for your target skill

Upwork Jobs Scraper: Find public Upwork jobs by search query, job type, experience and rate filters. Export titles, descriptions, skills, budgets and job links for freelance research. Source availability and filter coverage may change.

Start with a small example

  1. Open Input, switch to JSON and paste the example below. Replace the example searchQueries with your own research targets when ready.
  2. Check the live Pricing tab and set a run spending limit.
  3. Run the Actor, then open the Workflow output view. Inspect title, jobType, experienceLevel, budget, skills, url.
  4. Export JSON for nested data or CSV for a spreadsheet. Review a small sample before increasing the scope.
{
"searchQueries": [
"web scraping"
],
"maxItems": 20
}

At the rate checked September 9, 2026, 20 saved rows cost $0.060 in Actor event fees ($3.00 per 1,000 rows). Actual returned rows determine the event fee. Taxes and other account services may be separate.

What can I use it for?

  • Job alerts and lead feeds. Poll your niche keywords sorted by newest and push fresh postings to Slack, email or a CRM minutes after they go live, before the proposal count climbs.
  • Freelance market research. Measure demand for a skill, compare hourly rate ranges across experience levels, or track how many open jobs mention a tool over time using the searchTotal field.
  • Agency and staffing pipelines. Find companies posting contract-to-hire or full-time freelance work in your service area and qualify them by budget and client hiring history.
  • Pricing benchmarks. Pull hundreds of fixed-price and hourly budgets for a category to see what clients actually offer before you quote.
  • Training and analysis corpora. Full description text plus a normalized skill taxonomy with stable skill UIDs is a good input for classifiers, embeddings and trend dashboards.

What data does it return?

Each job posting is one record. The fields buyers filter on most are at the top level, so the CSV export is usable without post-processing:

FieldExample
id2095973892295888069
urlhttps://www.upwork.com/jobs/~022095973892295888069
titlePython developer for e-commerce price monitoring
descriptionFull posting text, newlines preserved
jobTypeHOURLY or FIXED
experienceLevelEntry level, Intermediate or Expert
hourlyBudgetMin / hourlyBudgetMax25 / 60 (hourly jobs)
fixedPriceAmount150 (fixed-price jobs)
budget$25-$60/hr or $150 fixed
skills["Python", "Web Scraping", "Data Extraction"]
skillUidsStable Upwork taxonomy IDs matching skills
postedAt2026-09-04T20:34:52.288Z
rankPosition of the job in the search results
searchQuery / sortBy / searchTotalThe query that found it, the sort used, and the total number of matches Upwork reported
scrapedAtWhen the record was collected

contractorTier keeps Upwork's raw experience-level value and relevance keeps the raw ranking payload for anyone who needs them. Budgets are in US dollars, which is the only currency Upwork uses for job posts.

Illustrative output

Values explain the output shape; they are not a live result or guaranteed field coverage.

{
"id": "2095973892295888069",
"url": "https://www.upwork.com/jobs/~022095973892295888069",
"title": "Scrape supplier catalogs into a Google Sheet weekly",
"description": "We sell replacement parts and need a weekly export of three supplier catalogs (about 4,000 SKUs) into a shared Google Sheet.\n\nDeliverables:\n- Python script with scheduling\n- Handles login and pagination\n- Documentation for our ops team",
"jobType": "FIXED",
"experienceLevel": "Intermediate",
"contractorTier": "IntermediateLevel",
"hourlyBudgetMin": null,
"hourlyBudgetMax": null,
"fixedPriceAmount": 150,
"budget": "$150 fixed",
"skills": ["Python", "Web Scraping", "Data Scraping", "Google Sheets"],
"skillUids": ["1031626722463629312", "996364628040994822", "1031626722683932672", "1110580755107926016"],
"postedAt": "2026-09-04T20:34:52.288Z",
"relevance": { "position": "3" },
"rank": 4,
"searchQuery": "web scraping",
"sortBy": "recency",
"searchTotal": 382,
"scrapedAt": "2026-09-04T21:12:40.512Z"
}

How to use it

  1. Enter one or more Search queries, one per line. Keywords, a skill, a category name such as web development, a quoted phrase such as "react native" and operators such as python AND scraping all work the same way they do on Upwork. Enter * to list the newest jobs across every category.
  2. Set Max jobs to the number of unique postings you want across all queries. The default of 100 is a good first run.
  3. Optionally open Filters to restrict job type, experience level, hourly rate, client hiring history, client country, weekly workload or contract-to-hire.
  4. Click Start. Results appear in the Output tab as they arrive and can be downloaded as JSON, CSV, Excel or XML, or read through the API.

To build a job-alert feed, schedule the actor to run every few minutes with Sort by set to newest first and a small Max jobs, then deduplicate on id downstream.

Input example

{
"searchQueries": ["web scraping", "data extraction"],
"maxItems": 200,
"sortBy": "recency",
"jobType": "any",
"experienceLevels": ["IntermediateLevel", "ExpertLevel"],
"hourlyRateMin": 30,
"clientHires": ["1-9", "10-"],
"clientLocations": ["United States", "United Kingdom"],
"contractToHireOnly": false
}

You do not need to configure a proxy. Upwork's bot protection scores browser fingerprints and IP ranges differently from hour to hour, so the actor probes a few combinations at the start of each run — a Firefox fingerprint over a direct connection, then through residential and datacenter proxies, then a Chrome fingerprint the same way — and keeps the first one Upwork accepts. When Upwork is challenging everything for a moment, the actor waits and tries again for up to about three minutes before giving up. Leave Proxy configuration empty unless you specifically want to force a proxy group.

How it works

The actor first loads the Upwork homepage the way a normal Chrome browser would, which issues a short-lived anonymous visitor token. It then calls the public job-search endpoint with that token, requesting 50 results per page and walking the pages until it reaches your limit or the end of the results. Pages are fetched a few at a time with automatic retries and token refresh, and each job is written to the dataset the moment it is received. Jobs that appear under more than one query are saved once.

Only fields that Upwork exposes to logged-out visitors are requested. The actor never logs in, never reuses anyone's session and never solves challenges.

Pricing

You pay per job posting saved to the dataset. There are no subscriptions or minimums, duplicates across queries are not charged twice, and you can cap the maximum spend of any run before you start it.

Scope and responsible use

This actor reads only the job-posting data that Upwork shows to anyone browsing the site without an account. It requires no login, cookies or credentials, does not collect freelancer profiles or any personal data, and does not attempt to bypass access controls. Job descriptions are written by the clients who post them; use the data in line with the laws that apply to you and with Upwork's terms.

Limitations

  • No client statistics. Client total spend, rating, review count, payment-verified status, location and proposal counts are only served to logged-in users, so they are not included. You can still filter by client hiring history and client country, and sort by client spend or rating, because Upwork applies those on its side.
  • 5,050 results per query. Upwork stops paging at offset 5,000. To go deeper, split a broad search into several narrower queries or filter combinations; results are deduplicated across queries automatically.
  • Derived job links. Upwork does not return job URLs to anonymous visitors. The url field is built from the numeric job ID using Upwork's current link format.
  • Hourly rate filters only affect hourly jobs. Fixed-price postings are still returned when a rate filter is set; combine with Job type if you want hourly jobs only.
  • Bot protection. Upwork uses Cloudflare. If runs fail with HTTP 403, enable Apify residential proxies under Proxy configuration.
  • Field availability depends on what Upwork exposes to visitors and can change without notice.

Something not working?

Open an issue on this actor's Issues tab with your input and the run ID. Reproducible problems are usually fixed within days.

Interpret empty results and errors

Check the run status and log if no records are returned. Restrictive filters, inaccessible content or a changed source page can produce an empty result; empty output does not prove there are no matching records on the source.

Billing follows saved job or company rows. Disclosed salary and optional fields may be missing. Proxy selection and run duration can affect operating costs. Do not increase concurrency or enable a more expensive proxy merely to work around an unclear result.

For a reproducible problem, open this Actor’s Issues tab with a small public input, expected behavior and relevant error text. Remove tokens and confidential information. No response-time or uptime guarantee is offered.

Use the result in an automation

In Make or n8n, use the Apify integration to run this Actor with the same JSON input, wait for completion, then retrieve its default dataset. Route failed runs and error rows to a review step before sending valid results to your spreadsheet or CRM. Scheduling does not make these Actors emit only new records: deduplicate downstream using the source URL or record ID.

For Node.js, install the official apify-client package and set your own APIFY_TOKEN environment variable. The following example starts a paid run with a small spending limit.

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const input = {
"searchQueries": [
"web scraping"
],
"maxItems": 20
};
const run = await client.actor('keystonelabs/upwork-jobs-scraper').call(input, {
memory: 512, timeout: 180, maxTotalChargeUsd: 0.10
});
if (run.status !== 'SUCCEEDED') throw new Error('Run did not succeed: ' + run.id);
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 100 });
console.log(items);

The first 100 rows are retrieved in this example. Use the dataset API pagination for a larger result. Keep credentials out of shared inputs and source files.

A useful next step