# Upwork Job Scraper: Scheduled Job Alerts, Qualified Clients (`hyperbach/upwork-scraper-ai`) Actor

Scheduled job alerts: new Upwork jobs every few minutes from payment-verified clients with real spend. No login. Qualify by client spend, hire rate, budget. Empty runs free. Start from any time; never miss a job. Slack, email or webhook via Apify integrations. $2 per 1,000, no monthly fee.

- **URL**: https://apify.com/hyperbach/upwork-scraper-ai.md
- **Developed by:** [Hyperbach](https://apify.com/hyperbach) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 163 total users, 72 monthly users, 99.9% runs succeeded, 3 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.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.
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?

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

## Upwork Job Scraper: Scheduled Job Alerts, Qualified Clients

> **Scheduled job alerts from Upwork: run this Actor every few minutes and get the new postings from payment-verified clients with real spend — into Slack, email or a webhook through Apify's own integrations.** It answers from an index that is already current, so a call takes seconds and an empty check costs nothing. Qualify by client spend, hire rate and budget; start the feed from any moment you choose and never miss a job. **$2 per 1,000 results — no monthly fee, no login.**

**Built for freelancers, agencies, and job-board builders** who need to see a posting before everyone else has replied to it. Most scrapers go to Upwork at request time and hand you raw rows. This one is already watching.

### What is measured, not claimed

- **Freshness.** 98.7% of postings are available within 10 minutes of going live, median under 4 minutes — measured on the live feed, not estimated.
- **A start point you own.** `created_at` is a value you pass, not state we keep. Among the published input schemas of the largest Upwork scrapers on the Store (checked 2026-09-06), none exposes one: their time filters are posting time, and their "new jobs only" modes are since-last-run state held on their side. Yours can be replayed, shared between machines, or resumed after downtime — and a job that Upwork lists late is never skipped.
- **The filters buyers actually use come first.** Payment-verified, total spent, client and job scores, hire rate, open jobs, invites — with comparison and range operators: `">=1000"`, `"500-2000"`. 57 filters in total; the ones nobody uses sit at the bottom of the form.
- **Screening questions captured per job**, ready for your proposal generator. AI-extracted skills, summaries and urgency for those who want them.
- **$2 per 1,000 results, less on paid plans, no monthly fee.** Zero-result runs cost nothing, so a tight polling schedule costs no more than a lazy one.

### 📈 Who it's for

- **Freelancers** — poll every few minutes and see matching jobs while they are still fresh, with screening questions ready to drop into your proposal.
- **Agencies** — filter straight to high-budget, agency-friendly clients with real spend and hire history.
- **Job boards & SaaS** — power a niche Upwork job board or alerting product on a feed that stays current without you running scrapers.
- **Market researchers** — track pricing, demand, and skill trends across millions of postings.

### 🚀 Quick Start

#### 1. Basic Usage

```javascript
{
  "limit": 10
}
```

#### 2. With Smart Filtering

```javascript
{
  "limit": 20,
  "skills": "React",
  "price_min": ">=50",
  "buyer_score": ">=4.0",
  "ai_urgency": "Urgent"
}
```

#### 3. Notification Mode (Only New Jobs)

```javascript
{
  "notifications_only": "true",
  "limit": 50
}
```

#### 4. Start the feed from a moment you choose

```javascript
{
  "created_at": ">=2026-09-01T00:00:00Z",
  "buyer_payment_verified": true,
  "total_spent": ">=1000",
  "limit": 100
}
```

`created_at` is when a job entered this index, not when Upwork posted it. Advance it to the newest value you received and call again: you get exactly the jobs added since, including the ones Upwork listed late. The start point is yours to keep, replay, or share between machines — nothing about your position is stored on our side.

### 🔔 Job alerts in five minutes

The Actor returns rows; Apify's own integrations deliver them. Nothing to host, nothing to code.

1. **Create a Task** from this Actor with your filters — for example `buyer_payment_verified: true`, `total_spent: ">=1000"`, `keywords: "react"` — and keep `notifications_only: true`, so each run returns only the postings that arrived since the previous one.
2. **Schedule it** — Task → Schedule. An empty run costs nothing, so the schedule can be as tight as you like; see the note below on how tight before the alerts get noisy.
3. **Add an integration** — Task → Integrations:
   - **Slack** posts one message per run, with a link to that run's rows.
   - **Gmail** sends one email per run with the rows attached (CSV, JSON, Excel).
   - **Webhook** calls your URL with the run and dataset ids, for anything else.

Ready-made: the published task **[Slack alerts for new Upwork jobs from qualified clients](https://apify.com/hyperbach/upwork-scraper-ai/examples/slack-alerts-for-new-qualified-upwork-jobs)** carries these filters — copy it, schedule it, connect Slack.

**How tight a schedule:** Slack and Gmail fire on every successful run, including a run that found nothing new. Every 15–60 minutes keeps that quiet. For polling every minute, send a **webhook** to Make, n8n or Zapier and filter on the run's item count, so only runs with new jobs reach you.

Why the alerts are complete: `notifications_only` keeps a per-account start point on our side; `created_at` lets you hold your own instead. Either way a job that reaches Upwork's search late still arrives on your next run, because the feed is ordered by when a job entered our index, not by when Upwork stamped it.

### 📤 Output Format

Each dataset item is a flat job record. Real example from a fresh posting (`job_score: 94`):

```json
{
  "id": "022053503680858460814",
  "title": "Senior React Native / Cloud Backend Engineer for iOS Video App",
  "url": "https://www.upwork.com/jobs/~022053503680858460814",
  "description": "We are looking for a senior engineer to join a small, fast-moving team building a live iOS app in React Native with heavy cloud backend connectivity and cloud-based video processing. This is not a web app project. We need someone experienced with real iOS apps on real devices, App Store/TestFlight workflows, mobile-to-cloud video uploads, client/server sync, and production-safe feature deployment...",
  "date_posted": "2026-05-10T15:53:24Z",
  "created_at": "2026-05-10T15:59:01Z",
  "price_type": "Hourly",
  "price_min": 70,
  "price_max": 100,
  "skills": "React Native, iOS, Mobile App Development, Google Cloud Platform, Python, AI Development",
  "category_name": "Web, Mobile & Software Dev",
  "subcategory_name": "Web Development",
  "experience_level": "Expert",
  "engagement_label": "3 to 6 months",
  "qual_pref_english": "ANY",
  "client_location": "United States",
  "buyer_city": "Miami",
  "buyer_score": 4.93,
  "buyer_feedback_count": 44,
  "buyer_payment_verified": true,
  "jobs_posted": 57,
  "hires": 55,
  "hire_rate": 100,
  "total_spent": 54226.41,
  "avg_hourly_rate": 55.35,
  "total_hours": 473,
  "open_jobs": 1,
  "ai_clients_technical_understanding": "High",
  "ai_technical_skills": "React Native, iOS app development, App Store/TestFlight workflows, mobile video/photo upload, camera roll/photo library permissions, cloud backend (GCP preferred, AWS acceptable), async jobs/queues/workers, client/server state sync, production logging/debugging, AI-assisted development",
  "ai_inferred_technical_skills": "Slack, Markdown documentation, real device testing",
  "ai_explicit_mention_of_agency": "No Mention",
  "job_score": 94,
  "job_score_breakdown": {
    "categories": {
      "price":             { "raw_score": 18, "normalized_score": 18, "weight": 20 },
      "client_reputation": { "raw_score": 42, "normalized_score": 42, "weight": 42 },
      "client_spending":   { "raw_score": 24, "normalized_score": 24, "weight": 40 },
      "location":          { "raw_score": 10, "normalized_score": 10, "weight": 10 }
    },
    "top_contributors": [
      { "attribute": "price_min,price_max",    "value": "$70-$100/hr",       "score": 18, "category": "price" },
      { "attribute": "hire_rate",              "value": "100% (57 jobs)",    "score": 15, "category": "client_reputation" },
      { "attribute": "buyer_score",            "value": "4.93 (44 reviews)", "score": 15, "category": "client_reputation" },
      { "attribute": "avg_hourly_rate",        "value": "$55.35/hr",         "score": 15, "category": "client_spending" },
      { "attribute": "buyer_payment_verified", "value": true,                "score": 12, "category": "client_reputation" },
      { "attribute": "client_location",        "value": "United States",     "score": 10, "category": "location" },
      { "attribute": "total_spent",            "value": "$54,226.41",        "score": 6,  "category": "client_spending" },
      { "attribute": "total_hours",            "value": "473 hours",         "score": 3,  "category": "client_spending" }
    ],
    "penalties": []
  },
  "questions": [
    "Describe a React Native iOS app you worked on that was live in TestFlight or the App Store. What parts of the client/server flow did you personally build or maintain, especially around photo/video uploads, auth, sync, or backend job status?",
    "Do you personally have an iPhone with a large real photo/video library that you can use for testing? What model and how many photos/videos in your camera roll?",
    "How are you currently using AI as a developer?",
    "How do you prevent regression?"
  ]
}
```

`job_score` is the rule-based 0–100 rank. Category weights: price (20) + client\_reputation (42) + client\_spending (40) + premium (5) + location (10). Each category is capped at its weight, then summed and bounded to 0–100.

`job_score_breakdown` is always included and explains which fields contributed how many points. Use it to build UI that highlights why a job ranked high, or to debug your score-threshold filters. `top_contributors` is sorted descending; `penalties` lists negative contributions (e.g., unverified payment, low hire rate on a client with many jobs posted).

`questions` is the array of Upwork screening questions captured for the job — empty `[]` for ~83% of postings, 1–6 strings when the client included them. Feed it into your auto-proposal generator.

Empty/missing AI fields are returned as `null` (not every job goes through full AI enrichment).

For the full list of fields with types and operator support, see [Complete Field Reference](#-complete-field-reference) above.

### 📸 One snapshot per job

Every job here is captured once, when it first enters the index — typically within minutes of going live (median under 4) — and the record never changes after that. There is no re-scrape: a job that later collects 40 proposals, or closes, keeps the record it had in its first minutes.

Everything a client writes into a posting is exact as of that moment: title, description, budget, skills, screening questions. The client's history (total spent, hire rate, feedback score, jobs posted) is what the job page showed at posting time, the same numbers you would have weighed before bidding.

`client_invites_sent` is also an at-capture value: clients usually send invites while creating the job, so it tells you whether a posting started invite-first (`>=1`) or open to everyone (`0`). It does not track invites the client sent later. Counters that only mean something live — proposals received, interviews, whether the job is still open — are absent from both the output and the filters: a first-minutes snapshot would pin them near zero forever, and serving that as data is worse than not serving it.

If you need the current state of a job days after posting (has it been filled? at how many proposals?), use **Refresh mode** below — `refresh_job_ids` fetches those jobs live, the one deliberate exception to the snapshot design. The index itself is built for the opposite moment: catching postings minutes after they appear, before the crowd arrives.

### 🔄 Refresh mode — live "get job by id"

The feed serves an index by design; refresh mode is the deliberate exception. Pass `refresh_job_ids` (up to 50 — job URLs, `~02…` ciphers, or numeric ids) and the run fetches those jobs **live from Upwork, right now**, bypassing the index entirely:

```json
{ "refresh_job_ids": ["https://www.upwork.com/jobs/~021234567890123456789", "021987654321098765432"] }
```

Each returned item is the job's current state, verbatim: title/description/budget as they read now, the **live activity counters** (`clientActivity` — applicants, interviews, hires, invites, last buyer activity), and the client's **recent contract history** (`workHistory` + `workInProgress`, up to ~50 contracts with both-way review texts and real paid amounts). This is the data that changes after posting — exactly what the snapshot index deliberately does not carry.

Rules: refresh runs cannot be combined with search filters; each refreshed job bills at the normal result rate; ids that no longer resolve (deleted or private jobs) cost nothing and are listed in the run `OUTPUT` under `refreshSummary.not_found`. A run of 50 ids takes roughly a minute.

### 💰 Pricing

**$2.00 per 1,000 results.** No monthly subscription. No platform usage fees. Runs that return zero results cost nothing.

Paid Apify plans pay less, automatically: **$1.70** per 1,000 on Starter, **$1.40** on Scale, **$1.00** on Business.

***

### 📖 Reference — Fields, Filters & Syntax

Everything below is the complete manual. Skim it when you're ready to build precise queries.

### ⚙️ Input Configuration

*All parameters are optional.*

| Parameter | Type | Description |
|-----------|------|-------------|
| `limit` | number | Number of jobs to return (1-100, default: 10) |
| `notifications_only` | boolean | If true, only returns NEW jobs since last call |

### ⚠️ Filter Quality — Read This Before Filtering

Upwork only **requires** title, description, skills, and price-type from the
client. Almost everything else is optional, and clients fill it in
inconsistently. **If you filter on a field that's empty for most jobs, you
exclude all the matching jobs that simply didn't supply the value** — not
just the non-matching ones.

Field density measured on the last 30 days (~135K postings):

- **Always-filled (safe to filter):** `title`, `description`, `skills`,
  `price_type`, `category_name`, `subcategory_name`, `experience_level`,
  `qual_pref_english`, `qual_type`, `client_location`, `hire_rate`,
  `company_size`, `open_jobs` (all ≥99%)
- **Mostly filled (mild loss):** `buyer_contract_date` (~82%),
  `buyer_payment_verified` (~78%), `ai_explicit_mention_of_agency` (~73%)
- **Half-filled (use carefully):** `price` / `price_min` / `price_max`
  (~40% — fixed-price jobs leave hourly bounds null and vice versa),
  `buyer_score`, `buyer_feedback_count`, `total_spent`, `industry`,
  `engagement_label`, `ai_technical_skills` (30-70%)
- **Sparse (filter at your own risk):** `ai_urgency` (~14%),
  `ai_duration` (~27%), `ai_deadline` (~12%), `qual_portfolio_required` (~7%),
  `qual_rising_talent` (~12%) — most jobs are *not* tagged

**Recommendation:** filter on dense fields, then use sparse fields for
post-fetch sorting / scoring / display. Combining sparse filters compounds
the loss multiplicatively.

### 📊 Available Filter Fields

You can filter jobs by **57 different fields** across these categories:

#### 📋 Essential Job Information

Core job details like title, description, pricing

**Available fields:** `title`, `description`, `skills`, `price_type`, `price`, `price_min`, `price_max`, `category_name`, `subcategory_name`, `date_posted`, `url`, `keywords`, `exclude_keywords`

#### 📋 Job Requirements

Experience level, qualifications, and constraints

**Available fields:** `experience_level`, `engagement_label`, `engagement_weeks`, `qual_min_hours_week`, `qual_min_success_score`, `qual_pref_english`, `qual_rising_talent`, `qual_portfolio_required`, `qual_type`

#### 📋 Client Information

Details about the client posting the job

**Available fields:** `client_location`, `jobs_posted`, `hire_rate`, `avg_hourly_rate`, `open_jobs`, `total_spent`, `hires`, `active_hires`, `total_hours`, `industry`, `company_size`, `buyer_city`, `buyer_feedback_count`, `buyer_score`, `buyer_contract_date`, `buyer_payment_verified`

#### 📋 AI-Powered Insights

AI-generated analysis of job requirements and urgency

**Available fields:** `ai_urgency`, `ai_duration`, `ai_deadline`, `ai_technical_skills`, `ai_inferred_technical_skills`, `ai_explicit_mention_of_agency`, `ai_clients_technical_understanding`

#### 📋 System Metadata

Internal tracking and system fields

**Available fields:** `id`, `created_at`, `questions`, `job_score`, `job_score_breakdown`

#### 🔥 Most Popular Filters

Measured on the live feed: share of requests that carry each filter.

| Field | Type | Example | Used in | Description |
|-------|------|---------|---------|-------------|
| `buyer_payment_verified` | boolean | true | 78% | Boolean flag indicating whether the client has verified their payment method on Upwork |
| `total_spent` | numeric | ">=1000" | 70% | Total amount the client has spent on Upwork across all their projects |
| `keywords` | text | "chatbot" | 66% | Full-text search across job title, description, skills, categories, location, industry, and AI-generated fields |
| `created_at` | date | ">=2026-09-01T00:00:00Z" | 48% | When this job entered our index — a start point you own |
| `date_posted` | date | ">=2026-09-01" | 40% | When the job was posted on Upwork |
| `category_name` | select | "Web, Mobile & Software Dev" | 32% | Main category of the job (e.g., Web Development, Data Science, Design) |
| `exclude_keywords` | text | "wordpress shopify" | 22% | Exclude jobs containing any of these keywords |
| `buyer_score` | numeric | ">=3" | 22% | Score of the client's performance on Upwork |
| `job_score` | numeric | ">=40" | 12% | Rule-based 0–100 score that ranks the job by attractiveness |
| `subcategory_name` | select | "AI Apps & Integration" | 5% | Specific subcategory within the main category (e.g., Front-End Development, Machine Learning) |

> **Tip — filter server-side, not after the fact:** every filter below is applied before `limit` picks the page, so narrowing costs you nothing and rows you would have discarded never enter your dataset. Comparison and range syntax works on any numeric field — `"total_spent": ">=10000"`, `"price": "500-2000"` — and `date_posted` is publication time, so `">=10 minutes ago"` means what it says.

### 🎛️ Filter Syntax Guide

Different field types support different filtering options:

#### **📝 Text Fields** (`skills`, `title`, `description`)

- **Contains search**: `"React"` → finds jobs mentioning React
- **Case-insensitive**: automatically handled
- **Empty check**: `"=null"` → finds jobs with empty/null values
- **Non-empty check**: `"!=null"` → finds jobs with content

#### **🔢 Numeric Fields** (`price`, `price_min`, `buyer_score`)

- **Exact match**: `"1000"` → exactly 1000
- **Operators**: `">=50"`, `"<=100"`, `">25"`, `"<75"`
- **Ranges**: `"500-2000"` → between 500 and 2000 (inclusive)

#### **✅ Boolean Fields** (`buyer_payment_verified`, `qual_rising_talent`, `qual_portfolio_required`)

- **True**: `"true"` or `"1"`
- **False**: `"false"` or `"0"`

#### **📅 Date Fields** (`date_posted`, `created_at`)

- **Relative age (easiest)**: `"7d"` = last 7 days, `"24h"` = last day, `"2w"` = last two weeks. A bare number means days: `"3"` = last 3 days.
- **Exact date**: `"2025-01-15"`
- **Operators**: `">=2025-01-01"`, `"<=2025-12-31"` — also work on relative ages (`"<=7d"` = older than a week)
- **ISO format**: `"2025-01-15T10:30:00Z"`

#### **🎯 Select Fields** (`experience_level`, `ai_urgency`)

- **Exact match**: `"Intermediate"`, `"Urgent"`
- **Case-sensitive**: use exact values from field options (e.g. `"Urgent"`, not `"urgent"` or `"high"`)

#### 📋 Complete Field Reference

##### Essential Job Information

| Field | Type | Operators | Examples | Description |
|-------|------|-----------|----------|-------------|
| `title` | text | contains, =null, !=null | `Build a React Dashboard with Real-time Analytics`, `Virtual Assistant for Email Management`, `Logo Design for Tech Startup` | The job posting title as written by the client. Contains the main description of what work needs to be done. |
| `description` | text | contains, =null, !=null | `We need an experienced React developer to build a ...`, `Looking for a Python expert who can integrate machine learning models ...` | Full job description text as written by the client. Contains detailed requirements, expectations, and project scope. |
| `skills` | text | contains, =null, !=null | `JavaScript, React, Node.js`, `Python, Machine Learning, TensorFlow` | Comma-separated list of required skills and technologies for the job as specified by the client. |
| `price_type` | select | equals | `Fixed-price`, `Hourly` | How the job is priced: Fixed Price (one-time payment) or Hourly (paid per hour worked). |
| `price` | numeric | >=, <=, >, <, =, ranges | `500`, `1200`, `50` | The budget amount for the fixed-price job. |
| `price_min` | numeric | >=, <=, >, <, =, ranges | `25`, `50`, `100` | Lower end of the job's hourly rate band (Upwork jobs advertise a range like $10-35/hr). Hourly jobs only - fixed-price jobs carry their budget in `price` and leave this empty. |
| `price_max` | numeric | >=, <=, >, <, =, ranges | `75`, `150`, `500` | Upper end of the job's hourly rate band (Upwork jobs advertise a range like $10-35/hr). Hourly jobs only - fixed-price jobs carry their budget in `price` and leave this empty. |
| `category_name` | select | equals | `Accounting & Consulting`, `Admin Support`, `Customer Service`, `Data Science & Analytics`, `Design & Creative`, +7 more | Main category of the job (e.g., Web Development, Data Science, Design). Upwork's primary job classification. |
| `subcategory_name` | select | equals | `3D Modeling & CAD`, `AI & Machine Learning`, `AI Apps & Integration`, `Accounting & Bookkeeping`, `Art & Illustration`, +45 more | Specific subcategory within the main category (e.g., Front-End Development, Machine Learning). More granular classification. |
| `date_posted` | date | >=, <=, >, <, =, equals | `7d`, `24h`, `>=2026-05-01`, `2026-05-17` | When the job was posted on Upwork. Use this to find recent opportunities or analyze posting patterns. |
| `url` | text | contains, =null, !=null | `https://www.upwork.com/jobs/~01234567890abcdef`, `https://www.upwork.com/jobs/~987654321fedcba09` | Direct link to the job posting on Upwork. Use this to view the full job details or apply. |
| `keywords` | text | contains | `python machine learning`, `react javascript typescript`, `design ui ux figma`, `marketing seo content`, `data analysis sql python` | Full-text search across job title, description, skills, categories, location, industry, and AI-generated fields. Uses PostgreSQL full-text search for fast performance on millions of jobs. |
| `exclude_keywords` | text | contains | `wordpress php`, `data entry copy paste`, `logo design graphic`, `social media marketing`, `excel spreadsheet manual` | Exclude jobs containing any of these keywords. Uses the same full-text search across job title, description, skills, categories, location, industry, and AI-generated fields. Perfect for filtering out unwanted job types or technologies. |

##### Job Requirements

| Field | Type | Operators | Examples | Description |
|-------|------|-----------|----------|-------------|
| `experience_level` | select | equals | `Entry_level`, `Expert`, `Intermediate` | Required experience level for the job. Exact values: Entry\_level, Intermediate, Expert. |
| `engagement_label` | select | equals | `1 to 3 months`, `3 to 6 months`, `Less than 1 month`, `More than 6 months` | Expected duration or type of engagement (e.g., 1 to 3 months, 3 to 6 months, Less than 1 month, Less than 1 week, More than 6 months). |
| `engagement_weeks` | select | equals | `3`, `9`, `18`, `52` | Project duration in weeks. Options: 1, 3, 9, 18, 52 weeks. |
| `qual_min_hours_week` | select | equals | `0`, `10`, `30`, `40` | Minimum hours per week required for hourly jobs. Options: 10, 30, 40. |
| `qual_min_success_score` | select | equals | `0`, `80`, `90` | Minimum Upwork success score required to apply for the job. |
| `qual_pref_english` | select | equals | `ANY`, `CONVERSATIONAL`, `FLUENT`, `NATIVE` | Client's preferred English proficiency level for freelancers. |
| `qual_rising_talent` | boolean | equals | `True`, `False` | Whether the job is open to Upwork Rising Talent (newer freelancers with potential). |
| `qual_portfolio_required` | boolean | equals | `True`, `False` | Whether the client requires a portfolio or work samples to apply. |
| `qual_type` | select | equals | `AGENCY`, `ANY`, `INDEPENDENT` | Type of freelancer the client is looking for: Agency (team/company), Independent (solo freelancer), or Any (no preference). |

##### Client Information

| Field | Type | Operators | Examples | Description |
|-------|------|-----------|----------|-------------|
| `client_location` | text | contains, =null, !=null | `United States`, `United Kingdom`, `Canada` | Geographic location of the client posting the job. |
| `jobs_posted` | numeric | >=, <=, >, <, =, ranges | `1`, `10`, `>50` | Total number of jobs the client has posted on Upwork. |
| `hire_rate` | numeric | >=, <=, >, <, =, ranges | `75`, `90`, `50` | Percentage of freelancers the client has hired from total applications. Higher rates indicate clients who actively hire rather than just browse. |
| `avg_hourly_rate` | numeric | >=, <=, >, <, =, ranges | `45.50`, `75.00`, `>=100` | Average hourly rate this client typically pays freelancers. Based on their historical hiring patterns. |
| `open_jobs` | numeric | >=, <=, >, <, =, ranges | `0`, `2`, `<5` | Number of jobs the client currently has open/active. |
| `total_spent` | numeric | >=, <=, >, <, =, ranges | `100`, `500`, `>=1000000` | Total amount the client has spent on Upwork across all their projects. |
| `hires` | numeric | >=, <=, >, <, =, ranges | `1`, `10`, `>50` | Total number of freelancers hired by the client. |
| `active_hires` | numeric | >=, <=, >, <, =, ranges | `1`, `10`, `>50` | Number of freelancers currently hired by the client. |
| `total_hours` | numeric | >=, <=, >, <, =, ranges | `100`, `500`, `>=1000` | Total number of hours the client has worked on Upwork across all their projects. |
| `industry` | select | equals | \`\`, `Aerospace`, `Agriculture & Forestry`, `Art & Design`, `Automotive`, +15 more | Main industry the client operates in. Select from available choices. |
| `company_size` | select | equals | `0`, `1`, `10`, `100`, `1000`, +4 more | Size of the client's company. Predefined option for company size. Select from available choices. |
| `buyer_city` | text | contains, =null, !=null | `New York`, `London`, `Paris`, `Tokyo`, `Sydney`, `Berlin`, `Rome`, `Madrid`, `Amsterdam`, `Mumbai`, `Beijing`, `Delhi`, `Mumbai` | City where the client is located. |
| `buyer_feedback_count` | numeric | >=, <=, >, <, =, ranges | `1`, `10`, `>=2` | Number of feedbacks the client has received. |
| `buyer_score` | numeric | >=, <=, >, <, =, ranges | `>=4.7`, `5.0` | Score of the client's performance on Upwork. |
| `buyer_contract_date` | date | >=, <=, >, <, =, equals | `>=2024-01-15`, `2024-01-20T14:30:00Z`, `365d` | Date when the client first registered their account on Upwork. Indicates how long the client has been active on the platform. |
| `buyer_payment_verified` | boolean | equals | `True`, `False` | Boolean flag indicating whether the client has verified their payment method on Upwork. |

##### AI-Powered Insights

| Field | Type | Operators | Examples | Description |
|-------|------|-----------|----------|-------------|
| `ai_urgency` | select | equals | `Immediate`, `Long-Term`, `Moderately Urgent`, `Not Urgent`, `Urgent`, `Very Urgent` | AI-detected urgency level of the job based on language and posting patterns. |
| `ai_duration` | select | equals | `Flexible`, `Flexible Deadline`, `Long-Term`, `Mid-Term`, `Part-Time`, `Short-Term` | AI-detected project duration based on job description analysis. Indicates expected length and type of engagement. |
| `ai_deadline` | select | equals | `Fixed Deadline`, `Flexible Deadline`, `Immediate Deadline`, `No Deadline` | AI-detected deadline type for the job based on urgency indicators and time-sensitive language in the job description. |
| `ai_technical_skills` | text | contains, =null, !=null | `JavaScript, React, Node.js`, `Python, Django, PostgreSQL`, `AWS, Docker, Kubernetes` | Technical skills explicitly mentioned in the job description, extracted using AI. These are skills directly stated by the client as requirements or preferences. |
| `ai_inferred_technical_skills` | text | contains, =null, !=null | `Git, REST APIs, Testing`, `Database Design, Security`, `Responsive Design, SEO` | Technical skills inferred by AI from the job description context, even when not explicitly mentioned. These are skills likely needed based on project requirements and industry patterns. |
| `ai_explicit_mention_of_agency` | select | equals | `Agencies Welcome`, `No Agencies`, `No Mention` | AI-detected explicit mention of agency preferences in the job posting. Indicates whether the client welcomes agencies, prefers individual freelancers, or has no specific preference. |
| `ai_clients_technical_understanding` | select | equals | `Expert`, `High`, `Low`, `Moderate` | AI-detected assessment of the client's technical understanding based on how they describe their project requirements. Helps identify whether the client has deep technical knowledge, moderate understanding, basic knowledge, or expert-level expertise in the domain. |

##### System Metadata

| Field | Type | Operators | Examples | Description |
|-------|------|-----------|----------|-------------|
| `id` | text | contains, =null, !=null | `~01234567890abcdef`, `~987654321fedcba09` | Unique identifier for the job posting on Upwork. Use this to track specific jobs or avoid duplicates. |
| `created_at` | date | >=, <=, >, <, =, equals | `2024-01-20T14:30:00Z` | When this job entered our index — a start point you own. Pass >= the newest created\_at you have received and you get exactly the jobs added since, including ones Upwork listed late. Replayable and shareable, unlike a vendor-held 'since last run' state. |
| `questions` | array | (read-only, not a filter input) | `['Name one long-tail keyword you would target for a UK mortgage and investment app and explain why?', 'Share a specific SEO or ASO result you have driven. Include the numbers?']`, `['Are you willing to undergo a background check, in accordance with local law/regulations?', 'How soon can be available for work?', 'What is your level of proficiency in English?', 'Briefly describe your experience with Oracle Fusion HCM']` | Upwork screening questions captured for the job, as written by the client. Empty array `[]` for ~83% of postings (jobs without screening questions). When present, contains 1-6 question strings. Feed straight into a proposal-answer generator to pre-write answers, or surface as an application checklist. |
| `job_score` | numeric | >=, <=, >, <, =, ranges | `>=50`, `>=70`, `60-90` | Rule-based 0–100 score that ranks the job by attractiveness. Weighted across price (20), client reputation (42), client spending (40), premium status (5), and location (10). Because 82 of those 100 points come from client reputation and spending, a threshold mostly selects for good clients rather than good briefs. |
| `job_score_breakdown` | object | (read-only, not a filter input) | - | Always-included explanation of the `job_score`: per-category raw/normalized/weight, ranked `top_contributors`, and `penalties`. Use to surface why a job ranked high in UI, or to debug threshold filters. See Output Format below. |

### 🎯 Smart Usage Patterns

#### 💼 Freelancer Job Alerts

```javascript
// High-value React jobs from quality clients
{
  "notifications_only": "true",
  "skills": "React",
  "price_min": ">=60",
  "buyer_score": ">=4.0",
  "buyer_payment_verified": "true",
  "ai_urgency": "Urgent"
}

// Remote-friendly design jobs
{
  "keywords": "design ui ux figma",
  "price_type": "Fixed-price",
  "price": "500-5000"
}
```

#### 🏢 Agency Lead Generation

```javascript
// Jobs explicitly welcoming agencies or teams
{
  "ai_explicit_mention_of_agency": "Agencies Welcome",
  "price_min": ">=100",
  "total_spent": ">=10000"
}

// Enterprise projects with team requirements
{
  "ai_explicit_mention_of_agency": "Agencies Welcome",
  "engagement_label": "More than 6 months",
  "company_size": "1000",
  "ai_clients_technical_understanding": "High"
}

// High-value development projects for agencies
{
  "ai_explicit_mention_of_agency": "Agencies Welcome",
  "keywords": "react node.js typescript",
  "price": ">=25000",
  "ai_duration": "Long-Term",
  "buyer_score": ">=4.5"
}
```

#### 🔍 Market Research & Analysis

```javascript
// Track AI/ML job trends and pricing
{
  "keywords": "machine learning artificial intelligence",
  "date_posted": ">=2025-01-01",
  "limit": 100
}

// Monitor mobile app development market
{
  "skills": "iOS Android React Native",
  "price_type": "Fixed-price",
  "date_posted": ">=2024-12-01",
  "experience_level": "Intermediate"
}

// Analyze client spending patterns
{
  "total_spent": ">=100000",
  "hire_rate": ">=90",
  "buyer_payment_verified": "true",
  "limit": 50
}
```

#### 🎯 Specialized Niches

```javascript
// Blockchain & crypto projects with quality clients
{
  "keywords": "blockchain cryptocurrency solidity ethereum",
  "exclude_keywords": "scam pyramid scheme",
  "ai_clients_technical_understanding": "High",
  "price_min": ">=50"
}

// Technical writing for clients who understand the work
{
  "keywords": "technical writing documentation",
  "ai_clients_technical_understanding": "Moderate",
  "experience_level": "Intermediate",
  "price_min": ">=30",
  "qual_pref_english": "FLUENT"
}

// Urgent fixes for immediate delivery
{
  "ai_urgency": "Immediate",
  "ai_deadline": "Fixed Deadline",
  "keywords": "bug fix maintenance urgent",
  "ai_duration": "Short-Term",
  "price_min": ">=40",
  "date_posted": ">=2025-01-01"
}
```

#### 🚀 Notification & Automation

```javascript
// Daily new job alerts for Python developers
{
  "notifications_only": "true",
  "skills": "Python",
  "price_min": ">=45",
  "buyer_score": ">=3.5",
  "limit": 20
}

// Weekend side project hunting
{
  "engagement_label": "Less than 1 month",
  "price_type": "Fixed-price",
  "price": "1000-10000"
}
```

### ⚡ Performance

- **Typical run:** ~8 seconds end to end for 100 results. Most of that is Apify container start-up — the query behind it returns in well under a second.
- **Jobs are served from a pre-indexed store, not scraped live per run.** That's why a 100-result run takes seconds rather than minutes, and why filters run against the whole corpus instead of one page of search results. Each job is indexed once and never re-scraped — see "One snapshot per job" above for what that means for the data.

### 🚨 Common Issues & Solutions

#### Issue: "No jobs returned"

- **Solution**: Check your filters aren't too restrictive
- **Tip**: Start with broader filters and narrow down

#### Issue: "Same jobs appearing"

- **Solution**: Use `notifications_only: true` for new jobs only
- **Tip**: Cursor tracking is automatic per user session

#### Issue: "Rate limit exceeded"

- **Solution**: Add delays between requests
- **Tip**: Use higher `limit` values to reduce request frequency

### 🔗 Integration Examples

#### Webhook Integration

```javascript
// Set up webhook to receive new job notifications
const webhook = await apifyClient.webhooks().create({
  eventTypes: ['ACTOR.RUN.SUCCEEDED'],
  requestUrl: 'https://your-app.com/webhook/new-jobs'
});
```

#### Slack Bot Integration

```javascript
// Post new jobs to Slack channel
const jobs = await actor.call({
  notifications_only: 'true',
  skills: 'React',
  price_min: '>=50'
});

jobs.items.forEach(job => {
  slack.postMessage({
    channel: '#job-alerts',
    text: `🚀 New ${job.skills} job: ${job.title} - $${job.price_min}+/hr`
  });
});
```

### 📊 Need historical data?

For research or model training, the live scraper isn't the right tool. We also publish a **historical Upwork jobs dataset** with 1.9M+ records from 2024–2025 in CSV, JSON, Parquet, SQLite, and DuckDB formats — with the same AI-enriched fields. See the [Upwork Jobs Dataset on Gumroad](https://hyperbach.gumroad.com/l/upwork) for details.

### 🆘 Support

Found an issue or have a feature request? Open an issue on the [Issues tab](https://apify.com/hyperbach/upwork-scraper-ai/issues). We respond within ~48 hours.

Contact: apify@hyperbach.com

# Actor input Schema

## `keywords` (type: `string`):

Full-text search across job title, description, skills, categories, location, industry, and AI-generated fields. Uses PostgreSQL full-text search for fast performance on millions of jobs.

## `exclude_keywords` (type: `string`):

Exclude jobs containing any of these keywords. Uses the same full-text search across job title, description, skills, categories, location, industry, and AI-generated fields. Perfect for filtering out unwanted job types or technologies.

## `category_name` (type: `string`):

Main category of the job (e.g., Web Development, Data Science, Design). Upwork's primary job classification.

## `subcategory_name` (type: `string`):

Specific subcategory within the main category (e.g., Front-End Development, Machine Learning). More granular classification.

## `date_posted` (type: `string`):

When the job was posted on Upwork. Use this to find recent opportunities or analyze posting patterns. Simplest form is a relative age: 7d = posted in the last 7 days, 24h = last day, 2w = last two weeks. A bare number means days (3 = last 3 days). Or give an ISO date (YYYY-MM-DD or full ISO datetime) with >= for newer than that date, <= for older.

## `created_at` (type: `string`):

When this job entered our index — a start point you own. Pass >= the newest created\_at you have received and you get exactly the jobs added since, including ones Upwork listed late. Replayable and shareable, unlike a vendor-held 'since last run' state. Use >=2026-09-01T00:00:00Z to start from a moment you choose, then advance it to the newest created\_at in each batch. This is ingestion time, not the Upwork posting date (that is date\_posted).

## `limit` (type: `integer`):

Maximum number of items to return (1-100).

## `notifications_only` (type: `boolean`):

Only return new jobs since last call (requires notification feed mode).

## `buyer_payment_verified` (type: `boolean`):

Boolean flag indicating whether the client has verified their payment method on Upwork.

## `total_spent` (type: `string`):

Total amount the client has spent on Upwork across all their projects. Use numeric operators like >=1000000 for values above threshold, or range syntax like 500000-1000000 for values between 500000-1000000.

## `buyer_score` (type: `string`):

Score of the client's performance on Upwork. Use numeric operators like >=5.0 for values above threshold, or range syntax like 4.7-5.0 for values between 4.7-5.0.

## `job_score` (type: `string`):

Rule-based 0–100 score that ranks the job by attractiveness. Weighted across price (20), client reputation (42), client spending (40), premium status (5), and location (10). Because 82 of those 100 points come from client reputation and spending, a threshold mostly selects for good clients rather than good briefs. Half of all jobs score under 37 and only 9% reach 70, so start at >=50 and raise it. Jobs with no client spending history skip that 40-point block and therefore score low — a high threshold filters out thin postings as well as weak ones.

## `hire_rate` (type: `string`):

Percentage of freelancers the client has hired from total applications. Higher rates indicate clients who actively hire rather than just browse. Use >= for clients who hire frequently (>=80), or <= for selective clients (<=50). Null values indicate new clients.

## `open_jobs` (type: `string`):

Number of jobs the client currently has open/active. Use numeric operators like <=10 for values below threshold, or range syntax like 5-20 for values between 5-20.

## `client_invites_sent` (type: `string`):

Invites the client had sent when the job was captured, minutes after posting. Clients usually send invites while creating the job, so this catches invite-first postings. 0 when the source reported none (NULLs backfilled to 0 on 2026-08-22 — before that, numeric filters silently dropped the unreported rows). "0" finds jobs with no invites yet at capture time (least pre-committed clients); ">=1" finds invite-first postings. Numeric operators and range syntax like 5-20 work as on any numeric field.

## `hires` (type: `string`):

Total number of freelancers hired by the client. Use numeric operators like >=10 for values above threshold, or range syntax like 5-20 for values between 5-20.

## `client_location` (type: `string`):

Geographic location of the client posting the job.

## `avg_hourly_rate` (type: `string`):

Average hourly rate this client typically pays freelancers. Based on their historical hiring patterns. Filter by client's typical budget range. Use >= for higher-paying clients (>=60) or <= for budget-conscious clients.

## `buyer_city` (type: `string`):

City where the client is located.

## `buyer_contract_date` (type: `string`):

Date when the client first registered their account on Upwork. Indicates how long the client has been active on the platform. Use >= for clients registered after a date, <= for before a date. Format: YYYY-MM-DD or ISO datetime. A relative age also works (365d = registered within the last year). Older registration dates indicate more established clients.

## `jobs_posted` (type: `string`):

Total number of jobs the client has posted on Upwork. Use numeric operators like >=10 for values above threshold, or range syntax like 5-20 for values between 5-20.

## `price_type` (type: `string`):

How the job is priced: Fixed Price (one-time payment) or Hourly (paid per hour worked).

## `price` (type: `string`):

The budget amount for the fixed-price job. Use numeric operators like >=1000 for jobs above $1000, or 500-2000 for budgets between $500-2000. Only applies to fixed-price jobs.

## `price_min` (type: `string`):

Lower end of the job's hourly rate band (Upwork jobs advertise a range like $10-35/hr). Hourly jobs only - fixed-price jobs carry their budget in `price` and leave this empty. price\_min and price\_max together describe the rate window you want, and a job matches when its advertised band OVERLAPS that window. price\_min=35 (or >=35) means "can pay $35/hr or more" and returns a job advertised at $10-35/hr. The operator decides which end of the window you are setting, so <=35 here means a ceiling and behaves exactly like price\_max=35 - a bare number is read as a floor, never as an exact match. Use 25-75 for work paying somewhere in $25-75. Jobs stating no rate are matched on the client's historical hourly rate instead; see include\_unspecified\_rate.

## `price_max` (type: `string`):

Upper end of the job's hourly rate band (Upwork jobs advertise a range like $10-35/hr). Hourly jobs only - fixed-price jobs carry their budget in `price` and leave this empty. Sets the ceiling of the rate window you want; a job matches when its advertised band overlaps it. price\_max=50 (or <=50) means "can be done at $50/hr or under". The operator decides which end you are setting, so >=50 here means a floor and behaves exactly like price\_min=50 - a bare number is read as a ceiling, never as an exact match. Combine with price\_min for a window, e.g. price\_min=35 plus price\_max=90. Jobs stating no rate are matched on the client's historical hourly rate instead; see include\_unspecified\_rate.

## `include_unspecified_rate` (type: `string`):

What a rate filter should do with jobs that state no rate - about a third of hourly jobs, plus every fixed-price job (their budget is in `price`). `likely` (default) also returns those jobs when the CLIENT's own historical hourly rate fits your window, which is the best evidence available when the posting itself is silent. `off` returns only jobs with a stated rate that overlaps. `all` returns every rate-unspecified job regardless of evidence.

## `experience_level` (type: `string`):

Required experience level for the job. Exact values: Entry\_level, Intermediate, Expert.

## `title` (type: `string`):

The job posting title as written by the client. Contains the main description of what work needs to be done.

## `description` (type: `string`):

Full job description text as written by the client. Contains detailed requirements, expectations, and project scope.

## `skills` (type: `string`):

Comma-separated list of required skills and technologies for the job as specified by the client.

## `job_is_premium` (type: `boolean`):

Boolean flag indicating whether job is premium applies to this job.

## `qual_rising_talent` (type: `boolean`):

Whether the job is open to Upwork Rising Talent (newer freelancers with potential).

## `qual_portfolio_required` (type: `boolean`):

Whether the client requires a portfolio or work samples to apply.

## `qual_min_hours_week` (type: `string`):

Minimum hours per week required for hourly jobs. Options: 10, 30, 40.

## `ai_urgency` (type: `string`):

AI-detected urgency level of the job based on language and posting patterns.

## `ai_technical_skills` (type: `string`):

Technical skills explicitly mentioned in the job description, extracted using AI. These are skills directly stated by the client as requirements or preferences.

## `ai_explicit_mention_of_agency` (type: `string`):

AI-detected explicit mention of agency preferences in the job posting. Indicates whether the client welcomes agencies, prefers individual freelancers, or has no specific preference.

## `clientId` (type: `string`):

Unique identifier for the client. The service automatically tracks what jobs this client has already seen.

## `fields` (type: `array`):

Optional list of field names to keep in the dataset output. If empty, all fields are returned. Saves storage cost on long runs. Example: id, title, url, job\_score, questions. Always-implicit field `id` is kept whether listed or not.

## `refresh_job_ids` (type: `array`):

LIVE mode: fetch these jobs fresh from Upwork right now instead of serving the pre-indexed store. Accepts up to 50 entries in any form — job URL, ~02… cipher, or the numeric id. Returns each job's CURRENT state: activity counters (applicants, hires, invites) plus the client's recent contract history with both-way review texts. Cannot be combined with search filters. Billed per returned job at the normal result rate; ids that no longer resolve are free and listed in the run OUTPUT.

## `active_hires` (type: `string`):

Number of freelancers currently hired by the client. Use numeric operators like >=10 for values above threshold, or range syntax like 5-20 for values between 5-20.

## `ai_anti_bot_phrase` (type: `string`):

AI-detected anti-bot phrases and instructions used by clients in job descriptions to filter out automated applications and ensure human applicants read the full posting. These phrases typically ask applicants to include specific words, numbers, emojis, or perform certain actions in their proposals to prove they've read the requirements carefully.

## `ai_clients_technical_understanding` (type: `string`):

AI-detected assessment of the client's technical understanding based on how they describe their project requirements. Helps identify whether the client has deep technical knowledge, moderate understanding, basic knowledge, or expert-level expertise in the domain.

## `ai_deadline` (type: `string`):

AI-detected deadline type for the job based on urgency indicators and time-sensitive language in the job description.

## `ai_duration` (type: `string`):

AI-detected project duration based on job description analysis. Indicates expected length and type of engagement.

## `ai_inferred_technical_skills` (type: `string`):

Technical skills inferred by AI from the job description context, even when not explicitly mentioned. These are skills likely needed based on project requirements and industry patterns.

## `ai_named_entities` (type: `string`):

AI-extracted named entities from the job description including company names, technologies, frameworks, tools, locations, and other important entities mentioned by the client. This field helps identify specific brands, platforms, or technologies the client is working with.

## `ai_specific_requirements_before_applying` (type: `string`):

AI-extracted specific requirements that clients mention applicants must fulfill before applying, such as portfolio submissions, work samples, or specific application instructions.

## `buyer_feedback_count` (type: `string`):

Number of feedbacks the client has received. Use numeric operators like >=10 for values above threshold, or range syntax like 5-20 for values between 5-20.

## `buyer_offset_utc` (type: `string`):

UTC offset of the buyer's timezone in milliseconds. Indicates the time difference between the buyer's local time and UTC. Values are in milliseconds. Common offsets: 0 (UTC/GMT), 3600000 (+1 hour, Europe), -28800000 (-8 hours, US West Coast), 28800000 (+8 hours, Asia). Use >= or <= to find clients in specific timezone ranges. Positive values are east of UTC, negative are west.

## `client_positions_to_hire` (type: `string`):

Number of client positions to hire. Use numeric operators like >=10 for values above threshold, or range syntax like 2-10 for values between 2-10.

## `company_size` (type: `string`):

Size of the client's company. Predefined option for company size. Select from available choices.

## `engagement_label` (type: `string`):

Expected duration or type of engagement (e.g., 1 to 3 months, 3 to 6 months, Less than 1 month, Less than 1 week, More than 6 months).

## `engagement_weeks` (type: `string`):

Project duration in weeks. Options: 1, 3, 9, 18, 52 weeks.

## `hourly_budget_type` (type: `string`):

Type of hourly budget. Options: AS\_NEEDED, DEFAULT, FULL\_TIME, MANUAL, NOT\_PROVIDED, NOT\_SURE, PART\_TIME.

## `industry` (type: `string`):

Main industry the client operates in. Select from available choices.

## `qual_min_success_score` (type: `string`):

Minimum Upwork success score required to apply for the job.

## `qual_pref_english` (type: `string`):

Client's preferred English proficiency level for freelancers.

## `qual_type` (type: `string`):

Type of freelancer the client is looking for: Agency (team/company), Independent (solo freelancer), or Any (no preference).

## `total_hours` (type: `string`):

Total number of hours the client has worked on Upwork across all their projects. Use numeric operators like >=100 for values above threshold, or range syntax like 50-200 for values between 50-200.

## Actor input object example

```json
{
  "limit": 50,
  "notifications_only": false,
  "clientId": "default"
}
```

# Actor output Schema

## `newJobs` (type: `string`):

Dataset of new AI-enriched Upwork jobs returned by this run. Cursor-tracked per clientId — only jobs the caller has not seen since their last run.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("hyperbach/upwork-scraper-ai").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("hyperbach/upwork-scraper-ai").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 '{}' |
apify call hyperbach/upwork-scraper-ai --silent --output-dataset

```

## MCP server setup

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

```

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/RDMDCh3d2XZKng8Mz/builds/z37G7MGJjZXuBgDqg/openapi.json
