# Upwork Freelancers Scraper · Rates, Earnings & JSS (`memo23/upwork-freelancers-scraper`) Actor

Find and shortlist Upwork freelancers without logging in. Search any skill, keep your Upwork filters, then filter by rate, JSS, earnings and hours. Get exact earnings, badges, location and skills, plus rating, hire-again rate and client feedback on request. Pay per freelancer.

- **URL**: https://apify.com/memo23/upwork-freelancers-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Lead generation, Jobs
- **Stats:** 3 total users, 3 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 freelancer rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Freelancers Scraper · Rates, Earnings, JSS & Work History

Scrape Upwork freelancer profiles from talent search without logging in. Each row is one freelancer: name, title, hourly rate, exact lifetime earnings, Job Success Score, hours billed, hourly and fixed-price job counts, Top Rated / Top Rated Plus badge, location, skills, portfolio and agency. Turn on profile details to add rating, hire-again rate, member since, last worked on, availability, job categories and the latest contracts with client feedback both ways.

### Why use it

- **Live talent search**: the same results and order Upwork shows today, up to 10,000 freelancers per search.
- **Exact numbers**: lifetime earnings to the cent (unless the freelancer hides them), JSS as a percentage, hours billed.
- **Filters that work**: keep your own Upwork search URL with every filter, or filter by country, rate, JSS, earnings, hours, badge and agency membership here.
- **Profile details on demand**: pay for the profile read only when you want it.
- **No login, no cookies, no Upwork account at risk.**

### Use cases

- Recruiting and talent sourcing: shortlist freelancers by rate, JSS, earnings and hours.
- Agency lead generation: find agency members and Top Rated Plus freelancers in a niche.
- Rate benchmarking: compare hourly rates by skill and country.
- Market research on the freelance economy: earnings and activity by category.

### How it works

1. Each keyword becomes an Upwork talent search (`/nx/search/talent/?q=...`); search URLs are used as they are.
2. The actor reads 10 freelancers per search page from the page's own data and walks pages until it has **Max results**. Pages load over plain HTTP where Upwork allows it, otherwise in a real Firefox browser on a US residential connection.
3. With **Add profile details**, it opens each freelancer's public profile and merges the extra fields.
4. Freelancer profile URLs are read directly.

### Input

| Field | What it does |
|---|---|
| `searchQueries` | Keywords, one search each, e.g. `python developer` |
| `startUrls` | Upwork talent search URLs (filters kept) or profile URLs |
| `profileUrls` | Profile URLs, `~01...` ids or custom profile URLs |
| `maxItems` | Freelancers per search (default 50, up to 10,000) |
| `includeProfileDetails` | Adds rating, hire-again, member since, contracts and feedback |
| `location`, `topRatedPlusOnly` | Sent to Upwork with keyword searches |
| `topRatedOnly`, `minHourlyRate`, `maxHourlyRate`, `minJobSuccessScore`, `minTotalEarnings`, `minTotalHours`, `excludeAgencyMembers` | Filters applied to the results |

```json
{
  "searchQueries": ["python developer"],
  "location": "United States",
  "maxItems": 100,
  "minJobSuccessScore": 90,
  "includeProfileDetails": true
}
```

### Output sample

```json
{
  "freelancerId": "~0157e03059a690281f",
  "profileUrl": "https://www.upwork.com/freelancers/~0157e03059a690281f",
  "name": "Eugene K.",
  "title": "Python Developer",
  "country": "United States",
  "city": "San Diego",
  "hourlyRate": 37,
  "currency": "USD",
  "jobSuccessScore": 100,
  "totalEarnings": null,
  "earningsHidden": true,
  "totalHours": 391,
  "totalHourlyJobs": 40,
  "totalFixedPriceJobs": 56,
  "topRatedStatus": "not_eligible",
  "skills": ["Data Scraping", "Flask", "pandas"],
  "agency": null,
  "searchQuery": "python developer",
  "totalSearchResults": 8529,
  "hasProfileDetails": true,
  "rating": 4.92,
  "totalFeedback": 67,
  "totalJobsWorked": 82,
  "hireAgainPercentage": 85.5,
  "memberSince": "2017-10-11T14:17:24Z",
  "lastWorkedOn": "2025-10-16T00:00:00.000Z",
  "availability": "As Needed - Open to Offers",
  "completedJobsCount": 81,
  "workHistory": [
    { "title": "Scraping for Tattoo Shops", "contractType": "Fixed", "status": "Closed", "clientFeedbackScore": 5, "clientFeedback": "I will hire Eugene again if we need him.", "freelancerFeedbackScore": 5 }
  ]
}
```

### Key output fields

- **Search card** (every row): `freelancerId`, `profileUrl`, `customProfileUrl`, `name`, `title`, `description`, `country`, `state`, `city`, `region`, `timezone`, `hourlyRate`, `jobSuccessScore`, `totalEarnings`, `earningsHidden`, `totalHours`, `totalHourlyJobs`, `totalFixedPriceJobs`, `topRatedStatus`, `isTopRated`, `isTopRatedPlus`, `identityVerified`, `skills`, `portfolio`, `agency`, `portraitUrl`, `searchPosition`.
- **Profile details** (`hasProfileDetails: true`): `rating`, `totalFeedback`, `totalJobsWorked`, `hireAgainPercentage`, `memberSince`, `lastWorkedOn`, `availability`, `contractToHire`, `jobCategories`, `specializedProfiles`, `portfolioProjects`, `completedJobsCount`, `jobsInProgressCount`, `workHistory` (latest 10 completed contracts plus those in progress, with feedback both ways), `testimonials`.

### FAQ

**Do I need an Upwork account?** No. Everything comes from pages Upwork shows to logged-out visitors.

**Why is `totalEarnings` null for some freelancers?** They chose to hide earnings (`earningsHidden: true`).

**Why do rows from `profileUrls` have no earnings or JSS?** Upwork shows those on search cards only, not on logged-out profile pages. Search for the freelancer to get them.

**Why only the latest contracts?** The logged-out profile page carries the newest 10 completed contracts; `completedJobsCount` gives the full count.

**Languages, education, employment?** Upwork does not include them in the logged-out profile page, so they are not in the output.

**Free Apify plan?** The first 3 rows of a run are complete. After that, `totalEarnings` is rounded to its order of magnitude and `workHistory` / `portfolioProjects` are left out, with a `freePlanNote` on the row. Any paid Apify plan gets every field.

**Pricing?** Pay per event: one event per freelancer row, plus one per row with profile details. Error rows (failed or private profiles, empty searches) are free.

### Support

Open an issue on the actor's Issues tab and include the run link.

### Explore more scrapers

- [Upwork Jobs Scraper](https://apify.com/memo23/upwork-jobs-scraper-ppe)

### 🤖 For AI agents & LLM apps

Call the actor with `searchQueries` and `maxItems`; each dataset item is one flat freelancer record with stable `freelancerId`. Use `profileUrls` for a known freelancer. Fields absent on the source are `null`, never guessed.

### ⚠️ Disclaimer

This actor collects only data Upwork shows publicly to logged-out visitors. It is not affiliated with or endorsed by Upwork. You are responsible for using the data in line with Upwork's terms, data-protection law (GDPR, CCPA) and your local regulations; do not use personal data for spam.

### SEO Keywords

upwork scraper, upwork freelancers scraper, upwork talent search scraper, upwork profile scraper, upwork freelancer data, scrape upwork freelancers, upwork hourly rates, upwork job success score, upwork earnings, upwork top rated plus, freelancer lead generation, hire freelancers data, upwork api alternative

# Actor input Schema

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

What you would type into Upwork's talent search, one search per entry, e.g. "python developer" or "shopify designer". Each search returns up to Max results freelancers.

## `startUrls` (type: `array`):

Paste a talent search URL from your browser (https://www.upwork.com/nx/search/talent/?q=...) to keep every filter you set there, or a freelancer profile URL (https://www.upwork.com/freelancers/~01...).

## `profileUrls` (type: `array`):

Specific freelancers to read in full: profile URLs, bare ids (~01...) or custom profile URLs. Billed as a freelancer row plus profile details. Logged-out profile pages do not show lifetime earnings or Job Success Score, so those fields stay empty for these rows; use a search to get them.

## `maxItems` (type: `integer`):

Freelancers to return for each search keyword or search URL. Upwork shows at most 10,000 per search.

## `includeProfileDetails` (type: `boolean`):

Open each freelancer's profile and add rating, total feedback, jobs worked, hire-again rate, member since, last worked on, availability, job categories, specialized profiles, portfolio projects and the latest completed contracts with client feedback both ways. Billed as an extra profile-details event per freelancer. Slower.

## `location` (type: `string`):

Country as Upwork names it, e.g. "United States", "United Kingdom", "Germany". Applied to keyword searches; for search URLs, set the location on Upwork itself.

## `topRatedPlusOnly` (type: `boolean`):

Ask Upwork for Top Rated Plus freelancers only (applied to keyword searches).

## `topRatedOnly` (type: `boolean`):

Keep only freelancers with a Top Rated or Top Rated Plus badge.

## `minHourlyRate` (type: `integer`):

Keep freelancers whose listed hourly rate is at least this.

## `maxHourlyRate` (type: `integer`):

Keep freelancers whose listed hourly rate is at most this.

## `minJobSuccessScore` (type: `integer`):

0 to 100. Freelancers without a shown score are left out when this is set.

## `minTotalEarnings` (type: `integer`):

Freelancers who hide their earnings are left out when this is set.

## `minTotalHours` (type: `integer`):

Total hours worked on Upwork.

## `excludeAgencyMembers` (type: `boolean`):

Leave out freelancers who appear in search under an agency.

## `maxConcurrency` (type: `integer`):

How many Upwork pages to load at once (browser tabs when a browser is needed).

## Actor input object example

```json
{
  "searchQueries": [
    "python developer"
  ],
  "maxItems": 20,
  "includeProfileDetails": false,
  "topRatedPlusOnly": false,
  "topRatedOnly": false,
  "excludeAgencyMembers": false,
  "maxConcurrency": 4
}
```

# Actor output Schema

## `freelancers` (type: `string`):

Freelancers with hourly rate, earnings, Job Success Score, hours, badges, location, skills and optional profile details.

# 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 developer"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/upwork-freelancers-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 = {
    "searchQueries": ["python developer"],
    "maxItems": 20,
}

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/upwork-freelancers-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/eKR7AVmaTr9hm4wWB/builds/npCVPk8CexMCsMb2t/openapi.json
