PeoplePerHour APIpeopleperhour.com ↗
Access PeoplePerHour freelance job postings via API. Search by keyword, category, and sort order. Get full job details including skills, timestamps, and location type.
What is the PeoplePerHour API?
The PeoplePerHour API provides two endpoints — list_jobs and get_job — to retrieve public freelance job postings from PeoplePerHour's job board. list_jobs returns paginated cards of up to 20 jobs per page, each carrying a title, description snippet, price display, and job URL. get_job resolves any of those URLs into a full posting with 9 structured fields including required skills, posting and expiry timestamps, employment type, location type, and applicant country restriction.
curl -X GET 'https://api.parse.bot/scraper/0fe6f91d-921f-48a4-854b-3dd214680064/list_jobs?sort=latest&category=technology-programming' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace peopleperhour-com-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""Walkthrough: PeoplePerHour freelance jobs — browse, search, and drill into details."""
from parse_apis.peopleperhour_com_api import PeoplePerHour, Sort, Category, JobNotFound
client = PeoplePerHour()
# Browse the latest tech-programming jobs, capped at 5 total items.
for job in client.job_summaries.list(category=Category.TECHNOLOGY_PROGRAMMING, sort=Sort.LATEST, limit=5):
print(job.title, job.price, job.proposals)
# Search by keyword and drill into the first match for full details.
hit = client.job_summaries.list(query="React Native", limit=1).first()
if hit is not None:
try:
detail = hit.details()
print(detail.title)
print(detail.description[:200])
print("Skills:", detail.skills)
print("Posted:", detail.date_posted, "Expires:", detail.valid_through)
print("Location:", detail.location_type, "Country:", detail.applicant_country)
except JobNotFound:
print("Job", hit.job_id, "is no longer available")
print("exercised: job_summaries.list / details")
Returns one page (20 job cards) of public freelance job postings from PeoplePerHour's job board, optionally restricted to a category, filtered by a keyword search and ordered by a sort option (default: latest first). Each record is one job card as shown on the listing page: title, snippet description, displayed price (fixed budget such as $67 or hourly such as $50/hr), buyer name/profile link, relative posting age, proposal count, location label, expiry label when the site shows one, and promotional tags (e.g. 'opportunity', 'urgent'). One round trip per call. Pagination is caller-controlled via `page` (1-based; omitted = page 1); `total` and `last_page` come from the site, `has_more` is derived from total and the 20-per-page size. A keyword with no matches returns an empty `jobs` array with total 0. Each job's `job_url` can be passed unchanged to get_job.
| Param | Type | Description |
|---|---|---|
| page | integer | 1-based result page number; 20 jobs per page. |
| sort | string | Ordering of results as offered by the site's sort dropdown. |
| query | string | Free-text keyword search over job postings (e.g. 'React Native'). Words are matched as the site's own search does; omitted = no keyword filter. |
| category | string | Job category or sub-category code from the site's category picker. Omitted = all categories. |
{
"type": "object",
"fields": {
"jobs": "array of job cards; each has job_id (numeric string), title, job_url (input for get_job), description (snippet text), price (display string, fixed or /hr), buyer_name, buyer_url, posted (relative age text), proposals (integer or null), location (label such as Remote, or null), expires (label when shown, else null), tags (array of promo labels)",
"page": "integer page returned",
"total": "integer total number of matching jobs reported by the site",
"has_more": "boolean, true when further pages exist",
"per_page": "integer page size (20)",
"last_page": "integer highest page number linked from this page"
},
"sample": {
"data": {
"jobs": [
{
"tags": [],
"price": "$67",
"title": "Squarespace editor freeze + navigation flash",
"job_id": "4519410",
"posted": "an hour ago",
"expires": null,
"job_url": "https://www.peopleperhour.com/freelance-jobs/technology-programming/programming-coding/squarespace-editor-freeze-navigation-flash-4519410",
"location": "Remote",
"buyer_url": "https://www.peopleperhour.com/freelancer/peter-xxxx",
"proposals": 21,
"buyer_name": "Peter A.",
"description": "Issue: The Squarespace page editor freezes/becomes unresponsive when editing site pages..."
}
],
"page": 1,
"total": 142,
"has_more": true,
"per_page": 20,
"last_page": 8
},
"status": "success"
}
}About the PeoplePerHour API
Browsing and Searching Jobs
The list_jobs endpoint accepts four optional parameters: page (1-based), sort for ordering results, query for free-text keyword search (e.g. 'React Native'), and category to scope results to a specific PeoplePerHour category or sub-category code. Each call returns up to 20 job cards alongside pagination metadata: total (total matching jobs), has_more, per_page, page, and last_page. This makes it straightforward to walk through all pages of results for a given search.
Job Detail Fields
Passing a job_url returned from list_jobs to get_job yields the complete posting. The response includes the full description text, skills as an array of strings, date_posted and valid_through as YYYY-MM-DD HH:MM:SS timestamps in site local time, employment_type and location_type using schema.org vocabulary (e.g. CONTRACTOR, TELECOMMUTE), and applicant_country which is either a country name string or null when the client has placed no geographic restriction on applicants.
Coverage and Scope
Both endpoints operate on publicly visible job postings. The list_jobs response reflects the same job cards a visitor sees when browsing the PeoplePerHour job board. Filtering combines keyword and category together, matching the site's own search behavior. The get_job endpoint does not require a separate lookup step — the job_url field from any list result is the direct input.
The PeoplePerHour API is a managed, monitored endpoint for peopleperhour.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when peopleperhour.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official peopleperhour.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Aggregate PeoplePerHour postings by category to track demand for specific freelance skill sets over time
- Monitor new
React Nativeor other keyword-matched jobs as they are posted using thesort=latestorder - Extract the
skillsarray fromget_jobto build a frequency map of in-demand technologies on the platform - Filter jobs by
applicant_countryfield to surface location-restricted opportunities for a target market - Pipe
date_postedandvalid_throughtimestamps into a pipeline that flags postings nearing expiry - Cross-reference
employment_typeandlocation_typefields to identify remote contract work across categories - Build a job alert system that pages through
list_jobsresults and notifies users when matching titles appear
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does PeoplePerHour have an official developer API?+
What does `list_jobs` return for each job card, and how do I filter results?+
list_jobs returns up to 20 job cards per page, each with a job_id, title, description snippet, price display value, and job_url. You can narrow results with the query parameter for keyword matching, the category parameter for a specific PeoplePerHour category code, and sort to control ordering. The response also includes total, has_more, last_page, and per_page for pagination.What geographic and employment-type data is available in `get_job`?+
get_job returns location_type using schema.org vocabulary (e.g. TELECOMMUTE) and applicant_country, which is a country name string when the poster has restricted applications by geography, or null when unrestricted. employment_type uses schema.org values such as CONTRACTOR.Does the API expose freelancer profiles, bids on a job, or hourly rates for freelancers?+
How fresh is the job listing data, and is there a limit on how far back results go?+
sort parameter. The valid_through timestamp on each full job record from get_job indicates the posting's expiry date. The API does not expose an archive of expired or closed postings — only active, publicly listed jobs are accessible.