# PeoplePerHour Scraper — Freelance Jobs & Freelancer Leads (`haketa/peopleperhour-scraper`) Actor

Scrape PeoplePerHour freelance jobs (title, budget, category, proposals, client) and freelancer profiles (name, hourly rate, skills, rating, reviews, country) for lead generation. Paste freelance-jobs or hire-freelancers URLs.

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

## Pricing

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

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

## PeoplePerHour Scraper — Freelance Jobs & Freelancer Leads

> **Extract structured data from PeoplePerHour at scale: freelance job listings (title, budget, currency, category, proposals, client) and freelancer profiles (name, headline, hourly rate, skills, rating, reviews, completed projects, country).** Paste freelance-jobs or hire-freelancers URLs and get clean JSON/CSV/Excel in seconds. Built for recruiters, agencies, market researchers and lead-gen teams.

[![Jobs](https://img.shields.io/badge/Freelance-Jobs-e64c3d)]()
[![Freelancers](https://img.shields.io/badge/Freelancer-Leads-17a67c)]()
[![Fields](https://img.shields.io/badge/Rich-Rate%20%2B%20Skills%20%2B%20Rating-2f7bbf)]()
[![Export](https://img.shields.io/badge/Export-JSON%20%2F%20CSV%20%2F%20Excel-f39c12)]()

***

### What This Actor Does

**PeoplePerHour** is a leading freelance marketplace with hundreds of thousands of freelancers and a live stream of freelance jobs. This Actor turns both sides into structured data. Two record types, from whichever URLs you provide:

| Record `type` | You provide | You get |
|---|---|---|
| **`job`** | A `freelance-jobs` URL | Job title, description, budget & currency, category, status, proposals count, posted date, and the client's name & country |
| **`freelancer`** | A `hire-freelancers` URL | Name, headline, hourly rate, skills, country/city, certification, rating, reviews, completed projects, response time, endorsements |

The Actor reads PeoplePerHour's own inline page data, so results are clean and complete — and it paginates automatically to the end of the result set.

***

### Why Use This

- **Freelancer lead lists.** Every profile carries a name, headline, skills, hourly rate, country and track record (rating, reviews, completed projects) — a ready-made source for recruiting, sourcing and outreach.
- **Live freelance job feed.** Track what buyers are posting — budgets, categories, proposal counts and the posting client — for market research, competitive intel or lead-gen to buyers.
- **Rich, structured fields.** Rate, skills, rating and stats come as clean typed values, not scraped text.
- **Fast and cheap.** Pure-HTTP with a browser-grade fingerprint. No headless browser, so it stays quick and inexpensive across thousands of records.

***

### Quick Start

#### Run it in the console (no code)

1. Open the Actor in Apify Console.
2. **Jobs:** paste a jobs URL, e.g. `https://www.peopleperhour.com/freelance-jobs/technology-programming`.
3. **Freelancers:** paste a freelancers URL, e.g. `https://www.peopleperhour.com/hire-freelancers?skills=Wordpress`.
4. Set **Max total items** (0 = all), click **Start**, then export as **JSON, CSV, Excel or HTML**, or push to Google Sheets, a webhook or a database.

#### Run it via API (Python)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run_input = {
    "searchUrls": [
        "https://www.peopleperhour.com/freelance-jobs/technology-programming",
        "https://www.peopleperhour.com/hire-freelancers?skills=Wordpress",
    ],
    "maxItems": 500,
}

run = client.actor("YOUR_USERNAME/peopleperhour-scraper").call(run_input=run_input)

for rec in client.dataset(run["defaultDatasetId"]).iterate_items():
    if rec["type"] == "freelancer":
        print(rec["name"], "·", rec["hourlyRate"], rec.get("cert"), "·", rec["country"])
```

#### Build a freelancer lead list (Python)

```python
run = client.actor("YOUR_USERNAME/peopleperhour-scraper").call(run_input={
    "searchUrls": ["https://www.peopleperhour.com/hire-freelancers?skills=Search%20engine%20optimization"],
    "maxItems": 1000,
})

leads = []
for r in client.dataset(run["defaultDatasetId"]).iterate_items():
    if r["type"] == "freelancer" and (r.get("feedbackRating") or 0) >= 0.95:
        leads.append({
            "name": r["name"], "headline": r["jobTitle"], "rate": r["hourlyRate"],
            "country": r["country"], "reviews": r["reviews"], "skills": r["skills"],
        })
print(len(leads), "high-rated freelancers")
```

#### Analyze the job market (Node.js)

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('YOUR_USERNAME/peopleperhour-scraper').call({
    searchUrls: ['https://www.peopleperhour.com/freelance-jobs/design'],
    maxItems: 300,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const jobs = items.filter(r => r.type === 'job');
const avg = jobs.reduce((s, j) => s + (j.budgetConverted || 0), 0) / jobs.length;
console.log('avg budget:', avg.toFixed(0), '· open jobs:', jobs.length);
```

***

### Input Parameters

| Field | Type | Description |
|---|---|---|
| `searchUrls` | array | PeoplePerHour `freelance-jobs` and/or `hire-freelancers` URLs. Auto-detected and paginated. |
| `maxItems` | integer | Max records (jobs + freelancers) across all URLs. `0` = no limit. |
| `proxyConfiguration` | object | Apify Proxy. Datacenter is enough and enabled by default. |

**Finding URLs:** search or filter on peopleperhour.com (by category, skill, keyword) and copy the address-bar URL — filters like `?skills=` are preserved.

***

### Output

#### `type: "job"`

```json
{
  "type": "job",
  "jobId": "4521722",
  "title": "Screen control system",
  "description": "I am looking to develop a website that will control digital screens…",
  "budget": 1000, "budgetConverted": 1342, "currency": "GBP",
  "category": "Technology & Programming", "subCategory": "Programming & Coding",
  "status": "open", "proposalCount": 58,
  "postedAt": "2026-09-16 04:50:40",
  "clientName": "Paul Swift", "clientCountry": "United Kingdom",
  "url": "https://www.peopleperhour.com/freelance-jobs/…/screen-control-system-4521722"
}
```

#### `type: "freelancer"`

```json
{
  "type": "freelancer",
  "freelancerId": "660962",
  "name": "GAJURA C.",
  "jobTitle": "SEO & Digital PR Expert | Authority Link Building",
  "country": "Spain", "countryCode": "ES", "city": "Marbella",
  "hourlyRate": 32, "cert": "TOP",
  "feedbackRating": 0.99, "reviews": 2580,
  "projectsCompleted": 3132, "completedRatio": 0.95,
  "responseTime": "within a few hours", "endorsements": 217,
  "skills": ["Link building", "Google ranking", "Content marketing", "Guest posting"],
  "url": "https://www.peopleperhour.com/freelancer/…"
}
```

***

### Use Cases

#### 1. Freelancer sourcing & recruiting

Build targeted lists of freelancers by skill, rate, country and track record. Filter to top-rated, high-volume sellers for staffing, agency bench-building or vendor sourcing.

#### 2. Freelancer lead generation

Every profile is a lead: name, headline, skills, rate and stats. Perfect for tools, courses, agencies and services that sell to freelancers.

#### 3. Freelance market & rate research

Analyze hourly rates, skills demand and budgets across categories and countries. Benchmark what freelancers charge and what buyers pay.

#### 4. Buyer / demand lead generation

Job postings carry the posting client and their country — a source of buyers actively hiring in your niche.

#### 5. Competitive & talent intelligence

Track the most in-demand skills, top freelancers and busiest categories to inform pricing, positioning and hiring.

#### 6. Data enrichment

Enrich CRM or ATS records with public freelancer stats — rating, reviews, completed projects and response time.

***

### Tips

- **Jobs URLs** look like `/freelance-jobs/{category}`; **freelancer URLs** like `/hire-freelancers` or `/hire-freelancers?skills={skill}`.
- **Skill filters** on freelancer URLs (`?skills=Wordpress`) narrow results to a niche — great for targeted lead lists.
- **`maxItems: 0`** paginates to the end; set a cap for quick samples.
- **Schedule it** with Apify Schedules to keep a fresh feed of jobs and freelancers.

***

### Frequently Asked Questions

**Do I need a PeoplePerHour account?**
No. The Actor reads publicly visible listing and profile data — no login required.

**Can I scrape both jobs and freelancers in one run?**
Yes. Put both kinds of URLs in `searchUrls`; each record carries a `type` of `job` or `freelancer`.

**How is the rating expressed?**
`feedbackRating` is PeoplePerHour's positive-feedback ratio (0–1, e.g. 0.99 = 99%). `reviews` is the review count.

**What export formats are supported?**
JSON, CSV, Excel, HTML, or via API — plus Google Sheets, webhooks, Make and Zapier.

***

### Legal & Responsible Use

This Actor collects only publicly available information for research, analytics and business use. You are responsible for how you use the data. Please:

- Respect PeoplePerHour's Terms of Service and robots directives.
- Comply with applicable data-protection laws (GDPR/CCPA) when handling personal data.
- Do not use the data for spam, harassment, or any unlawful purpose.
- Use reasonable request volumes and scheduling.

This project is an independent tool and is not affiliated with, endorsed by, or sponsored by PeoplePerHour.

# Actor input Schema

## `searchUrls` (type: `array`):

PeoplePerHour URLs. Jobs: https://www.peopleperhour.com/freelance-jobs/technology-programming . Freelancers: https://www.peopleperhour.com/hire-freelancers?skills=... . Search/filter on peopleperhour.com and copy the URL.

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

Maximum number of records (jobs + freelancers) across all URLs. 0 = no limit (paginate to the end).

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

Apify Proxy. PeoplePerHour requires a residential proxy (datacenter IPs are blocked) — residential is enabled by default.

## Actor input object example

```json
{
  "searchUrls": [
    "https://www.peopleperhour.com/freelance-jobs/technology-programming",
    "https://www.peopleperhour.com/hire-freelancers"
  ],
  "maxItems": 40,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

job | freelancer

## `jobId` (type: `string`):

Job ID

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

Job title

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

Job description

## `budget` (type: `string`):

Budget in listed currency

## `budgetConverted` (type: `string`):

Budget converted

## `currency` (type: `string`):

Currency code

## `category` (type: `string`):

Job category

## `subCategory` (type: `string`):

Job sub-category

## `status` (type: `string`):

Job status (open/…)

## `proposalCount` (type: `string`):

Number of proposals

## `postedAt` (type: `string`):

Posted date

## `clientName` (type: `string`):

Client public name

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

Client country

## `name` (type: `string`):

Freelancer name

## `jobTitle` (type: `string`):

Freelancer headline/title

## `hourlyRate` (type: `string`):

Hourly rate

## `country` (type: `string`):

Freelancer country

## `city` (type: `string`):

Freelancer city

## `cert` (type: `string`):

PPH certification (TOP/…)

## `feedbackRating` (type: `string`):

Feedback rating (0-1)

## `reviews` (type: `string`):

Number of reviews

## `projectsCompleted` (type: `string`):

Completed projects

## `responseTime` (type: `string`):

Typical response time

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

Freelancer skills

## `url` (type: `string`):

Job or profile URL

## `scrapedAt` (type: `string`):

ISO timestamp

# 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 = {
    "searchUrls": [
        "https://www.peopleperhour.com/freelance-jobs/technology-programming",
        "https://www.peopleperhour.com/hire-freelancers"
    ],
    "maxItems": 40,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/peopleperhour-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 = {
    "searchUrls": [
        "https://www.peopleperhour.com/freelance-jobs/technology-programming",
        "https://www.peopleperhour.com/hire-freelancers",
    ],
    "maxItems": 40,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("haketa/peopleperhour-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 '{
  "searchUrls": [
    "https://www.peopleperhour.com/freelance-jobs/technology-programming",
    "https://www.peopleperhour.com/hire-freelancers"
  ],
  "maxItems": 40,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call haketa/peopleperhour-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,haketa/peopleperhour-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/O5BQqJYiUC7T0drpn/builds/VaPelADcIev50fFjg/openapi.json
