# Upwork Jobs Scraper (Job Alerts, Client Stats, No Login) (`locaihost/upwork-jobs`) Actor

Scrape new Upwork jobs with no login: title, budget in USD, skills, experience level, proposals band and full description, plus the client's public stats (payment verified, total spent, hires, rating, country). Client filters and only-new mode make a job alert feed. $1 per 1,000 jobs.

- **URL**: https://apify.com/locaihost/upwork-jobs.md
- **Developed by:** [locaihost data](https://apify.com/locaihost) (community)
- **Categories:** Jobs, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.55 / 1,000 jobs

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Upwork Jobs Scraper (Job Alerts, Client Stats, No Login)

Get new Upwork jobs as rows, each with budget in USD, skills, experience level, proposals band and the full description, plus the numbers you check before you bid: the client's payment verification, total spent, hires, rating, reviews and country. From Upwork's public job search and job pages; no Upwork account needed.

- **Run a job alert:** schedule a saved search every 15 to 60 minutes and get only jobs you haven't seen.
- **Skip weak clients:** keep only payment-verified clients who have spent and hired before.
- **Bid early:** keep only jobs with few proposals so far.
- **Study the market:** budgets, skills and client spend across a category, newest first.
- **Feed your own tools:** send each new job to a sheet, Slack or n8n with Apify's integrations.

### Quick start

Paste this into the **JSON** tab of the input and click **Start**:

```json
{"searchQueries": ["python"], "maxJobsPerSearch": 10, "proxyConfiguration": {"useApifyProxy": true}}
```

You get the 10 newest matching jobs in about a minute: most of that is opening an Upwork visitor session, after which each job takes about a second. `onlyNew` is on by default, so running the same search again returns only jobs posted since.

A typical alert set-up, saved as a task and scheduled:

```json
{
  "searchQueries": ["python", "web scraping"],
  "onlyNew": true,
  "paymentVerifiedOnly": true,
  "minClientSpendUsd": 1000,
  "maxProposals": 15
}
```

### What data can I get?

| Group | Fields |
|---|---|
| Job | `jobId`, `title`, `url`, `postedAt`, `postedMinutesAgo`, `description`, `skills`, `category`, `categoryGroup`, `occupation` |
| Budget and terms | `jobType`, `budgetText` (such as "$30-$60/hr"), `fixedBudgetUsd`, `hourlyMinUsd`, `hourlyMaxUsd`, `experienceLevel`, `duration`, `workload` |
| Activity | `proposals` (band such as "5 to 10"), `interviewing`, `invitesSent`, `hired`, `positionsToHire`, `clientLastViewedAt` |
| Client | `clientCountry`, `clientPaymentVerified`, `clientTotalSpentUsd`, `clientHires`, `clientActiveHires`, `clientHoursBilled`, `clientRating`, `clientReviews`, `clientMemberSince`, `clientSpendPerHireUsd` (total spent divided by hires), `clientStatsAvailable` |
| Preferences | `freelancerCountries`, `freelancerRegions`, `preferredJobSuccessScore` |
| Run | `searchQuery`, `scrapedAt` |

Every row has the same columns. A value Upwork didn't show is `null`.

One real row (run on 2026-10-18, shortened):

```json
{
  "jobId": "~022111660249868354735",
  "title": "Senior Big Data Engineer Needed to Troubleshoot and Fix Apache Spark Data Load Issue",
  "url": "https://www.upwork.com/freelance-jobs/apply/Senior-Big-Data-Engineer-Needed-to-Troubleshoot-and-Fix-Apache-Spark-Data-Load-Issue_~022111660249868354735/",
  "postedAt": "2026-10-18T03:27:06.251Z",
  "jobType": "fixed",
  "fixedBudgetUsd": 100,
  "budgetText": "$100 fixed",
  "experienceLevel": "expert",
  "duration": "Less than 1 month",
  "category": "Data Mining & Management",
  "skills": ["Apache Hadoop", "Python", "Machine Learning", "Algorithm Development"],
  "proposals": "Less than 5",
  "interviewing": 0,
  "clientCountry": "United States",
  "clientPaymentVerified": true,
  "clientTotalSpentUsd": 16366,
  "clientHires": 10,
  "clientHoursBilled": 513,
  "clientRating": 5,
  "clientReviews": 6,
  "clientMemberSince": "2026-02-22",
  "clientSpendPerHireUsd": 1637,
  "clientStatsAvailable": true
}
```

The dataset has three views (**Jobs**, **Client quality**, **Budget and details**) and exports to JSON, CSV, Excel or HTML. The run's `SUMMARY` record lists jobs returned and skipped, with the reasons.

### How much does it cost?

You pay per job returned, with client stats included. There is no charge for Apify platform usage on top, and the residential proxy is included.

| Row | Free and Starter | Scale | Business |
|---|---|---|---|
| Job | $1.00 per 1,000 | $0.75 per 1,000 | $0.55 per 1,000 |

Plus $0.00005 per run start. **Apify's free $5 monthly credit covers about 5,000 jobs.** Free-plan runs stop at 1,000 jobs each; any paid Apify plan removes the cap.

**What's free:** a job found by two of your searches (returned and charged once), jobs skipped by `onlyNew`, jobs your client, budget or proposal filters leave out, and runs that can't open Upwork or find nothing. To cap a run's spend, set **Maximum cost per run** in the run options.

### Limits, stated up front

- **Logged-out only.** It reads what Upwork shows a visitor without an account: the public job search and public job pages. It never logs in and never asks for your cookies.
- **Upwork sits behind Cloudflare.** The Actor reads Upwork in a real browser over residential IPs and handles the "Verify you are human" check when it appears. Opening that session is the slow part of a run. If it can't be opened, the run stops and nothing is charged.
- **Not shown to logged-out visitors, so not here:** the client's name or company, jobs posted, hire rate, open jobs, average hourly rate paid, and connects price.
- **About 1 job in 10** has a client box Upwork shows to registered users only. Those jobs still come back with their search data and `clientStatsAvailable: false`; when a client filter is on, they are left out.
- **Client filters are applied by the Actor.** Upwork's own search sends them to the login page, so `paymentVerifiedOnly`, `minClientSpendUsd`, `minClientRating`, `minClientHires`, `maxProposals`, `minHourlyRateUsd` and `minFixedBudgetUsd` are checked on the public data. Budget filters are checked before a job page is opened.
- **About 5,000 results per search** is Upwork's own cap. Split broad searches by category or job type.
- **Budgets** are in USD as Upwork lists them. A job with no budget given says so in `budgetText`.
- **Time limit:** if a run nears its timeout, it stops, keeps what it found and says so in the status message.

### Only new since last run, and schedules

`onlyNew` is on by default. The Actor remembers which jobs each saved search already returned, so a schedule becomes an alert feed and you never pay twice for the same job. Results are newest first, and a run stops reading as soon as it reaches jobs an earlier run returned. Add `postedWithinHours` (for example 24) to cap how far back a first run goes, and turn `onlyNew` off to get every matching job again. Set up a schedule under **Schedules** in Apify Console.

### Integrations: API, n8n, Make and AI agents

**API.** Run it and get the rows back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/locaihost~upwork-jobs/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["shopify"], "maxJobsPerSearch": 20, "paymentVerifiedOnly": true}'
```

Apify's API clients for Python and JavaScript work the same way.

**n8n, Make, Zapier, Google Sheets, Slack, webhooks.** Apify's built-in integrations send each run's dataset wherever you need it, so every scheduled run can post its new jobs to a channel.

**AI agents (MCP).** Add this Actor to Claude, Cursor or any MCP client with `https://mcp.apify.com/?tools=locaihost/upwork-jobs`, then ask something like *"Show me new fixed-price Shopify jobs from payment-verified clients who have spent over $1,000."*

### FAQ

#### How much does it cost to scrape Upwork jobs?

$1 per 1,000 jobs on the Free and Starter plans, client stats included, and less on Scale and Business. Apify's free $5 monthly credit covers about 5,000 jobs.

#### Can I use it through the API or from Python?

Yes. Use the `run-sync-get-dataset-items` call above, or Apify's Python and JavaScript clients. Every run's data is also available as JSON, CSV or Excel from its dataset.

#### Can I use it from an AI agent?

Yes. Through the Apify MCP server (`https://mcp.apify.com/?tools=locaihost/upwork-jobs`) any MCP client can run a search and read the jobs back.

#### Is it legal to scrape Upwork jobs?

The Actor reads only Upwork's public job search and job pages, what anyone sees without an account. It never logs in. Upwork's own terms restrict automated access, so weigh that for your use. Client data stays at public business stats and country: no names, companies or cities. Email addresses, phone numbers and WhatsApp or Telegram links typed into descriptions are removed. Use the data to find work and study the market, not to contact people outside Upwork. You are responsible for how you use the results.

#### Which search syntax works?

Upwork's own: quotes for a phrase, AND, OR, NOT (for example `"react native" OR flutter`). `categories`, `jobType`, `experienceLevel` and `clientLocations` are sent to Upwork's search too. With no query, a category search returns the newest jobs in the whole category.

#### Why are some fields empty?

Jobs whose client box is for registered users only have `clientStatsAvailable: false` and empty client fields. New clients have no rating or spend yet, and some jobs give no budget, duration or workload.

### More from locaihost data

- [LinkedIn Jobs Scraper](https://apify.com/locaihost/linkedin-jobs): full-time and contract jobs from LinkedIn, no login.
- [Google Jobs Scraper](https://apify.com/locaihost/google-jobs): jobs from many boards at once, with every apply link.
- [Careers Page Jobs Scraper](https://apify.com/locaihost/careers-jobs): open roles straight from company career pages.

### Did it help?

If this saved you time, a quick rating on the Store helps other people find it.

### Support

Something not working, or a field you need? Open an issue on the **Issues** tab: we answer issues within 24 hours.

# Changelog

This Actor's version history is a separate document: https://apify.com/locaihost/upwork-jobs/changelog.md

# Actor input Schema

## `searchQueries` (type: `array`):

What you'd type into Upwork's job search, e.g. "python", "shopify", "logo design". Upwork's search syntax works: quotes for a phrase, AND, OR, NOT. Each query is one search. Leave empty to search whole categories instead.

## `maxJobsPerSearch` (type: `integer`):

Per search query (or per category search). Newest first. Leave empty for 50. Upwork shows at most about 5,000 results for one search.

## `onlyNew` (type: `boolean`):

Remembers which jobs this search already returned, so a schedule every 15 to 60 minutes becomes a job alert and you never pay twice for a job. Turn off to get every matching job again.

## `includeClientStats` (type: `boolean`):

Read each job's public page for the client box (payment verified, total spent, hires, rating, country, member since) and activity (proposals, interviewing, invites, last viewed). Turn off for faster rows without them.

## `categories` (type: `array`):

Optional. Only jobs in these Upwork categories. With no search queries, the newest jobs of the whole category are returned.

## `jobType` (type: `string`):

Hourly, fixed price, or both.

## `experienceLevel` (type: `array`):

Optional. Only jobs asking for these levels.

## `clientLocations` (type: `array`):

Optional. Only jobs from clients in these countries or regions, written the way Upwork's filter writes them, e.g. "United States", "United Kingdom", "Europe".

## `postedWithinHours` (type: `integer`):

Optional. Only jobs posted in the last this many hours, e.g. 24. Leave empty for any time.

## `paymentVerifiedOnly` (type: `boolean`):

Leave out jobs whose client has no verified payment method.

## `minClientSpendUsd` (type: `integer`):

Leave out clients who have spent less than this on Upwork, e.g. 1000.

## `minClientRating` (type: `number`):

Leave out clients rated below this (out of 5), and clients with no reviews yet, e.g. 4.5.

## `minClientHires` (type: `integer`):

Leave out clients who have hired fewer freelancers than this on Upwork.

## `minHourlyRateUsd` (type: `integer`):

Hourly jobs whose top rate is below this, or that give no rate, are left out. Fixed-price jobs are kept.

## `minFixedBudgetUsd` (type: `integer`):

Fixed-price jobs with a smaller budget, or none, are left out. Hourly jobs are kept.

## `maxProposals` (type: `integer`):

Leave out jobs that already have more proposals than this, e.g. 10 for jobs with little competition.

## `proxyConfiguration` (type: `object`):

Apify Proxy by default: residential first (Upwork lets it through without a challenge far more often), datacenter as the fallback. Included in the price.

## Actor input object example

```json
{
  "searchQueries": [
    "python",
    "web scraping",
    "\"react native\""
  ],
  "maxJobsPerSearch": 10,
  "onlyNew": true,
  "includeClientStats": true,
  "categories": [
    "Web, Mobile & Software Dev"
  ],
  "jobType": "any",
  "clientLocations": [
    "United States",
    "Canada"
  ],
  "paymentVerifiedOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

One record per Upwork job: budget, experience level, proposals band and client stats.

## `summary` (type: `string`):

Jobs returned and skipped, filter drops, field-drift check and measured cost per 1,000.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "python"
    ],
    "maxJobsPerSearch": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("locaihost/upwork-jobs").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["python"],
    "maxJobsPerSearch": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("locaihost/upwork-jobs").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "python"
  ],
  "maxJobsPerSearch": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call locaihost/upwork-jobs --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,locaihost/upwork-jobs"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ntTwQxehPGWbpjNjV/builds/cddVj2nlaytzwkuul/openapi.json
