Reed.co.uk API & Scraper
The Reed.co.uk API returns UK job listings as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary search endpoint returns jobs with id, title, URL, company, location, county, job type and full/part-time flags, and you can pull a detail, a company's jobs and a sector. It is built for recruiting tools, UK labor-market analysts and job boards that need Reed data without a scraper. One ReefAPI key, one shared credit pool, the standard envelope.
Reed's salary, date and id fields, and where each one is actually filled
Reed is UK-only and its pay data is genuinely structured, not a snippet you have to regex. The catch is that the search action and the detail action fill different fields, rename two of them, and format the same salary string differently. Measured on 'python developer' in London (512 jobs across 21 pages), a contract sweep that returned per-day and per-hour rows, job 57258187 in detail, and the it, accountancy and healthcare sectors.
| Field | Measured shape | Where it comes from |
|---|---|---|
| job_id | 8-digit integer: 57258187. It is the last path segment of reed.co.uk/jobs/<title-slug>/<id>. | search, sector and company-jobs rows. The detail action takes it as job_id, or accepts the full URL. |
| salary_from / salary_to | Numbers, not strings, and not always integers: an hourly role came back as 31.87. | Both actions. currency was GBP on every row we sampled. |
| salary_period | 'per annum', 'per day' or 'per hour' | Both actions. In one contract sweep, 20 of 25 rows were per day. |
| salary_from_per_annum / salary_to_per_annum | Reed's own annualized figures, detail only | GBP 450-600 per day became 117000-156000, a 260-day year. GBP 31.87 per hour became 62146.5, which is 1950 hours. This is the only way to compare a day rate with a salary. |
| salary_description | Display string. Search: '£450 - £600 per day'. Detail: '£ 450.00 - £ 600.00 per day'. | The spacing and decimals differ between the two actions, so do not use it as a join key. |
| posted_date / expiry_date | ISO-8601 with milliseconds and no timezone: '2026-08-20T09:24:11.977'. Expiry always lands at 23:55:00. | Both actions. Detail adds created_date and updated_date, which drift from posted_date by milliseconds and by days respectively. |
| remote_option | 'Remote', 'Hybrid', 'On-Site' or 'NotSpecified' -- it is a string, never null | search. Detail renames it remote_type, lowercases the value ('hybrid'), and adds a separate is_remote boolean. |
| description_text | null on every search row we pulled | Only the detail action fills it: 2,929 characters of plain text for job 57258187, alongside 3,737 characters of description_html. |
| sector | Two levels, named differently per action | search gives sector_top 'Developer' plus sector 'Python Developer'. Detail gives sector 'Software Developer' (sector_id 58) under parent_sector 'IT & Telecoms' (parent_sector_id 52), plus a taxonomy path string. |
| company_slug | Two shapes: lowercase 'mcgregor-boyall-associates-limited', and Title-Case with a trailing number 'Tenth-Revolution-Group-91770' | That trailing number is not the employer id you get in detail: the same job reported company_profile_id 72466, not 91770. |
| pagination | 25 jobs per page, page 1 to 200 | meta.pagination carries page, total_pages, has_more and next_page. 'python developer' in London: total 512 across 21 pages. Sector 'it': 6,952 jobs across 279 pages. |
An unrecognized sector slug is not an error. sector='zzzz' returned ok:true with total 0 and an empty jobs array, and an unknown company behaves the same way. Check total rather than trusting the 200.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/reed/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"keywords": "python developer",
"location": "London"
}
}{
"ok": true,
"meta": {
"api": "reed",
"endpoint": "search",
"mode": "live",
"latency_ms": 1355.2,
"record_count": 25,
"bytes": 410832,
"cache_hit": false,
"completeness_pct": 100,
"stop_reason": "limit_reached",
"pagination": {
"page": 1,
"total_pages": 20,
"has_more": true,
"next_page": 2
},
"charged_credits": 1,
"version": "1.0.0"
},
"data": {
"jobs": [
{
"job_id": 57363584,
"title": "AWS/Python Software Engineer",
"url": "https://www.reed.co.uk/jobs/aws-python-software-engineer/57363584",
"company": "E.ON",
"company_slug": "e-on-2345",
"location": "London",
"county": "London",
"job_type": "Permanent",
"is_full_time": true,
"is_part_time": false,
"remote_option": null,
"posted_date": "2026-09-18T13:29:28.17",
"expiry_date": "2026-10-02T23:55:00",
"is_easy_apply": false,
"is_promoted": false,
"sector_top": "Engineer",
"sector": "Software Engineer",
"external_url": null,
"description_text": null,
"salary_from": 70000,
"salary_to": 900000,
"salary_period": "per annum",
"salary_description": "£70,000 - £900,000 per annum",
"currency": "GBP"
},
{
"job_id": 57355173,
"title": "Python Developer",
"url": "https://www.reed.co.uk/jobs/python-developer/57355173",
"company": "Pontoon",
"company_slug": "pontoon-45427",
"location": "London",
"county": "London",
"job_type": "Temporary",
"is_full_time": true,
"is_part_time": false,
"remote_option": null,
"posted_date": "2026-09-16T16:47:35.137",
"expiry_date": "2026-09-30T23:55:00",
"is_easy_apply": true,
"is_promoted": false,
"sector_top": "Developer",
"sector": "Python Developer",
"external_url": null,
"description_text": null,
"salary_from": 600,
"salary_to": 600,
"salary_period": "per day",
"salary_description": "£600 per day",
"currency": "GBP"
},
{
"job_id": 57280155,
"title": "Python Developer",
"url": "https://www.reed.co.uk/jobs/python-developer/57280155",
"company": "McGregor Boyall",
"company_slug": "mcgregor-boyall-associates-limited",
"location": "London",
"county": "London",
"job_type": "Permanent",
"is_full_time": true,
"is_part_time": false,
"remote_option": null,
"posted_date": "2026-08-26T14:59:12.087",
"expiry_date": "2026-10-07T23:55:00",
"is_easy_apply": true,
"is_promoted": false,
"sector_top": "Developer",
"sector": "Python Developer",
"external_url": null,
"description_text": null,
"salary_from": 125000,
"salary_to": 160000,
"salary_period": "per annum",
"salary_description": "£125,000 - £160,000 per annum",
"currency": "GBP"
}
],
"total": 483
}
}What the Reed.co.uk API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| search | Search Reed.co.uk job postings by keyword with optional location, distance, salary range, job-type, date-posted, remote and sort filters. Returns up to 25 jobs per page with title, company, salary, location, dates and a plain-text description. | Recruiting teams call search to search Reed.co.uk job postings by keyword with optional location, distance, salary range, job…. | keywords, location, distance, job_type, full_time, ... |
| detail | Full posting for one Reed job by job_id (or a Reed job URL): title, company, salary (range + display), location breakdown (town/county/region/country/postcode/remote), full HTML + plain-text description, contract type, sector, eligibility, dates and breadcrumbs — plus related jobs. | Labor-market analysts call detail to get full posting for one Reed job by job_id (or a Reed job URL). | job_id, url, include_similar |
| company-jobs | All public job postings at a specific company on Reed. Pass the company's Reed profile slug, full company URL, or its name; optionally narrow with the same keyword/location/salary/job-type/date filters as search. | Job boards call company-jobs to get all public job postings at a specific company on Reed. | company, keywords, location, distance, job_type, ... |
| sector | Browse Reed jobs by sector/category (e.g. 'it', 'accountancy', 'healthcare', 'engineering', 'sales', 'marketing'). Optionally scope by location and the same salary/job-type/date/remote filters as search. | Sales intelligence teams call sector to get browse Reed jobs by sector/category (e.g. | sector, location, distance, job_type, salary_from, ... |
Call search from your stack
curl -X POST https://api.reefapi.com/reed/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"keywords":"python developer","location":"London"}'import requests
r = requests.post(
"https://api.reefapi.com/reed/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"keywords": "python developer",
"location": "London"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/reed/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"keywords": "python developer",
"location": "London"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.reed.search with {"keywords":"python developer","location":"London"}.Who uses this API and why
- UK job boards call search to aggregate fresh Reed listings by role and location.
- Labor-market analysts use sector and search to measure hiring demand across the UK.
- Recruiting tools use company-jobs to track a specific employer's open roles.
Questions developers ask before integrating
Is Reed's salary structured or a free-text string?
Structured, and this is the main reason to use Reed rather than a scraped board. Every priced row carries numeric salary_from and salary_to, a currency of GBP, and a salary_period of 'per annum', 'per day' or 'per hour'. salary_description is a separate human-readable string ('£450 - £600 per day') that you can display but should not parse.
How do I compare a contract day rate with a permanent salary?
Use the detail action, which adds salary_from_per_annum and salary_to_per_annum. We measured the conversion: a £450-£600 per day contract became 117000-156000, which is a 260-day working year, and a £31.87 per hour role became 62146.5, which is 1950 hours. Search results do not carry those fields, only the raw figure and the period.
Why is description_text null in search results?
Reed's listing payload does not include the body, so search, sector and company-jobs all return description_text as null. Call detail with the job_id for the postings you care about and you get both description_text and description_html. For job 57258187 that was 2,929 characters of text and 3,737 of HTML, the HTML being simple paragraph and list markup.
What timezone are posted_date and expiry_date in?
There is no offset in the string at all: you get '2026-08-20T09:24:11.977'. Reed is a UK-only board, so treat these as UK local time and be careful in summer, when that is UTC+1. expiry_date is always set to 23:55:00 on the closing day, which makes it a date rather than a real deadline time.
Can I feed company_slug from a search result into the company-jobs action?
Not blindly. In our tests plain lowercase slugs resolved ('lhh-recruitment-solutions' returned 12 jobs, 'fdm-group' 97), but slugs ending in a number returned ok:true with total 0 and a null company block, and one of them returned a different employer's listings altogether. Check that data.company.name matches what you asked for, or search the employer name instead, which returned 26 jobs for 'McGregor Boyall'.
What sector slugs does the sector action take?
Reed's own category slugs. 'it' returned 6,952 jobs, 'accountancy' 4,931 and 'healthcare' 9,954, and 'engineering', 'sales', 'marketing', 'education', 'construction', 'hospitality' and 'legal' follow the same pattern. A slug Reed does not recognize returns ok:true with total 0 rather than an error, so always look at total before deciding a sector is empty.
Which field tells me whether a job is permanent, contract or temporary?
Use the detail action's contract_type together with the is_permanent, is_temporary and is_contract booleans. The search action's job_type field is not dependable: filtering job_type='permanent' labeled rows 'Permanent', filtering 'contract' labeled comparable rows 'Temporary', and filtering 'temporary' returned the raw code 4 instead of a label. The filters themselves work; it is the returned label that is inconsistent.
How deep can I page, and how many results per page?
25 jobs per page, and the page parameter accepts 1 to 200, so 5,000 rows per query is the practical ceiling. meta.pagination.total_pages tells you where to stop: 21 pages for 'python developer' in London, 279 for the whole IT sector. Reed caps its own result sets, so loop against total_pages rather than dividing total by 25 yourself.
What is the Reed.co.uk API?
Reed.co.uk API is a ReefAPI endpoint group for reed.co.uk It returns live JSON through POST requests under /reed/v1.
Is the Reed.co.uk API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Reed.co.uk calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Reed.co.uk login or account?
No login to Reed.co.uk is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Reed.co.uk data?
The page example is captured from a live search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Reed.co.uk API use?
Reed.co.uk actions currently cost 1 credit per successful call. Failed or blocked calls are free. All APIs draw from one credit pool.
Can I call Reed.co.uk from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call reed actions with the same key, credit pool and JSON envelope used by normal REST requests.