---
title: "How to Scrape Upwork Jobs | Scrape.ly"
description: "Scrape Upwork jobs by keyword: title, budget, skills, experience level, posting time and client stats. A real run, its cost, and API, Python and MCP code."
url: https://scrape.ly/blog/scrape-upwork-jobs
markdown: https://scrape.ly/blog/scrape-upwork-jobs.md
---

[Tutorials](https://scrape.ly/blog/category/tutorials.md)

# How to Scrape Upwork Jobs: Titles, Budgets, Skills and Client Stats

Scrape Upwork jobs by keyword or search URL: title, description, budget or hourly range, skills, experience level, posting time and the client's country, rating and spend. No-code steps, curl, Python, Node.js and MCP, with a real run from 27 October 2026 and its exact cost.

[Openrole Actor publisher on the Scrape.ly Store](https://scrape.ly/blog/author/openrole.md)

- Published

  Oct 27, 2026

- Reading time

  8 min read

![Pixel-art robot with an orange >\_ on its terminal head pulls a job card with a briefcase off a cork job board topped with the black Upwork logo and pinned with job cards, and drops it into a crate](https://scrape.ly/media/scrape-upwork-jobs.png?w=1080)

To scrape Upwork jobs, give a ready-made Actor a keyword such as `web scraping` (or an Upwork search URL with your filters) and it returns one row per job post: title, description, fixed budget or hourly range, skills, experience level, when it was posted and, when Upwork shows them, the client's country, rating and total spend. On Scrape.ly that's one input and one click, or one API call, with no Upwork login and no API application.

This guide covers the fields you get, the no-code steps, the same job in curl, Python, Node.js and through an AI agent, a real run from 27 October 2026 (the six newest "web scraping" jobs), its log and exact cost, and how to turn it into a new-contract alert.

## What data you can get from Upwork job posts

The [Upwork Job Scraper](https://scrape.ly/store/openrole/upwork-job-scraper.md) returns one row per job:

| Field                                                | What it holds                                                |
| ---------------------------------------------------- | ------------------------------------------------------------ |
| `id`, `subId`, `url`                                 | Upwork job IDs and the link to the post                      |
| `title`, `description`                               | Job title and full description                               |
| `budget`                                             | Fixed budget (`$200.00`) or hourly range (`20 - 30`) as text |
| `jobType`, `experienceLevel`                         | `Fixed` or `Hourly`; Entry, Intermediate or Expert           |
| `relativeDate`, `absoluteDate`                       | "Posted 6 hours ago" and the exact timestamp                 |
| `tags`                                               | The skills attached to the job                               |
| `clientLocation`, `clientRating`, `clientTotalSpent` | The client's country, rating and lifetime spend, when shown  |
| `paymentVerified`                                    | Payment verification, or `unknown` when hidden               |

Client name, proposal counts, screening questions and hire rate exist as keys but stay empty: Upwork shows them only to signed-in users.

## How to scrape Upwork jobs without code

1. Open the Upwork Job Scraper in the Scrape.ly Store.
2. Enter a keyword in **Search query**, or start from the [search preset](https://scrape.ly/store/openrole/upwork-job-scraper/presets/search.md). This is the input we ran:

```json
{
  "query": "web scraping",
  "maxResults": 6,
  "sort": "newest"
}
```

3. Click **Start**. Our run took 22 seconds.
4. Open the dataset and export it as JSON, CSV, Excel or HTML.

The input fields that matter most:

- `query`: keywords as you'd type them into Upwork's job search.
- `rawUrl`: an `upwork.com/nx/search/jobs` URL. Set up a search on Upwork once, paste the link, and every filter comes with it. The [raw URL preset](https://scrape.ly/store/openrole/upwork-job-scraper/presets/raw-url.md) shows how.
- `maxResults`: how many jobs to save (default 10, no upper limit).
- `sort`: `newest` for alerts, `relevance` for research.
- `maxJobAge`: drop older posts, for example `{"value": 24, "unit": "hours"}`.
- `experienceLevel`, `jobType`, `minHourlyRate`/`maxHourlyRate`, `minFixedPrice`/`maxFixedPrice`, `clientHistory`, `location`: Upwork's own filters. The [filters preset](https://scrape.ly/store/openrole/upwork-job-scraper/presets/search-filters.md) combines several.
- `customFilters`: your own rules on saved fields with `includes`, `notIncludes`, `equals` and `notEquals`, for example only jobs whose tags include "Puppeteer".

Scrape.ly Store Upwork Job Scraper Public Upwork job posts by keyword or search URL: title, description, budget, skills, experience level, posting time and client country, rating and spend, with Upwork's filters.

[Try it free](https://scrape.ly/store/openrole/upwork-job-scraper.md) [Browse the Store](https://scrape.ly/store.md)

## How to scrape Upwork jobs with the API

Create a token under **Settings → API & Integrations** in the dashboard and store it as `SCRAPELY_TOKEN`. A 10-job run takes about 20 to 40 seconds, so the synchronous endpoint (45-second wait) works for small pulls:

```shell
curl -X POST "https://api.scrape.ly/v2/actors/openrole~upwork-job-scraper/run-sync-get-dataset-items"
  -H "Authorization: Bearer $SCRAPELY_TOKEN"
  -H "Content-Type: application/json"
  -d '{"query": "web scraping", "maxResults": 10, "sort": "newest", "maxJobAge": {"value": 24, "unit": "hours"}}'
```

For hundreds of jobs, start the run asynchronously so a `408` timeout can't tempt you into starting a second run: `POST /v2/actors/openrole~upwork-job-scraper/runs`, poll `/v2/actor-runs/RUN_ID?waitForFinish=60` until `SUCCEEDED`, then read `/v2/actor-runs/RUN_ID/dataset/items?format=csv`. The [API docs](https://scrape.ly/docs/api-actors.md) show the full flow.

In Python (`pip install scrapely-client-python`), this rate-benchmarking script pulls 200 recent hourly jobs for a skill and prints the median of each job's hourly range, so you know what clients actually offer before you bid:

```python
import os
from statistics import median

from scrapely_client import ScrapelyClient

client = ScrapelyClient(os.environ["SCRAPELY_TOKEN"], api_url="https://api.scrape.ly")

def budget_range(text):
    if not text:
        return None
    parts = [p.strip().replace("$", "").replace(",", "") for p in text.split("-")]
    nums = [float(p) for p in parts if p]
    return (min(nums), max(nums)) if nums else None

run = client.actor("openrole/upwork-job-scraper").call(
    run_input={"query": "web scraping", "maxResults": 200, "jobType": ["hourly"], "sort": "newest"}
)
mids = []
for job in client.dataset(run.default_dataset_id).iterate_items():
    r = budget_range(job.get("budget"))
    if r:
        mids.append(sum(r)  2)
print(len(mids), "hourly jobs with a range, median midpoint", median(mids), "USD/h")
```

In JavaScript or Node.js (`npm install scrapely-client`), a new-contract alert: run it every hour on a schedule and post anything from the last hour to a Slack webhook:

```javascript
import { ScrapelyClient } from 'scrapely-client';

const client = new ScrapelyClient({ token: process.env.SCRAPELY_TOKEN, baseUrl: 'https://api.scrape.ly' });

const run = await client.actor('openrole/upwork-job-scraper').call({
  query: 'puppeteer',
  maxResults: 20,
  sort: 'newest',
  maxJobAge: { value: 1, unit: 'hours' },
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const job of items) {
  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: `${job.title} | ${job.jobType} ${job.budget ?? ''} | ${job.url}` }),
  });
}
```

The [Upwork API](https://scrape.ly/api/upwork-api.md) page shows the same Actor as a ready-made API, and the [Python client](https://scrape.ly/docs/client-python.md) docs cover paging through large datasets.

### With an AI agent (MCP)

Scrape.ly runs a hosted MCP server at `https://mcp.scrape.ly`. In Cursor, add it to `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "scrapely": {
      "url": "https://mcp.scrape.ly",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}
```

In Claude Code, run `claude mcp add --transport http scrapely https://mcp.scrape.ly`, then `/mcp` to sign in with OAuth. Then ask: "Use scrapely to get the 50 newest Upwork jobs for 'n8n automation', and list the ones from clients who have spent over $10,000, with budget and skills." The [MCP docs](https://scrape.ly/docs/mcp.md) list ChatGPT, VS Code, Windsurf and the other clients.

## A real run: the newest "web scraping" jobs

We ran the input above on 27 October 2026. It saved 6 jobs in 22 seconds. Upwork doesn't show client names to logged-out visitors, so there are none to hide; the client columns below are the country and spend the public post shows:

| Title                                                              | Type   | Budget  | Level        | Posted (UTC)     | Client country | Client spent |
| ------------------------------------------------------------------ | ------ | ------- | ------------ | ---------------- | -------------- | ------------ |
| Develop and maintain web scrapers                                  | Hourly | 20 - 30 | Intermediate | 2026-10-27 10:19 | United States  | $238,347     |
| Data Entry & Public-Records Research - US Local Contacts & Details | Fixed  | $200.00 | Intermediate | 2026-10-27 07:11 | India          | $2,582       |
| Download website content as PDF                                    | Fixed  | $7.50   | Entry        | 2026-10-26 21:21 | —              | —            |
| Senior Python/AsyncIO Engineer: Fix "Thundering Herd"…             | Fixed  | $400.00 | Intermediate | 2026-10-26 16:22 | —              | —            |
| Web Scraping for University Data                                   | Fixed  | $250.00 | Intermediate | 2026-10-26 09:08 | India          | —            |

```plaintext
20261027T16:25:05.803Z [scrapely.actor] INFO  Searching public jobs, limit 6, sort newest.
20261027T16:25:09.425Z [scrapely.actor] INFO  curl_cffi homepage status 403, visitor cookie no, cookie names __cf_bm.
20261027T16:25:09.427Z [scrapely.actor] INFO  Datacenter was blocked. Trying residential.
20261027T16:25:12.482Z [scrapely.actor] INFO  Public search session is ready (curl_cffi residential).
20261027T16:25:19.312Z [scrapely.actor] INFO  Job page was not readable: Job page returned HTTP 403.
20261027T16:25:25.181Z [scrapely.actor] INFO  Strategy curl_cffi residential, jobs 6.
20261027T16:25:25.181Z [scrapely.actor] INFO  [Status message]: Saved 6 jobs
```

What the six rows show:

- **Budgets span two orders of magnitude.** From $7.50 to download a 153-page website as PDF, to $400 for a Python asyncio fix, to an ongoing 20-hour-a-week scraper role at $20–30 an hour. One hourly job listed no range at all. That spread is why you benchmark by skill before you bid.
- **Client stats are the best filter.** Three of six posts showed the client's rating and spend. The client behind the long-term role had spent $238,347 with a 5.0 rating; another had spent $9,410 with a 2.5 rating. `clientTotalSpent` and `clientRating` are the fastest way to sort serious buyers from tire-kickers.
- **Newest-first means fresh.** The six posts were 6 to 33 hours old. With `maxJobAge` and an hourly schedule, you see a job within the hour it's posted.
- **Tags are noisy but useful.** "Data Scraping" appeared on five of six jobs; one job carried 20 tags, from Scrapy to FastAPI. Use `customFilters` on `tags` to keep only the stacks you work in.
- **Verification stays hidden.** `paymentVerified` was `unknown` on every row, because Upwork shows it only to signed-in users. Use client spend and rating instead.
- **The run adapts to blocks.** Upwork refused the datacenter pool, so the Actor switched to residential addresses and carried on. One job page answered 403, and that job was still saved from the search results.

## What it costs

The Actor charges one event per saved job, platform usage (compute, proxy and any browser session) included. Price checked on the Store page on 27 October 2026: **$0.00323 per job** ($3.23 per 1,000).

**Our run:** 6 × $0.00323 = $0.01938 at list price. The account that ran it has a plan Store discount, so it was billed a little less.

**At scale:** an hourly alert that finds about 50 new jobs a day saves roughly 1,500 jobs a month, which costs 1,500 × $0.00323 = $4.85. Runs that find nothing new cost nothing in job events.

## What to watch out for

- **Some fields are login-only.** Client name, proposal counts, screening questions, hire rate and payment verification stay empty. The payment-verified filter is ignored for the same reason.
- **Client stats come from the job page.** When Upwork rate-limits the run (HTTP 429) or a job page refuses the request, as one did in our run, the job is saved without its page details. Treat missing client stats as unknown, not as zero.
- **Budget is text.** Fixed prices come as `$200.00`, hourly ranges as `20 - 30`, and some hourly posts have none. Parse it, as the Python script does.
- **Bigger runs need more time.** A 10-job run takes 20 to 40 seconds over plain HTTP and about a minute when a browser session is needed. Raise the run timeout for runs of hundreds of jobs.
- **Job posts can contain personal data.** Descriptions sometimes include names or contact details. Strip them before you store or share posts.

Upwork also has an official API, but it's built for a different job. Its API terms say "Your use of the Upwork API is limited to the purpose of facilitating your own or your Users’ use of the Upwork Site and Site Services", and access goes through an application Upwork reviews. For market research, rate benchmarking or a feed into your own tools, the Actor is the direct route.

## Is it legal to scrape Upwork jobs?

The Actor reads public job search and job pages without logging in. Upwork's own rules restrict automated access, though: its [Terms of Use](https://www.upwork.com/legal) (version 4.12, effective September 10, 2025, read on 27 October 2026) say you can't "use a robot, spider, scraper, or similar mechanisms on our site without written permission", and its robots.txt disallows the `/nx/` paths that hold the job search. Whether and how those terms bind you depends on your jurisdiction, whether you have an Upwork account that accepted them, and what you do with the data.

Job posts are written by clients, some of whom are private individuals, so treat descriptions as potentially personal data under the GDPR and similar laws. Keep what you need (title, budget, skills, timing), don't republish full posts, and don't use the data to contact clients outside Upwork. Our guide to [whether web scraping is legal](https://scrape.ly/blog/is-web-scraping-legal.md) covers the basics, including the GDPR. This is not legal advice; for a commercial project, ask a lawyer about your case.

## Why Scrape.ly for Upwork jobs

- **No account, no API application.** Public job posts by keyword or search URL with one token, without putting a freelancer or client account at risk.
- **Filters that match Upwork's.** Experience, job type, rate and price ranges, client history, location and job age are input fields, plus custom rules on any saved field.
- **Blocks handled for you.** The Actor tries datacenter, then residential, then a browser session, and the per-job price already includes all of it.
- **Every job board on one platform.** The same token runs [Indeed](https://scrape.ly/blog/scrape-indeed.md), [LinkedIn Jobs](https://scrape.ly/blog/scrape-linkedin-jobs.md) and [Google Jobs](https://scrape.ly/blog/scrape-google-jobs.md); our [job scraping](https://scrape.ly/blog/job-scraping.md) guide shows how to merge them into one feed.

## FAQ

### Can you scrape Upwork jobs for free?

Yes, for small jobs. The Free plan includes $5 of usage credit every month with no credit card. At list price that covers about 1,500 Upwork jobs.

### How do I get alerts for new Upwork jobs?

Set `sort: "newest"` and `maxJobAge` to `{"value": 1, "unit": "hours"}`, save the input and schedule it hourly. Send the dataset to Slack, email or a webhook, as in the Node.js example.

### Can I use an Upwork search URL with my filters?

Yes. Build the search on Upwork, copy the `upwork.com/nx/search/jobs` URL into `rawUrl`, and the Actor reuses every filter in it. It replaces `query` and the filter fields.

### Does the scraper return client names or proposal counts?

No. Upwork shows client names, proposal counts, screening questions and hire rate only to signed-in users, so those fields stay empty. Client country, rating and total spend come through when the post shows them.

### Can I scrape Upwork freelancer profiles?

This Actor collects job posts only. Freelancer profiles are personal data; if you need them, check Upwork's terms and privacy law first.

### Can I scrape Upwork jobs with Python?

Yes. Install `scrapely-client-python` and call `openrole/upwork-job-scraper`, as in the rate-benchmarking script above. The scraping runs in the cloud, so you don't manage Upwork's blocks or proxies.

Scrape.ly Store Scrape your first Upwork jobs Enter a keyword or paste an Upwork search URL and get budgets, skills, posting times and client stats at $3.23 per 1,000 jobs, platform usage included.

[Try it free](https://scrape.ly/store/openrole/upwork-job-scraper.md) [Browse the Store](https://scrape.ly/store.md)

About the author

[Openrole](https://scrape.ly/blog/author/openrole.md) Actor publisher on the Scrape.ly Store

Every open role, wherever it is posted: job boards, company ATS pages and freelance markets in dozens of countries. For recruiters, labour-market analysts and job aggregators. A Scrapely studio.

- [Website](https://scrape.ly/users/openrole.md)

Keep reading

## More in Tutorials

- [![Pixel-art robot with an orange >\_ on its terminal head pulls a company card with a small building and a blue line out of a monitor showing the blue Crunchbase logo above rows of company profiles with blue funding bars, and drops it into a crate](https://scrape.ly/media/scrape-crunchbase.png?w=750)](https://scrape.ly/blog/scrape-crunchbase.md)
  ### [How to Scrape Crunchbase Company Data: Search, Profiles, Ranks and IDs](https://scrape.ly/blog/scrape-crunchbase.md)
  [Scrape Crunchbase company data without an account: search by name or open organization pages for permalink, UUID, categories, location, website, employee band and rank. No-code steps, curl, Python, Node.js and MCP, plus real runs from 27 October 2026 and their exact cost.](https://scrape.ly/blog/scrape-crunchbase.md)
- [![Pixel-art robot with an orange >\_ on its terminal head pulls a comment bubble out of a phone showing the TikTok note logo above a list of comments, and drops it into a crate](https://scrape.ly/media/scrape-tiktok-comments.png?w=750)](https://scrape.ly/blog/scrape-tiktok-comments.md)
  ### [How to Scrape TikTok Comments: Text, Likes, Reply Counts and Creator Engagement](https://scrape.ly/blog/scrape-tiktok-comments.md)
  [Scrape TikTok comments from any public video, profile, hashtag or search: comment text, time, likes, reply count and whether the creator liked or pinned it, plus the video's own stats. No-code steps, curl, Python, Node.js and MCP, with real runs from 27 October 2026 and their exact cost.](https://scrape.ly/blog/scrape-tiktok-comments.md)
- [![Pixel-art robot with an orange >\_ on its terminal head pulls a review slip with red rating squares out of a monitor showing the red G2 logo above rows of software reviews, and drops it into a crate](https://scrape.ly/media/scrape-g2-reviews.png?w=750)](https://scrape.ly/blog/scrape-g2-reviews.md)
  ### [How to Scrape G2 Reviews: Ratings, Likes and Dislikes, Reviewer Roles and Product Data](https://scrape.ly/blog/scrape-g2-reviews.md)
  [Scrape G2 reviews for any software product: star rating, likes, dislikes, problems solved, reviewer role, company size, validation and review source, plus G2's pros and cons summary. No-code steps, curl, Python, Node.js and MCP, with a real run from 27 October 2026 and its exact cost.](https://scrape.ly/blog/scrape-g2-reviews.md)

## The internet is full of data. Start owning yours.

Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes.

[Get Started Free](https://my.scrape.ly/register) [Chat with Support](mailto:hello@scrape.ly?subject=Question%20about%20Scrape.ly\&body=Hi%2C%20I%20have%20a%20question%20about%20Scrape.ly.)

No credit card. No setup calls.
