# Upwork Jobs Scraper - Job Search with Client Details (`benthepythondev/upwork-jobs-scraper`) Actor

Scrape Upwork jobs by keyword or search URL: title, description, budget, skills, posted time, plus client country, total spent, hires, rating and proposals. No login.

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

## Pricing

from $2.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 Search with Client Details

Search Upwork's job board by keyword or search URL and get every job as structured data: title, full description, hourly range or fixed budget, skills, experience level and time posted. Each job can include what Upwork shows about the client: country, total spent, hires, rating, payment verification and the number of proposals so far. Export to JSON/CSV/Excel, run on a schedule, call via API, or connect to Make, Zapier or n8n.

### 🔎 What is the Upwork Jobs Scraper?

It is an Apify Actor that runs Upwork's public job search for you and saves one row per job. No Upwork account, cookies or login are needed, because it reads the same public data a visitor sees.

Type a keyword such as "python developer", or set up a search with any filters on upwork.com and paste its address. With **Include client details** on, the Actor also opens each job and adds the client and activity figures that decide whether a job is worth a proposal.

A time limit turns it into a job alert. Set **Posted within** to 1 hour, schedule the Actor hourly, and every run returns only the jobs published since the last one.

Upwork limits how fast its data can be read, so the Actor paces itself: about one job with client details per second on a small run, and several connections side by side on a large one.

#### What data does it extract?

- **Job:** `jobId`, `url`, `title`, `description`, `jobType` (hourly or fixed), `experienceLevel`, `skills[]`, `category`, `categoryGroup`
- **Money:** `hourlyRateMin`, `hourlyRateMax`, `fixedBudget`, `currency`
- **Scope:** `duration`, `durationWeeks`, `workload`, `projectType`
- **Timing:** `createdAt`, `publishedAt`
- **Activity:** `proposals`, `interviewing`, `invitesSent`, `hired`, `positions`, `lastClientActivity`
- **Client:** `clientCountry`, `clientCity`, `clientTimezone`, `clientPaymentVerified`, `clientTotalSpent`, `clientHires`, `clientActiveHires`, `clientJobsWithHires`, `clientHoursBilled`, `clientRating`, `clientReviews`, `clientMemberSince`, `clientIndustry`, `clientCompanySize`
- **Requirements:** `requirements` (regions, countries, minimum job success score, English level and similar conditions the client set)

`clientDetailsLoaded` tells you whether the client block was read. Upwork hides the details of some jobs from visitors; those rows keep their search data.

### ⬇️ Input

| Field | Type | What it does |
|---|---|---|
| `queries` | array | Keywords or full Upwork job-search URLs. Each entry is searched separately |
| `maxJobsPerQuery` | integer | Maximum jobs to save per search. 50 when left empty |
| `postedWithinHours` | integer | Only jobs published in the last N hours |
| `jobType` | string | `any`, `hourly` or `fixed` |
| `experienceLevels` | array | Any of `entry`, `intermediate`, `expert` |
| `clientCountries` | array | Only clients from these countries, for example "United States" |
| `sortBy` | string | `newest`, `relevance`, `clientSpend` or `clientRating` |
| `includeClientDetails` | boolean | Add the client and activity fields. On by default |

The filters apply to keywords. A pasted Upwork URL keeps the filters it contains (job type, experience level, budget, hourly rate, project length, hours per week, client hires, client location, category and sort order).

#### Example input

Expert-level fixed-price Shopify jobs from US clients, published in the last 24 hours:

```json
{
  "queries": ["shopify"],
  "maxJobsPerQuery": 100,
  "postedWithinHours": 24,
  "jobType": "fixed",
  "experienceLevels": ["expert"],
  "clientCountries": ["United States"]
}
```

### ⬆️ Output

One row per job. This is a real row with the description shortened:

```json
{
  "jobId": "2106035970179278880",
  "url": "https://www.upwork.com/jobs/~022106035970179278880",
  "title": "Low-code Software Engineer",
  "description": "*Job Title:* Freelance Software Engineer (Low Code/No Code Expertise) *Commitment:* 20–30 hours per week ...",
  "jobType": "hourly",
  "experienceLevel": "Intermediate",
  "hourlyRateMin": 15,
  "hourlyRateMax": 40,
  "currency": "USD",
  "duration": "More than 6 months",
  "durationWeeks": 52,
  "workload": "More than 30 hrs/week",
  "projectType": "Ongoing project",
  "skills": ["JavaScript", "Low Code & RAD Software", "Python", "C++", "Desktop Application", "API", "Java", "Web Development"],
  "category": "Web Development",
  "categoryGroup": "Web, Mobile & Software Dev",
  "createdAt": "2026-10-02T14:57:58.679Z",
  "publishedAt": "2026-10-02T15:01:57.886Z",
  "proposals": 48,
  "interviewing": 0,
  "invitesSent": 0,
  "hired": 0,
  "positions": 1,
  "lastClientActivity": "2026-10-02T14:57:58.559Z",
  "requirements": {"regions": ["Europe", "Asia"], "minJobSuccessScore": 90, "prefEnglishSkill": "CONVERSATIONAL", "risingTalent": true},
  "clientCountry": "United Kingdom",
  "clientCity": "London",
  "clientTimezone": "Europe/London (UTC+01:00)",
  "clientPaymentVerified": true,
  "clientTotalSpent": 232417.3,
  "clientHires": 90,
  "clientActiveHires": 30,
  "clientJobsWithHires": 81,
  "clientHoursBilled": 15581.5,
  "clientRating": 4.7,
  "clientReviews": 46,
  "clientMemberSince": "2014-02-17",
  "clientIndustry": "Health & Fitness",
  "clientCompanySize": 2,
  "query": "python developer",
  "clientDetailsLoaded": true,
  "scrapedAt": "2026-10-02T15:31:42+00:00"
}
```

A fixed-price job has `"jobType": "fixed"` and `"fixedBudget": 500` instead of the hourly range.

### 💰 How much does it cost?

You pay per job saved, plus a tiny start fee per run. Client details are included in the price.

| Apify plan | Price per 1,000 jobs |
|---|---|
| Free | $3.00 |
| Starter | $2.85 |
| Scale | $2.70 |
| Business and above | $2.55 |

An hourly alert that finds 10 new jobs per run costs about 72 cents a day on the Free plan. Set **Maximum cost per run** in the run options and the Actor stops saving at that amount.

### 💡 Use cases

- 🔔 **Job alerts for freelancers and agencies:** schedule hourly runs with a time limit and push new jobs to Slack, Telegram or email before the proposal count climbs.
- 🎯 **Lead qualification:** keep jobs from payment-verified clients with real spend and a good rating, and skip the rest.
- 📈 **Market research:** track budgets, hourly ranges and demand for a skill over weeks.
- 🧲 **Outbound prospecting:** find companies that are hiring for a service you sell, by country and industry.

### ❓ FAQ

**Do I need an Upwork account or cookies?** No. The Actor reads the public job board as a visitor.

**How many jobs can one search return?** Upwork shows at most 5,000 jobs per search, and fewer for narrow keywords. Use several keywords or filters to cover more.

**Why is `clientDetailsLoaded` false on some rows?** Upwork hides the details of some jobs from visitors, usually jobs limited to invited or local freelancers. Those rows still have the title, description, budget and skills.

**Why are some client fields empty?** New clients have no hires, rating or spend yet, so Upwork has nothing to show. `clientPaymentVerified` and `clientCountry` are filled for almost every visible job.

**How do I get only new jobs?** Set `postedWithinHours` to the interval of your schedule, for example 1 with an hourly schedule. The run reads newest first and stops when it reaches older jobs.

**Can I use my own Upwork search?** Yes. Apply any filters on upwork.com, copy the address bar and paste it as a search. The URL's own filters are used.

**How fast is it?** Search alone returns 50 jobs per request. With client details the Actor reads about one job per second per connection and opens up to four connections on large runs. In our tests 20 jobs with client details took 25 seconds and 40 took 32 seconds.

**Does it return the client's name or contact details?** No. Upwork does not show them to visitors, and the Actor does not log in.

**Is it legal to scrape Upwork jobs?** The Actor collects job posts that Upwork shows publicly to any visitor and does not log in. You are responsible for how you use the data. Job descriptions can contain personal data, so GDPR, CCPA and similar rules apply to storage and reuse, and Upwork's terms of use apply to you as well. If in doubt, ask a lawyer.

**Something is missing or broken?** Open an issue on the Actor's Issues tab with the run ID. Issues are answered within one business day.

### 🧩 Need this as a managed feed?

If you would rather receive a daily file of qualified jobs for your niche than run the Actor yourself, request a quote at <https://benthepythondev00.github.io/custom.html>.

### 🔗 You might also like

- [Remote Jobs Aggregator](https://apify.com/benthepythondev/remote-jobs-aggregator)
- [Reddit Search Scraper](https://apify.com/benthepythondev/reddit-search-scraper)
- [Google Maps Email Scraper](https://apify.com/benthepythondev/google-maps-email-scraper)
- [Website Contact Extractor](https://apify.com/benthepythondev/website-contact-extractor)
- [Tech Stack Detector](https://apify.com/benthepythondev/tech-stack-detector)

**Keywords:** upwork scraper, upwork jobs scraper, upwork job search api, upwork job alerts, upwork rss alternative, freelance jobs scraper, upwork client data, upwork leads, upwork jobs to slack, freelance job monitoring, upwork api alternative, upwork jobs csv, remote freelance jobs data, upwork proposals count, upwork budget data, freelance market research

# Actor input Schema

## `queries` (type: `array`):

Keywords ("python developer", "shopify") or full Upwork job-search URLs. Each entry is searched separately. A pasted URL keeps the filters it contains.

## `maxJobsPerQuery` (type: `integer`):

Maximum number of jobs to save for each search. Each job is one charged result. Upwork shows at most 5,000 jobs per search. Leave empty for 50.

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

Only jobs published in the last N hours, newest first. Use 24 with a daily schedule, or 1 with an hourly one.

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

Hourly or fixed-price jobs.

## `experienceLevels` (type: `array`):

Leave empty for every level.

## `clientCountries` (type: `array`):

Only jobs from clients in these countries, written as on Upwork ("United States", "United Kingdom", "Germany").

## `sortBy` (type: `string`):

Order of the results. "Posted within" always uses the newest order.

## `includeClientDetails` (type: `boolean`):

Add what Upwork shows about the client and the job's activity: country, city, total spent, hires, rating, payment verification, proposals and interviews. Takes about one second more per job.

## Actor input object example

```json
{
  "queries": [
    "python developer"
  ],
  "maxJobsPerQuery": 20,
  "jobType": "any",
  "sortBy": "newest",
  "includeClientDetails": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "queries": [
        "python developer"
    ],
    "maxJobsPerQuery": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("benthepythondev/upwork-jobs-scraper").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 = {
    "queries": ["python developer"],
    "maxJobsPerQuery": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("benthepythondev/upwork-jobs-scraper").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 '{
  "queries": [
    "python developer"
  ],
  "maxJobsPerQuery": 20
}' |
apify call benthepythondev/upwork-jobs-scraper --silent --output-dataset

```

## MCP server setup

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

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/xa6Mta2IWpH8pwMEi/builds/s2kGpBQgkKTS4CpGm/openapi.json
