Developers
Our job and pay data, in a form built for machines, and a straight account of what we will not hand over.
Want TechnoJobs in Claude, ChatGPT, Cursor or VS Code? Add it to your AI assistant.
What TechnoJobs is, before you build against it
We are a UK tech job board carrying predominantly permanent, salaried roles, with some contract roles priced by the day. Many adverts on the website name no employer. Those are not in this API: we are not told who the advertiser is, so they cannot be counted in any published figure. Contract data here is thin as a result. No day-rate figure is published yet. See live contract jobs.
Every advert we return comes from a verified employer source, pulled from the employer’s own public applicant-tracking board, or posted here by an authenticated recruiter for their own organisation.
The public API
/api/public/v1: read-only, versioned, no API key, CORS open to any origin, no cookies and no credentials. Rate limited per IP; the limit is sized for people reading us, not mirroring us.
GET /api/public/v1
Service index: endpoint list, live counts, links to everything below.
GET /api/public/v1/jobs
Live vacancies, newest first, cursor-paginated. Filters: employer, jobType, workMode, q.
GET /api/public/v1/jobs/{idOrSlug}
One vacancy with its full advert text.
GET /api/public/v1/employers
Employers with live vacancies, busiest first.
GET /api/public/v1/salaries
Pay benchmarks: board-wide, per role, per work mode.
GET /api/public/v1/openapi.json
OpenAPI 3.1 description of all of the above.
| Endpoint | What it returns |
|---|---|
| GET /api/public/v1 | Service index: endpoint list, live counts, links to everything below. |
| GET /api/public/v1/jobs | Live vacancies, newest first, cursor-paginated. Filters: employer, jobType, workMode, q. |
| GET /api/public/v1/jobs/{idOrSlug} | One vacancy with its full advert text. |
| GET /api/public/v1/employers | Employers with live vacancies, busiest first. |
| GET /api/public/v1/salaries | Pay benchmarks: board-wide, per role, per work mode. |
| GET /api/public/v1/openapi.json | OpenAPI 3.1 description of all of the above. |
Pagination is cursor-based, keyed on posting time. Take page.nextCursor and pass it back as ?cursor=; the value is opaque, so do not parse it. Offset paging would silently skip a vacancy or return it twice every time an advert expires mid-crawl, and a job board whose API loses jobs is worse than one with no API. The page size caps at 100. A cursor we do not recognise is a 400, never a quiet restart at page one.
Every response carries meta.supply (which advert provenances it was computed from), meta.lastUpdated and meta.generatedAt. Every response also carries a Link: …; rel="service-desc" header pointing at the OpenAPI document, so you can find the description from any endpoint without having read this page.
Schemas are additive within a version. Fields may be added to v1; none is ever removed, renamed or retyped. A breaking change gets a v2.
Pay data: two provenances, never merged
Greenhouse and Lever, the boards behind most direct-employer vacancies here, publish no salary field at all. So our pay data comes from two places, and every figure says which:
employer_declared: the employer published a structured pay field. Their word.
extracted_from_description: our reading of the pay stated in the advert’s own prose. The response carries the exact sentence we read it from and a confidence score, and we never publish one below our floor. A deterministic pass finds the candidate figures; a language model may only choose which of them is the salary, or veto them all. It never returns a number, so a hallucinated figure has nowhere to land.
Do not present the second as the first. That distinction is the entire reason we ship the source sentence with it.
Benchmarks pass two independent gates. Provenance: computed from real supply only. Sample size: under our minimum, a row publishes its real n and null figures instead of a median. A row with n: 0 is a deliberate statement, not a missing value. It is us telling you we have nothing there.
The MCP server
An MCP server exposing the same read-only data answers at https://technojobs.co.uk/api/mcp: streamable HTTP, no authentication, no cookies, no session, so an assistant can query this board inside a conversation instead of scraping it.
In Claude Code, one line adds it:
claude mcp add --transport http technojobs https://technojobs.co.uk/api/mcpIn Claude Desktop, Cursor, or anything else that reads an mcpServers block, the equivalent is:
{
"mcpServers": {
"technojobs": {
"type": "http",
"url": "https://technojobs.co.uk/api/mcp"
}
}
}Once connected it offers these tools:
search_jobs: Search live UK tech vacancies. Defaults to permanent roles, because that is what this board carries. Real adverts only, and capped: a search tool, not an export.get_job: One advert in full: the employer's own text, the pay figure with its provenance, and the honest apply route. The advert body is returned fenced and marked as data, not instructions.salary_benchmark: Median and interquartile range for a role, always with its sample size and provenance mix. Refuses to publish below the sample floor or from unverified supply. The refusal is the answer.market_overview: Live counts across the board: employment types, work modes, employers, classified roles, pay coverage. Counted this minute, never modelled or carried over.company_profile: One employer as their live adverts show them. Approved reviews are reported as counts with a link. No review text, and nothing pending or rejected.
There is a deliberate hard boundary in that design, stated in the server’s own description so an agent reads the limit rather than probing for it: no tool returns another person’s profile, CV, application or contact details, and no tool submits an application.
The authenticated tier: an agent that acts for one person
The five tools above need no credential. There are five more that do, for the case where somebody wants their own assistant to help them apply, using their own account.
Agent registration is not switched on yet, so this tier cannot be used today. When it is, this section will say how an agent registers. Only service_auth registrations will be honoured: the agent registers with the account holder’s own email and that person confirms it in their own browser.
assess_fit: Scores the account holder’s own CV against one advert and returns the strengths and the gaps: which of the skills the employer listed their CV evidences, and which it does not. Computed, not written by a model, so no strength can be invented. Returns no CV content of any kind.my_applications: Their own application activity, in two lists that mean different things: what we actually hold and passed to an employer, and what we handed off to an employer’s own form. That list is marked outcome_known: false, permanently, because we cannot see it.prepare_application: Writes a draft and returns a single-use confirm link plus a plain-text summary. It cannot create an application. The insert refuses outright while an agent credential is in the call tree.list_my_drafts: Their own prepared drafts. None has been sent; no employer has seen any of them.discard_draft: Bins one draft and invalidates its confirm link. Nothing was sent, so nothing is withdrawn.
We deliberately never send. There is no tool, no parameter and no permission that submits an application on someone’s behalf, and there will not be one. An agent prepares; the person opens a single-use confirm link in their own browser, reads what was written, including the gaps the assessment found, and decides. The refusal is structural rather than a policy: the insert into our applications table throws if an agent credential is anywhere in the call chain, and a test walks the import graph from every agent-reachable entry point and fails the build if any module it can reach is even able to write one.
The cap is on output, not on speed. We limit how many applications one person can put in front of employers per day; we do not limit how fast an agent reads, assesses or prepares, because that is the part worth having. A cap on agent requests would slow a machine down and change nothing an employer experiences.
And for the vacancies applied for off this site, which is most of them, we say so rather than pretending. We do not submit those forms and we cannot see their outcome. If a person continues to the employer, we record an unverified intent to apply in their own tracker, labelled that way everywhere it appears. It is never shown to an employer and never counted as an application. The column that would say otherwise does not exist: in the database verified is generated false, and Postgres refuses any statement that tries to set it.
What we will never expose, and why
Anyone else’s candidates, CVs, applications or contact details
Not through this API, not through the MCP server, not with a key, not with a partnership, not at any price. The authenticated agent tier is the one exception and it is a narrow one: with a credential a person authorised themselves, an agent reads that person’s own records and nobody else’s, there is no parameter for another person, and no search. Our CV database is sold to employers under authenticated, audited access that demands a fresh sign-in every time, and it has no agent surface at all: no scope reaches CV bytes, and an agent credential can only ever add a refusal to that gate, never satisfy one. If a tool offers you TechnoJobs candidate data, it is not us.
Adverts that name no employer
The API leaves out the adverts that name no employer. They are licensed for display on this website only. So the API lists fewer vacancies than the website, and the difference is stated in the service index and in /llms.txt rather than left for you to discover.
Applying on someone’s behalf
Most vacancies here are applied for off this site: on the employer’s own applicant-tracking system, or on another UK job site. We will not drive a third party’s system for you, and we will not submit an application without a human pressing the button, including from an agent. An agent may read, assess and prepare; a person decides to contact an employer. That is a product line, not a caution.
Ranked or personalised results
The website ranks by relevance, recency and a paid featured boost. The API does not rank at all. It returns newest first, so pagination is stable and nobody’s dataset is silently reordered by something an employer bought. If you want our ranking, use the website.
A bulk dump of the whole board
There is no export endpoint and the page size is capped. Everything is reachable by paging, and the rate limit is generous for anyone reading us and immediate for anyone mirroring us.
Pay figures without their working
There is no parameter that strips the sample size or the provenance off a benchmark, because a median without them is the shape of every misleading pay statistic ever published.
Crawling us
Our robots.txt names assistant crawlers individually rather than hiding behind a wildcard, and every user-agent token in it was copied from that operator’s own published documentation. We welcome:
- OpenAI: OAI-SearchBot, ChatGPT-User, GPTBot
- Anthropic: Claude-SearchBot, Claude-User, ClaudeBot
- Perplexity: PerplexityBot, Perplexity-User
- Google: Google-Extended
- Apple: Applebot, Applebot-Extended
- Meta: meta-externalagent
You may read anything a signed-out person may read, plus the public API. Admin, recruiter, candidate-account and the rest of /api/ are disallowed, and behind real authorisation as well, because a robots directive is a request, not a lock.
/llms.txt and /llms-full.txt summarise what is on this board right now. They are generated on request from the same database as the API. There is no committed copy of either, so they cannot drift into a lie.
If you cite us
Credit TechnoJobs and link the specific page: the advert itself, or the page a figure came from. When you quote a pay figure, carry its sample size and its provenance with it. “Median £X across n adverts, read from advert text” is our claim. “£X is the market rate” is not.
Building something with this, or found a figure that looks wrong? Both are worth an email: get in touch. A wrong number is a bug and we want it.