# PeoplePerHour Scraper — Hourlies, Freelancers & Jobs (`logiover/peopleperhour-scraper`) Actor

PeoplePerHour scraper: export hourlies (fixed-price gigs) with USD/GBP/EUR prices, sales, reviews, extras and seller stats; freelancers with rates, CERT and track record; open and closed jobs with budgets, proposals and client hire rate. No login. Not affiliated with PeoplePerHour.

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

## Pricing

from $2.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

**PeoplePerHour Scraper** exports the public data on [PeoplePerHour](https://www.peopleperhour.com) — **hourlies** (fixed-price services, the PeoplePerHour version of gigs), **freelancer profiles** and **jobs** — to **CSV, Excel, JSON, XML or an API**, with no login, no cookies and no browser. If you were looking for a **PPH scraper**, a **PeoplePerHour API alternative** or a way to **scrape PeoplePerHour hourlies, freelancers and jobs without login**, this Actor does all three in one run: search by keyword, category, country and price, or paste any PeoplePerHour URL.

It reads the same JSON data the PeoplePerHour website loads, 50 records per request, so it is **fast** (1,000 hourlies in about 7 seconds), **deep** (it goes past PeoplePerHour's 10,000-results-per-search limit automatically — 92,626 hourlies in one measured run) and **efficient** (no browser, no proxy traffic — you pay only per result). Use it for **freelance pricing research**, **competitor gig analysis**, **Fiverr alternative data**, **agency and freelancer lead generation** and **UK talent sourcing** — every row comes with prices in USD, GBP and EUR, sales, reviews, seller statistics, budgets, proposal counts and client hiring history, ready to **export to CSV, Excel or JSON**.

> **Measured on 2026-10-13 on the Apify platform:** 92,626 hourlies in one run (7 min 30 s, 0 duplicates) · all 18,228 listed freelancers in 52 s · 1,000 hourlies in 7 s · pay per result · no login · no cookies · no proxy needed.

***

### What you get

| Row type | What it is | Columns | Highlights | Catalogue size (2026-10-13) |
|---|---|---|---|---|
| **Hourlie** (`type: "hourlie"`) | A fixed-price service a freelancer sells ("gig") | 53 | price + USD/GBP/EUR price list, sales, reviews, average stars, delivery days, add-on extras, gallery, video, category, tags, full seller block with CERT level, feedback %, completed jobs | **~92,600** live hourlies |
| **Freelancer** (`type: "freelancer"`) | A seller profile | 33 | hourly rate (own currency + USD), country, city, feedback %, reviews, jobs completed, completion rate, buyers, endorsements, CERT level, skills, languages, portfolio size | **18,228** listed freelancers |
| **Job** (`type: "job"`) | A project a client posted for proposals | 34 + 6 | budget (own currency + USD), fixed / hourly, experience level, proposals, status, dates, category, client country, and the client's hiring history (jobs posted, hires, hire rate) | **~700 open + ~77,650 closed** (78,345 in total) |

One run can mix row types (for example a list of hourlie, freelancer and job URLs); the `type` column tells them apart, and the dataset has a ready-made table view for each.

***

### Use cases

#### Freelance pricing research

What do logo designers, WordPress developers, copywriters or bookkeepers charge on a UK marketplace? Export every hourlie in a category with its price in USD, GBP and EUR, delivery days, add-on prices and sales count, then chart price against sales and reviews. For the demand side, the **closed-job archive** (~77,650 projects) shows what clients actually budgeted for each kind of work — fixed vs hourly, entry vs expert level, and how many proposals each job attracted.

#### Competitor gig analysis

Selling services on PeoplePerHour, Fiverr or Upwork? See which hourlies rank first for your keywords (`position`), how many sales and reviews they have, what they promise in the title, which extras they upsell and at what price, how fast they deliver and how long they have been listed. Schedule the same search daily to track rank and sales changes.

#### Agency and freelancer lead generation

Build lists of agencies and freelancers by skill, category and country — with profile URL, headline, hourly rate, CERT level, review count, completed jobs, buyers worked with and last activity date. Top sellers and agencies stand out by `sales` and `sellerBuyersWorkedWith`. Ideal for B2B outreach, partnership programmes, white-label resellers and recruitment agencies.

#### UK talent sourcing

PeoplePerHour is a UK-based marketplace, and the United Kingdom is the biggest country in its freelancer directory (about 5,350 of the 18,228 listed freelancers on 2026-10-13). Filter freelancers by `countries: ["GB"]` and skill, sort by rating or sales, and shortlist people with a long track record (`projectsCompleted`), a high `completionRatePercent` and recent work (`lastProjectAt`).

#### More ideas

**Fiverr alternative data** for cross-marketplace price comparison · **job-market analysis** (new jobs, budgets and proposal competition per category) · **client prospecting** using each client's hiring history · clean typed **datasets for BI, pricing models and AI**.

***

### How it works

1. **Choose a mode** — `hourlies`, `freelancers` or `jobs` — and describe the search: keywords, categories, countries and filters. Or paste PeoplePerHour URLs.
2. **The Actor reads PeoplePerHour's own JSON data** (the same data the website renders) over a direct connection, 50 records per request, several requests in parallel. No headless browser, no login, no cookies, no proxy traffic.
3. **Past the 10,000 limit.** PeoplePerHour returns at most 10,000 results per search. When you ask for more, the Actor splits the search automatically along PeoplePerHour's own facet counts — subcategory, then seller country, then price band for hourlies; country, then rate band for freelancers; subcategory, experience level and budget type for jobs. These facets partition the results exactly, so nothing is skipped, and every row is de-duplicated by id.
4. **Fair share.** With several searches and a *Max results* limit, each search first gets an equal share; whatever a small search does not use goes to the bigger ones.
5. **Rows are saved as they arrive**, with `position` (rank in the search), `query` (which search produced the row) and `scrapedAt`.

**Reliability built in:** automatic fallback to Apify datacenter proxies and then your proxy if PeoplePerHour refuses a route · automatic recovery if PeoplePerHour changes its web-client key · back-off on rate limits · exact *Max results* · stops cleanly at the run's time limit or maximum cost and keeps what it has · no matches → **succeeds** with a clear message; PeoplePerHour unreachable → **fails** with the reason · a `SUMMARY` record with request counts, splits and the route used.

**Freshness:** every run reads PeoplePerHour live at that moment — no cache, no stale database.

***

### Input

#### Hourlies (services / gigs)

```json
{
  "mode": "hourlies",
  "searchTerms": ["logo design", "wordpress"],
  "categories": [],
  "countries": ["GB"],
  "minPrice": 0,
  "maxPrice": 150,
  "deliveryWithinDays": "any",
  "sort": "most-sales",
  "includeDetails": true,
  "maxItems": 2000
}
```

Leave `searchTerms` empty to browse whole categories. `categories` takes a category, subcategory or skill (`"Design"`, `"Logo Design"`, `"SEO"`, `"logo-design"` or an id such as `"9197"`); `countries` is the seller's country; `minPrice` / `maxPrice` are in USD. `includeDetails` adds the USD/GBP/EUR price list, extras, gallery and video at no extra requests.

#### Freelancers

```json
{
  "mode": "freelancers",
  "searchTerms": ["python", "excel"],
  "categories": ["Website Development"],
  "countries": ["GB", "US"],
  "minHourlyRateGbp": 20,
  "maxHourlyRateGbp": 80,
  "sort": "rating",
  "maxItems": 5000
}
```

`countries` is where the freelancer is based; the rate range is in GBP (PeoplePerHour filters rates in pounds) while every row also carries `hourlyRateUsd`. Sorts: `relevance`, `most-sales`, `rating`, `price-desc` (highest rate first), `newest` (newest members).

#### Jobs

```json
{
  "mode": "jobs",
  "searchTerms": ["shopify"],
  "categories": [],
  "countries": ["GB"],
  "jobStatus": "all",
  "jobBudgetType": "any",
  "jobExperience": "any",
  "sort": "newest",
  "includeDetails": true,
  "maxItems": 1000
}
```

`jobStatus` is `open` (the live board), `closed` (the archive: in progress, completed, expired) or `all`; `countries` is the client's country. Sorts: `relevance`, `newest`, `price-desc` (highest budget first), `popularity` (most proposals), `ending` (ending soon). `includeDetails` adds the client's hiring history (one extra request per client).

#### PeoplePerHour URLs

```json
{
  "startUrls": [
    { "url": "https://www.peopleperhour.com/hourlie/create-an-interesting-and-unique-500-word-blog-for-you-no-ai/43223" },
    { "url": "https://www.peopleperhour.com/freelance-jobs/design/label-merchandising/laber-for-granola-4526420" },
    { "url": "https://www.peopleperhour.com/services/logo-design?price=20-50" },
    { "url": "https://www.peopleperhour.com/hire-freelancers?keyword=excel" }
  ],
  "maxItems": 300
}
```

Hourlie pages, freelancer profiles, job pages and listing pages (`/services/...`, `/hire-freelancers/...`, `/freelance-jobs/...`) are all accepted; listing filters in the URL (`price`, `minPrice`, `maxPrice`, `deliveryDays`, `location`, `keyword`, `sort`) are kept. If you give only URLs, only the URLs are scraped.

#### All input fields

| Field | Type | Default | Applies to | Description |
|---|---|---|---|---|
| `mode` | select | `hourlies` | — | `hourlies`, `freelancers` or `jobs`. |
| `searchTerms` | string list | — | all | Keywords; each is one search. |
| `categories` | string list | — | all | Category / subcategory / skill by name, slug or id. Unknown names are searched as keywords. |
| `countries` | string list | — | all | Seller country (hourlies), freelancer country, client country (jobs). |
| `startUrls` | URL list | — | all | PeoplePerHour URLs of any kind. |
| `maxItems` | integer | `1000` | all | Max unique rows; `0` = no limit. |
| `sort` | select | `relevance` | all | Options that do not exist for a mode fall back to relevance. |
| `includeDetails` | boolean | `true` | hourlies, jobs | Price list, extras, gallery (hourlies); client hiring history (jobs). |
| `minPrice`, `maxPrice` | integer | `0` | hourlies | Price range in USD; `0` = off. |
| `deliveryWithinDays` | select | `any` | hourlies | 1–4 days. |
| `minHourlyRateGbp`, `maxHourlyRateGbp` | integer | `0` | freelancers | Rate range in GBP; `0` = off. |
| `jobStatus` | select | `open` | jobs | `open`, `closed`, `all`. |
| `jobBudgetType` | select | `any` | jobs | `fixed`, `hourly`. |
| `jobExperience` | select | `any` | jobs | `entry`, `intermediate`, `expert`. |
| `concurrency` | integer | `8` | all | Parallel requests (1–20). |
| `proxyConfiguration` | proxy | Residential | all | Fallback only — used if PeoplePerHour refuses direct connections. |

***

### Output data dictionary

All rows share `type`, `id`, `url`, `position`, `query` and `scrapedAt`. Dates are ISO 8601 in UTC. Missing values are `null` — never `0` or an empty string — so averages and filters stay honest.

#### Hourlie columns

| Column | Type | Description |
|---|---|---|
| `type` | string | Always `hourlie`. |
| `id`, `title`, `url` | string | Hourlie id (unique key), title and page URL. |
| `price`, `currency` | number, string | Price and its currency as PeoplePerHour quotes it (USD on Apify). |
| `priceUsd`, `priceGbp`, `priceEur` | number | PeoplePerHour's own price list in USD, GBP and EUR (*Include details*). |
| `sellerCurrency` | string | Currency the seller set the price in. |
| `deliveryDays` | number | Promised delivery time in days. |
| `sales` | number | Times the hourlie was bought. |
| `reviewCount`, `averageStars` | number | Reviews of this hourlie and their average star rating (1–5). |
| `ratingPercent` | number | PeoplePerHour's positive-rating %; null when there are no reviews yet. |
| `featured` | boolean | Featured listing. |
| `category`, `subcategory` | string | PeoplePerHour category and subcategory. |
| `tags` | array | Skill tags on the hourlie. |
| `description`, `requirements`, `keywords` | string | Full description, what the buyer must provide, the seller's keyword line. |
| `image`, `images`, `videoUrl` | string, array, string | Cover image, full gallery in original size (*Include details*), video if any. |
| `extras` | array of objects | Paid add-ons: `title`, `price`, `currency`, `extraDays`, `type` (custom / predefined). |
| `createdAt` | string | Date the hourlie was listed (UTC). |
| `responseTime` | string | Seller's typical response time, e.g. "within a few hours". |
| `sellerId`, `sellerName`, `sellerUrl`, `sellerPhoto` | string | Seller member id, public name, profile URL and photo. |
| `sellerHeadline`, `sellerAbout` | string | Profile headline and about text. |
| `sellerCountry`, `sellerCountryCode`, `sellerCity` | string | Seller location. |
| `sellerHourlyRate`, `sellerRateCurrency`, `sellerHourlyRateUsd` | number, string, number | Seller's hourly rate, its currency, and the rate in USD. |
| `sellerMemberSince` | string | Date the seller joined. |
| `sellerCertLevel`, `sellerVerified` | number, boolean | PeoplePerHour CERT level 0–6 (6 = TOP CERT); identity verified. |
| `sellerFeedbackPercent`, `sellerReviewCount` | number | Positive feedback % and reviews on the seller profile. |
| `sellerProjectsCompleted`, `sellerBuyersWorkedWith`, `sellerLastProjectAt` | number, number, string | Jobs completed, distinct buyers, date of the last job (UTC). |
| `sellerSkills`, `sellerLanguages` | array | Profile skills and spoken languages. |
| `position`, `query`, `scrapedAt` | number, string, string | Rank in its search (1 = first; null for start URLs), the search that produced the row, collection time (UTC). |

#### Freelancer columns

| Column | Type | Description |
|---|---|---|
| `type` | string | Always `freelancer`. |
| `id` | string | Member id (unique key). |
| `name`, `url`, `headline` | string | Public name, profile URL and headline. |
| `hourlyRate`, `rateCurrency`, `hourlyRateUsd` | number, string, number | Hourly rate in the freelancer's currency, that currency, and the rate in USD. |
| `country`, `countryCode`, `city` | string | Where the freelancer is based. |
| `feedbackPercent`, `reviewCount` | number | Positive feedback % (null without reviews) and number of reviews. |
| `projectsCompleted`, `projectsWorkedOn`, `completionRatePercent` | number | Jobs completed, jobs worked on, and the completed share in %. |
| `buyersWorkedWith`, `endorsements` | number | Distinct buyers worked with; endorsements. |
| `lastProjectAt`, `memberSince` | string | Date of the last job (UTC); date joined. |
| `responseTime` | string | Typical response time. |
| `certLevel`, `verified`, `featured` | number, boolean, boolean | CERT level 0–6 (6 = TOP CERT); identity verified; featured. |
| `skills`, `languages` | array | Profile skills and spoken languages. |
| `portfolioItems` | number | Number of portfolio items. |
| `about`, `photo`, `coverImage` | string | About text, profile photo, cover image. |
| `position`, `query`, `scrapedAt` | number, string, string | Rank in its search (1 = first; null for start URLs), the search that produced the row, collection time (UTC). |

#### Job columns

| Column | Type | Description |
|---|---|---|
| `type` | string | Always `job`. |
| `id`, `title`, `url` | string | Job id (unique key), title and page URL. |
| `budget`, `currency`, `budgetUsd` | number, string, number | Budget in the client's currency (per hour for hourly jobs), that currency, and the budget in USD. |
| `budgetType`, `experienceLevel` | string | `fixed` or `hourly`; `entry`, `intermediate` or `expert`. |
| `status`, `isOpen` | string, boolean | `open`, `in_progress`, `expired`, `completed` or `closed`; whether it still accepts proposals. |
| `proposals` | number | Proposals freelancers sent. |
| `postedAt`, `expiresAt` | string | Posting date and proposal deadline (UTC). |
| `category`, `subcategory` | string | PeoplePerHour category and subcategory. |
| `locationType` | string | `remote`, `remote_country` or `onsite`. |
| `urgent`, `featured`, `preFunded`, `highValue` | boolean | Urgent, featured, budget pre-funded, tagged as a high-value opportunity. |
| `description`, `attachments` | string, array | Full job description; URLs of files the client attached. |
| `clientId`, `clientName`, `clientUrl` | string | Client member id, public name, profile URL. |
| `clientCountry`, `clientCountryCode`, `clientCity` | string | Client location. |
| `clientMemberSince`, `clientVerified` | string, boolean | Date the client joined; identity verified. |
| `clientJobsPosted`, `clientJobsCompleted`, `clientFreelancersHired`, `clientHireRatePercent` | number | Client's hiring history: jobs posted, jobs completed, freelancers hired, share of jobs awarded (*Include details*). |
| `clientLastJobAt`, `clientResponseTime` | string | Client's last job date (UTC) and response time (*Include details*). |
| `position`, `query`, `scrapedAt` | number, string, string | Rank in its search (1 = first; null for start URLs), the search that produced the row, collection time (UTC). |

***

### Output examples (real rows from a test run, shortened)

Real rows from runs on 2026-10-13; long texts are cut and personal names shortened for this page.

**Hourlie**

```json
{
  "type": "hourlie",
  "id": "43223",
  "title": "create an interesting and unique 500 word blog for you. No AI!",
  "url": "https://www.peopleperhour.com/hourlie/create-an-interesting-and-unique …",
  "price": 30,
  "currency": "USD",
  "priceUsd": 30,
  "priceGbp": 20,
  "priceEur": 25,
  "sellerCurrency": "GBP",
  "deliveryDays": 6,
  "sales": 5312,
  "reviewCount": 2285,
  "averageStars": 4.91,
  "ratingPercent": 98,
  "featured": false,
  "category": "Writing & Translation",
  "subcategory": "Content Writing",
  "tags": [
    "Article",
    "Blog"
  ],
  "description": "Do you need interesting, factual and customer focused blogs for your b …",
  "images": [
    "https://dw3i9sxi97owk.cloudfront.net/uploads/hourliesAttachments/2016/04/c442ae6c81de5c5c098e8f97bb7385c0.jpg",
    "https://dw3i9sxi97owk.cloudfront.net/uploads/hourliesAttachments/2013/03/d7c40b3b0f7d2945d3e2030ab5e4d01f.jpg"
  ],
  "extras": [
    {
      "title": "I can deliver all work in 1 working day",
      "price": 20,
      "currency": "USD",
      "extraDays": 1,
      "type": "predefined"
    }
  ],
  "createdAt": "2013-03-13T14:09:08.000Z",
  "responseTime": "within 2 days",
  "sellerId": "303619",
  "sellerName": "Sam T.",
  "sellerCountryCode": "GB",
  "sellerCity": "Swindon",
  "sellerHourlyRate": 15,
  "sellerRateCurrency": "GBP",
  "sellerHourlyRateUsd": 20.13,
  "sellerCertLevel": 5,
  "sellerVerified": true,
  "sellerFeedbackPercent": 98.1,
  "sellerReviewCount": 2898,
  "sellerProjectsCompleted": 3421,
  "sellerBuyersWorkedWith": 1779,
  "sellerLastProjectAt": "2026-08-06T18:10:32.000Z",
  "position": 4,
  "query": "all hourlies",
  "scrapedAt": "2026-10-13T10:12:05.863Z"
}
```

**Freelancer**

```json
{
  "type": "freelancer",
  "id": "243100",
  "name": "Callum G.",
  "headline": "Graphic Designer",
  "hourlyRate": 25,
  "rateCurrency": "GBP",
  "hourlyRateUsd": 33.55,
  "country": "United Kingdom",
  "countryCode": "GB",
  "city": "Wales",
  "feedbackPercent": 100,
  "reviewCount": 245,
  "projectsCompleted": 230,
  "projectsWorkedOn": 256,
  "completionRatePercent": 90,
  "buyersWorkedWith": 150,
  "endorsements": 9,
  "lastProjectAt": "2026-10-07T14:20:06.000Z",
  "responseTime": "within two days",
  "certLevel": 5,
  "verified": true,
  "memberSince": "2011-11-22",
  "skills": [
    "Ad design",
    "Graphics design",
    "Brand design"
  ],
  "languages": [
    "English"
  ],
  "portfolioItems": 12,
  "position": 2,
  "query": "\"excel\" · based in GB"
}
```

**Job with client hiring history**

```json
{
  "type": "job",
  "id": "4526420",
  "title": "Laber for Granola",
  "url": "https://www.peopleperhour.com/freelance-jobs/design/label-merchandisin …",
  "budget": 71,
  "currency": "GBP",
  "budgetUsd": 95.29,
  "budgetType": "fixed",
  "experienceLevel": "entry",
  "status": "open",
  "isOpen": true,
  "proposals": 26,
  "postedAt": "2026-10-13T09:15:52.000Z",
  "expiresAt": "2026-11-12T09:15:52.000Z",
  "category": "Design",
  "subcategory": "Label & Merchandising",
  "locationType": "remote",
  "urgent": false,
  "preFunded": false,
  "highValue": false,
  "description": "Morning,\nI need a label for a bag of granola. need something elegant a …",
  "clientName": "Valentina S.",
  "clientCountryCode": "GB",
  "clientCity": "Northolt",
  "clientMemberSince": "2016-10-17",
  "clientJobsPosted": 11,
  "clientJobsCompleted": 8,
  "clientFreelancersHired": 8,
  "clientHireRatePercent": 80,
  "clientLastJobAt": "2023-05-26T13:53:44.000Z",
  "clientResponseTime": "instantly",
  "position": 4,
  "query": "all jobs"
}
```

***

### How much data can I get?

| | Live on PeoplePerHour (2026-10-13) | Reached by this Actor | Measured |
|---|---|---|---|
| Hourlies | ~92,600 | **all of them** | 92,626 unique rows in one platform run, 7 min 30 s |
| Freelancers | 18,228 | **all of them** | 18,228 unique rows in one platform run, 52 s |
| Jobs (open + closed) | 78,345 | **all of them** | 12,000 closed jobs in one platform run (19 s, details off); all 78,345 open + closed jobs in a full test run |

- A single search returns up to **10,000** rows; above that the automatic split takes over (the hourlie run above used 123 sub-searches).
- With *Max results* `0` and no search terms you export a whole catalogue in one run — **tens of thousands of rows per run**.
- Narrow searches return what PeoplePerHour has: for example "logo design" ≈ 2,400 hourlies, "python" ≈ 400 hourlies and ≈ 1,200 freelancers.

#### Big runs need a big enough "Maximum cost per run"

This Actor is paid per result, and every paid run on Apify has a **maximum cost** — if you do not set one, the platform applies a default limit, which may cover only a few thousand results. For **10,000+ results**, set **Maximum cost per run** (run options in Console, or `maxTotalChargeUsd` in the API) to at least **results × price per result** — for example 20,000 results at $2.00 per 1,000 need at least **$40**; the whole hourlie catalogue (~92,600) at $2.00 per 1,000 needs about $186.

If the maximum cost is lower than that, nothing breaks: the Actor works out at the start how many results the limit covers, fetches only those, **stops cleanly at that number and finishes as Succeeded**, with a status message such as *"Saved 5,700 hourlies — this run's maximum cost ($17.10) covers 5,700 results at the current price. Raise 'Maximum cost per run' to get more."* The `SUMMARY` record then contains `stoppedBy: "MAX_COST"` and `affordableResults`. You are never charged beyond your limit, and the run never fails or aborts because of it.

***

### Speed and cost per 1,000 rows (measured)

**What you pay:** a fixed price per result — see the *Pricing* tab (on 2026-10-13: $3.00 per 1,000 results on the Free plan, $2.00 per 1,000 from Gold up). Compute, storage and proxies are included.

**How fast it is** — measured on the Apify platform on 2026-10-13 with the default 1 GB memory unless noted:

| Input | Rows | Run time | Peak memory |
|---|---|---|---|
| Example input — "logo design", 100 hourlies | 100 | 4.5 s | 57 MB |
| All hourlies by most sales, 1,000 | 1,000 | 7.2 s | 79 MB |
| All hourlies, 15,000 (auto-split) | 15,000 | 64 s | 127 MB |
| **Whole hourlie catalogue** | 92,626 | 7 min 30 s | 138 MB |
| **All freelancers** | 18,228 | 52 s | 86 MB |
| Freelancers: python + excel × GB + US | 600 | 3.0 s | 62 MB |
| Whole open job board + client history | 697 | 9.8 s | 68 MB |
| Closed jobs, newest 2,000 + client history (0.5 GB) | 2,000 | 41 s | 62 MB |
| **Closed jobs 12,000, details off** | 12,000 | 19 s | 80 MB |
| Search with no matches | 0 | 1.7 s | 50 MB |

Throughput on 1 GB is roughly **205 rows per second** for large exports with details on, so 1,000 rows take seconds and 10,000 rows well under a minute. The Actor is CPU-bound on JSON parsing, so more memory (= more CPU) is what makes very large exports faster — see *Limits and tips*.

***

### Integrations

#### API (curl)

Run the Actor and get the rows in one call (fine for runs up to a few minutes):

```bash
curl -X POST "https://api.apify.com/v2/acts/logiover~peopleperhour-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN&format=json" \
  -H "Content-Type: application/json" \
  -d '{"mode":"hourlies","searchTerms":["logo design"],"maxItems":200}'
```

For big exports start a run with a maximum cost that covers them (`POST /v2/acts/logiover~peopleperhour-scraper/runs?maxTotalChargeUsd=50`) and read `GET /v2/datasets/{defaultDatasetId}/items?format=csv` when it finishes — `format` can be `json`, `csv`, `xlsx`, `xml` or `jsonl`.

#### Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('logiover/peopleperhour-scraper').call({
    mode: 'freelancers',
    searchTerms: ['python'],
    countries: ['GB'],
    maxItems: 500,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0].name, items[0].hourlyRateUsd);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("logiover/peopleperhour-scraper").call(run_input={
    "mode": "jobs",
    "jobStatus": "closed",
    "categories": ["Design"],
    "maxItems": 5000,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["title"], job["budget"], job["currency"], job["proposals"])
```

#### Make, Zapier and n8n

Use the Apify app in **Make** ("Run an Actor" → "Get dataset items"), the **Zapier** Apify integration, or the Apify node in **n8n**. Pick `logiover/peopleperhour-scraper`, paste the input JSON, and map the dataset fields into your CRM, Airtable, Notion, Slack or e-mail steps.

#### Google Sheets

Add the Google Sheets integration from the Actor's *Integrations* tab to append every run's rows to a sheet, or download the dataset as CSV / Excel and import it.

#### Webhooks

Add a webhook for the `ACTOR.RUN.SUCCEEDED` event; the payload contains `resource.defaultDatasetId`, so your backend can fetch the rows the moment a scheduled run finishes.

#### MCP and AI agents

AI agents can call this Actor as a tool through the Apify MCP server (`https://mcp.apify.com?actors=logiover/peopleperhour-scraper`) from Claude, Cursor, VS Code or any MCP client — for example "find UK WordPress freelancers under £40/hour with 50+ reviews" or "what do PeoplePerHour sellers charge for a logo?".

***

### Comparison

| | Browsing PeoplePerHour by hand | Generic HTML scrapers / browser extensions | **PeoplePerHour Scraper** |
|---|---|---|---|
| Speed | one page of ~20 results at a time | slow: a browser per page; PeoplePerHour's pages sit behind a bot check | **1,000 rows in ~7 s** |
| Depth | what you can click through | usually stops at the site's page limit | **whole catalogue — past the 10,000 limit automatically** |
| Fields | what the card shows | what is visible on the page | **53 hourlie / 33 freelancer / 40 job columns**, incl. USD/GBP/EUR prices, seller stats, client hire rate |
| Login / cookies | account needed for some views | often needs your session | **none** |
| Proxy cost | — | typically residential proxies for every page | **none by default** |
| Output | copy-paste | raw HTML or partial CSV | **typed JSON / CSV / Excel / API**, nulls instead of fake zeros |
| Reliability | — | breaks on layout changes | reads structured data; honest success/failure; automatic route fallback |

***

### Limits and tips

- **10,000 per search** is PeoplePerHour's limit; set *Max results* above 10,000 (or `0`) and the Actor splits searches for you.
- **Fastest exports:** the Actor is CPU-bound, so memory is the speed dial — going from 0.5 GB to 1 GB cut a 15,000-hourlie run from 161 s to 64 s, while doubling *Parallel requests* from 8 to 16 only took it to 60 s. Give exports above 50,000 rows 2–4 GB, and turn *Include details* off for hourlies if you do not need the price list, extras and gallery (half-size downloads).
- **Client hiring history** costs one extra request per client — for the full ~77,650-job archive turn *Include details* off, or run it in category batches.
- **Currencies:** `price` is quoted in the currency PeoplePerHour shows the scraper (USD on Apify); use `priceUsd` / `priceGbp` / `priceEur`, `hourlyRateUsd` and `budgetUsd` for like-for-like comparisons.
- **Rank tracking:** schedule the same search and compare `position`, `sales` and `reviewCount` per `id` over time.
- **Categories:** names match PeoplePerHour's 10 categories, ~130 subcategories and ~390 skills case-insensitively — e.g. `Design`, `Website Development`, `SEO`, `Logo Design`, `Bookkeeping`, `Unity`.
- **Job skills:** PeoplePerHour's job data has no per-job skill tags; filter jobs by skill through *Categories* instead.

***

### FAQ

#### What is PeoplePerHour Scraper?

An Apify Actor that exports PeoplePerHour hourlies, freelancer profiles and jobs as structured data (JSON, CSV, Excel, XML, API) — a PeoplePerHour API alternative without building your own scraper.

#### Does PeoplePerHour have a public API for this data?

PeoplePerHour does not offer a self-serve export of its listings. This Actor reads the same public data the website shows and gives it to you as typed rows you can download or call through the Apify API.

#### Do I need a PeoplePerHour account, login or cookies?

No. Everything comes from public pages' data. No account, no login, no cookies, no session.

#### Do I need proxies?

No. The data is read over a direct connection, so there is no proxy traffic to pay for. The proxy setting is only a fallback if PeoplePerHour ever starts refusing direct connections.

#### How many results can I get per run?

As many as PeoplePerHour has. One search returns up to 10,000 rows; with *Max results* above that (or `0`) the Actor splits the search automatically. Measured: 92,626 hourlies and 18,228 freelancers in single runs. For 10,000+ results also raise **Maximum cost per run** to at least results × price per result (see the next questions).

#### How fast is it?

About 7 seconds for 1,000 hourlies, about a minute for 15,000, and 7 min 30 s for the whole hourlie catalogue on the default 1 GB memory (measured 2026-10-13).

#### How much does it cost to run?

You pay per result — see the *Pricing* tab (on 2026-10-13: $3.00 per 1,000 results on the Free plan, $2.00 per 1,000 from Gold up). Compute, storage and proxies are included.

#### Why did my run stop before reaching Max results?

Usually because of **Maximum cost per run**. Every paid run has one (the platform sets a default if you don't), and the Actor stops cleanly at the number of results it covers instead of failing. The status message tells you how many results your limit covered; raise *Maximum cost per run* to at least results × price per result — e.g. $40 for 20,000 results at $2.00 per 1,000 — and run again. The other reasons are that the search has fewer results than you asked for, or the run reached its time limit; the status message says which.

#### What is a PeoplePerHour Hourlie?

A fixed-price service a freelancer sells on PeoplePerHour — for example "design a logo for £50 in 2 days" — the PeoplePerHour equivalent of a Fiverr gig. Hourlies have a price, delivery time, optional paid extras, sales and reviews.

#### Can I scrape PeoplePerHour freelancers by country and skill?

Yes. Use `mode: "freelancers"` with `countries` (e.g. `["GB"]`) and `searchTerms` or `categories` (e.g. `["Website Development"]`); add a GBP rate range if you need one.

#### Can I get closed or historical PeoplePerHour jobs?

Yes. `jobStatus: "closed"` reads PeoplePerHour's archive of ~77,650 past projects (in progress, completed, expired) with budget, budget type, experience level, proposal count and dates; `"all"` combines open and closed.

#### Which currency are prices in?

Hourlies give `price` + `currency` (as PeoplePerHour quotes them, USD on Apify) and PeoplePerHour's own `priceUsd`, `priceGbp` and `priceEur`. Freelancers give the rate in their own currency plus `hourlyRateUsd`; jobs give the budget in the client's currency plus `budgetUsd`.

#### Why are some prices identical in USD, GBP and EUR?

PeoplePerHour keeps a price list per currency and some sellers set the same round number in each (10 / 10 / 10). Those are PeoplePerHour's values — the Actor does not convert them.

#### Why is `ratingPercent` sometimes null?

PeoplePerHour reports a 0 % rating for hourlies that have no reviews yet. The Actor returns `null` in that case so a new listing does not look like a badly rated one.

#### Can I get only new jobs every day?

Yes — schedule `mode: "jobs"`, `sort: "newest"`, `maxItems: 100` hourly or daily and keep the rows whose `id` you have not seen.

#### What happens if my search has no results or PeoplePerHour is down?

No matches → the run succeeds with 0 rows and a clear status message. PeoplePerHour unreachable → the run fails with the reason. You can always tell an empty result from an outage.

#### I need a field or filter that is missing. Can you add it?

Yes. **Open an issue on the Actor's Issues tab — answered within 24 h.** Bug reports with the run link get fixed first.

***

### Is it legal to scrape PeoplePerHour?

This Actor collects only publicly available listing data — the same information any visitor sees without logging in — and does not bypass logins or access private messages, orders or payment data. Scraping public data is generally lawful, but how you use the data matters: if you process information about individual freelancers or clients (names, cities, profile texts), data-protection rules such as the GDPR and UK GDPR apply to you, and you should have a legitimate purpose and respect people's rights. Check PeoplePerHour's terms where they apply to you. This is not legal advice.

***

### Changelog

- **2026-10-13 — first release.** Hourlies, freelancers and jobs (open and closed) from one Actor; direct JSON reading with no proxy; automatic split past the 10,000 limit; USD/GBP/EUR price lists, extras, gallery, seller statistics, client hiring history; fair share across searches; exact *Max results*; honest success/failure; `SUMMARY` record. Full details in the *Changelog* tab.

### Support

Questions, missing fields or a broken run? **Open an issue on the Issues tab — answered within 24 h.** Include the run link and we will look at the exact run.

***

*PeoplePerHour Scraper is an independent tool and is **not affiliated with, endorsed by or sponsored by PeoplePerHour Ltd** or any of its subsidiaries. "PeoplePerHour" and "Hourlie" are trademarks of their respective owner and are used here only to describe the data the tool works with.*

# Changelog

This Actor's version history is a separate document: https://apify.com/logiover/peopleperhour-scraper/changelog.md

# Actor input Schema

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

Hourlies are PeoplePerHour's fixed-price service listings (like Fiverr gigs). Freelancers are seller profiles. Jobs are projects clients post for proposals. Start URLs below can mix all three.

## `searchTerms` (type: `array`):

Keywords to search, e.g. "logo design", "wordpress", "bookkeeping". Each term is one search. Leave empty to browse everything in the chosen categories / countries.

## `categories` (type: `array`):

Optional. PeoplePerHour category, subcategory or skill — by name ("Logo Design", "Website Development", "SEO"), URL slug ("logo-design") or numeric id ("9197"). Every search term is combined with every category. A name that is not a PeoplePerHour category is searched as a keyword.

## `countries` (type: `array`):

Optional. Two-letter country codes (GB, US, IN, PK, DE, ...) or English names. Hourlies: the seller's country. Freelancers: where the freelancer is based. Jobs: the client's country (applied to the results PeoplePerHour returns).

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

Optional. Paste hourlie pages (/hourlie/...), freelancer profiles (/freelancer/...), job pages (/freelance-jobs/.../...-1234567) or listing pages (/services/logo-design, /hire-freelancers/..., /freelance-jobs/design). If you only give URLs, only the URLs are scraped.

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

Stop after this many unique rows across all searches. 0 means no limit. One search can give up to 10,000 rows; above that the scraper splits the search by subcategory, country and price automatically.

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

PeoplePerHour's own sort order. The `position` column keeps each row's rank. An option that does not exist for the chosen mode falls back to relevance.

## `includeDetails` (type: `boolean`):

Hourlies: prices in USD, GBP and EUR, add-on extras, full image gallery and video (no extra requests, slightly larger downloads). Jobs: the client's hiring history — jobs posted, freelancers hired, hire rate, last job (one extra request per client). Freelancer rows are complete either way.

## `minPrice` (type: `integer`):

Only hourlies priced at or above this amount. 0 turns it off.

## `maxPrice` (type: `integer`):

Only hourlies priced at or below this amount. 0 turns it off.

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

PeoplePerHour's delivery-time filter for hourlies.

## `minHourlyRateGbp` (type: `integer`):

PeoplePerHour filters freelancer rates in pounds sterling. 0 turns it off.

## `maxHourlyRateGbp` (type: `integer`):

Upper bound of the freelancer rate filter, in pounds sterling. 0 turns it off.

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

Open jobs are the live board (a few hundred at any time). Closed jobs are PeoplePerHour's archive of ~78,000 past projects with budgets and proposal counts — ideal for pricing research.

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

Fixed-price or hourly projects.

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

The experience level the client asked for.

## `concurrency` (type: `integer`):

How many PeoplePerHour API pages (50 rows each) to load at once.

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

Used only as a fallback. The scraper reads PeoplePerHour's public JSON API over a direct connection first (no proxy cost); if PeoplePerHour refuses it, it switches to Apify datacenter proxies and then to this proxy.

## Actor input object example

```json
{
  "mode": "hourlies",
  "searchTerms": [
    "logo design"
  ],
  "categories": [],
  "countries": [],
  "startUrls": [],
  "maxItems": 100,
  "sort": "relevance",
  "includeDetails": true,
  "minPrice": 0,
  "maxPrice": 0,
  "deliveryWithinDays": "any",
  "minHourlyRateGbp": 0,
  "maxHourlyRateGbp": 0,
  "jobStatus": "open",
  "jobBudgetType": "any",
  "jobExperience": "any",
  "concurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

All rows collected in this 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 = {
    "mode": "hourlies",
    "searchTerms": [
        "logo design"
    ],
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("logiover/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 = {
    "mode": "hourlies",
    "searchTerms": ["logo design"],
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("logiover/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 '{
  "mode": "hourlies",
  "searchTerms": [
    "logo design"
  ],
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call logiover/peopleperhour-scraper --silent --output-dataset

```

## MCP server setup

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