# 🦅 UpworkHawk — Smart Upwork Job Scraper + AI Job Matcher (`inexhaustible_glass/upwork-hawk`) Actor

Best Upwork scraper with AI features: Match Score (how well job fits YOU), auto proposal drafts, client quality grades (A-F), competition analysis, salary estimator. Cheaper than alternatives. Built for freelancers who want to win.

- **URL**: https://apify.com/inexhaustible\_glass/upwork-hawk.md
- **Developed by:** [Hitman studio](https://apify.com/inexhaustible_glass) (community)
- **Categories:** Jobs, Developer tools
- **Stats:** 4 total users, 3 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## 🦅 UpworkHawk — Upwork Job Scraper + AI Job Matcher

Scrape full Upwork job details from a single link, multiple links, or a
keyword search — then let the built-in AI score how well each job fits
your own skills, grade the client's quality, gauge the competition, and
draft a proposal for you.

### Quick start (simplest way)

Paste **one Upwork job URL** into the `jobUrl` field and run the Actor.
That's it — the full job details land in the Output tab.

```json
{
  "jobUrl": "https://www.upwork.com/jobs/~0123456789abcdef"
}
```

### Features

- **Scrape any Upwork job URL** — title, full description, budget, job
  type, experience level, duration, skills, and proposal count.
- **Extract Upwork client information** — country, city, total spent,
  hires, payment-verified status, member-since date.
- **AI match score against your own skills** — set `userSkills` and every
  job gets scored A–F for how well it fits you, with matched and missing
  skills broken out.
- **Auto-generate an Upwork proposal draft** — a ready-to-edit cover letter
  for every job, using your name and portfolio link.
- **Grade Upwork clients by quality** — an A–F score built from spend,
  hires, and payment verification, plus a plain-English recommendation
  ("Consider skipping — low spend/rating" or similar).
- **Estimate competition and a fair bid range** — proposal-count-based
  difficulty rating with a suggested bid\_min/bid\_max for the job.
- **Search Upwork jobs by keyword** — leave the URL fields empty and set
  `searchQuery` to pull a page of matching jobs instead of one link.
- **Filter results** by job type, experience level, client country, minimum
  client rating, minimum client spend, or payment-verified-only.
- **Unlock logged-in-only client data** — paste your own Upwork session
  cookie to also get the client's star rating, review count, jobs-posted
  count, hire rate, average hourly rate paid, their full recent job history
  (freelancers hired, dates, feedback), and their other open jobs. Upwork
  hides all of this from anonymous visitors by design — without a cookie
  these fields are simply absent, never guessed or faked.

### Use cases

- **Freelancers deciding what to apply to** — paste a batch of job links
  from your Upwork feed and instantly see which ones actually match your
  skills, which clients are worth the risk, and get a proposal draft ready
  to send.
- **Agencies triaging leads for their team** — filter a keyword search down
  to payment-verified, high-spend clients only, so nobody wastes connects
  on a low-quality post.
- **Market research on Upwork job trends** — pull a category's job listings
  to see typical budgets, required skills, and experience-level demand.
- **Automated proposal pipelines** — feed job URLs in from your own
  monitoring tool and get structured JSON back, ready to pipe into a
  spreadsheet, Slack alert, or n8n/Make workflow.

### Input

| Field | What it does |
|---|---|
| `jobUrl` | One Upwork job link — the simplest option |
| `jobUrls` | Multiple Upwork job links at once |
| `searchQuery` | Keyword search instead of specific links |
| `jobType`, `experienceLevel`, `clientCountry`, `minClientRating`, `minClientSpend`, `paymentVerifiedOnly` | Filters |
| `upworkCookie` | Your own Upwork session cookie — unlocks logged-in-only client fields |
| `userSkills`, `userName`, `portfolioUrl` | Powers the AI match score and proposal draft |
| `maxResults` | Cap on jobs returned in search mode |

Full field-by-field descriptions and defaults are in the Actor's Input tab.

### Output example

```json
{
  "title": "Seeking Lead Generation & Email List Building Specialist",
  "budget": "$5.00",
  "job_type": "Fixed-price",
  "skills": ["Lead Generation", "Email List Building", "Web Research"],
  "client_country": "United States",
  "client_total_spent": "$3.5K",
  "client_hires": 197,
  "match_score": 50,
  "match_grade": "C",
  "client_quality_grade": "D",
  "competition_level": "Easy",
  "suggested_bid_min": 50,
  "suggested_bid_max": 300,
  "ai_proposal_draft": "..."
}
```

### How it works

A layered fetch strategy tries the cheapest method first and only falls
back to a real browser when needed: Upwork's own GraphQL search API → a
TLS-impersonating HTTP client → Scrapling's StealthyFetcher (a real
Cloudflare-clearing browser) → a dedicated Camoufox/patchright browser pass.
Every attempt is logged, and a job that genuinely couldn't be reached comes
back as an honest "not found" result — never fabricated data.

### FAQ

**Does this need my Upwork login?**
No — the core features (job content, AI scoring, client basics) work fully
anonymously. Your own cookie is optional and only unlocks the extra fields
Upwork already shows *you* once you're signed in (rating, job history, etc.).

**Will it work on a proxy-blocked or removed job link?**
An old, filled, or removed listing (or one that redirects to a generic
category page instead of 404ing) comes back as a clean "not found" — never
fake data dressed up as a real job.

**Can I run this on a schedule?**
Yes — set up an Apify Schedule pointed at this Actor with your usual input,
same as any other Actor.

**Why do some client fields come back empty?**
Client rating, review count, jobs-posted count, hire rate, avg hourly rate
paid, and job history are hidden by Upwork from anonymous visitors — that's
Upwork's own privacy design, not a bug here. Provide `upworkCookie` to see them.

### Notes

- Uses a residential proxy (recommended, prefilled in the Input tab) —
  Upwork blocks datacenter IPs outright.
- Reads only Upwork's own public job pages and search API. No account
  takeover, no unauthorized access — just what any visitor (or, with your
  own cookie, what you) can already see.

# Actor input Schema

## `jobUrl` (type: `string`):

The simplest way to use this Actor: paste a single Upwork job link (e.g. https://www.upwork.com/jobs/~0123456789abcdef) and click Start. You'll get that job's full details — title, description, budget, skills, and client info. Leave blank only if you're using Multiple URLs or Search Query below instead.

## `jobUrls` (type: `array`):

Paste several Upwork job links at once instead of just one above. Leave empty if you only need the single URL field.

## `searchQuery` (type: `string`):

Instead of a job link, search Upwork by keyword (used only if the URL fields above are empty). Example: python web scraping

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

Only keep jobs of this type.

## `experienceLevel` (type: `string`):

Only keep jobs at this level.

## `clientCountry` (type: `string`):

Only keep jobs whose client is in this country (exact match, e.g. "United States"). Blank = no filter.

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

Skip jobs whose client rating is below this. 0 = no filter. Only checked when a rating was actually found (never assumes one).

## `minClientSpend` (type: `number`):

Skip jobs whose client has spent less than this on Upwork historically. 0 = no filter.

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

Skip jobs whose client hasn't verified a payment method.

## `upworkCookie` (type: `string`):

Your own Upwork session cookie for logged-in scraping. Get it: Chrome → log into upwork.com → F12 → Application → Cookies. Without this, some client fields (rating, job-posting history, other open jobs) are hidden by Upwork to anonymous visitors and simply won't appear — not a bug, that's Upwork's own privacy design.

## `capsolverApiKey` (type: `string`):

Your own CapSolver.com API key (paid, ~$1 per 1000 solves). When set, a job page still stuck on Cloudflare's Turnstile challenge after the built-in stealth browser attempts is sent to CapSolver to actually solve it, instead of returning an honest 'blocked' result. Optional — everything else works without it.

## `userSkills` (type: `array`):

Your skills for AI match scoring. Example: Python, Web Scraping, React. Leave empty to skip AI matching.

## `userName` (type: `string`):

Used in auto-generated proposal drafts.

## `portfolioUrl` (type: `string`):

Your portfolio URL included in proposals.

## `maxResults` (type: `integer`):

Maximum jobs to scrape from search results.

## `minBudget` (type: `number`):

Skip jobs below this budget. 0 = no filter.

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

Skip jobs with more proposals than this. 999 = no filter.

## `minMatchScore` (type: `integer`):

Only show jobs with this score or higher.

## `generateProposals` (type: `boolean`):

Auto-generate cover letters for each job.

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

Fallback proxy used only if the built-in Cloudflare WARP proxy fails to set up (e.g. rate-limited). Recommended: Apify RESIDENTIAL proxy group — Upwork blocks plain datacenter/Apify IPs.

## Actor input object example

```json
{
  "jobType": "",
  "experienceLevel": "",
  "minClientRating": 0,
  "minClientSpend": 0,
  "paymentVerifiedOnly": false,
  "userSkills": [
    "python",
    "web scraping",
    "automation"
  ],
  "userName": "Aman",
  "maxResults": 50,
  "minBudget": 0,
  "maxProposals": 999,
  "minMatchScore": 0,
  "generateProposals": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "userSkills": [
        "python",
        "web scraping",
        "automation"
    ],
    "userName": "Aman",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("inexhaustible_glass/upwork-hawk").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 = {
    "userSkills": [
        "python",
        "web scraping",
        "automation",
    ],
    "userName": "Aman",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("inexhaustible_glass/upwork-hawk").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 '{
  "userSkills": [
    "python",
    "web scraping",
    "automation"
  ],
  "userName": "Aman",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call inexhaustible_glass/upwork-hawk --silent --output-dataset

```

## MCP server setup

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

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/78LRIgr6jVKJIX6WN/builds/lcqvCcxGlHRWq8ZTc/openapi.json
