Education hiring and jobs-market research
Use Tes endpoints to turn education hiring and jobs-market research into repeatable API requests with documented inputs and JSON responses.
Turn Tes's public education marketplace into structured JSON — teaching-job search with the site's own live faceted breakdown, job and employer detail, teaching-resource search and detail with attachment metadata and reviews, author shops, and the Schools Directory, whose ids resolve straight to a full employer profile. Credential-free.
Search Tes teaching jobs, the teaching-resources marketplace, and the Schools Directory, and get normalized job, resource, employer, and school facts as JSON.
Endpoint families
3
Documented params
37
Examples
7
Live catalog snapshot
Active endpoints
7
Methods
GET
Required params
12
Schema refs
7
{
"platform": "Tes",
"endpoint": "tes-job-search",
"method": "GET",
"path": "/tes/jobs/search",
"auth": "apiKey"
}Use cases
Search Tes teaching jobs, the teaching-resources marketplace, and the Schools Directory, and get normalized job, resource, employer, and school facts as JSON.
Use Tes endpoints to turn education hiring and jobs-market research into repeatable API requests with documented inputs and JSON responses.
Use Tes endpoints to turn teaching-resource marketplace analysis into repeatable API requests with documented inputs and JSON responses.
Use Tes endpoints to turn school directory and employer research into repeatable API requests with documented inputs and JSON responses.
Managed execution
Every figure below is read from the live Tes endpoint catalog — 7 endpoints, 37 documented request parameters, and 7 published response schemas — the same catalog Docs and Playground run against.
7 documented Tes endpoints, grouped into 3 request families — Jobs, Resources and Schools.
37 request parameters are documented across those Tes endpoints, 12 of them required — the full input contract is public before you write any integration code.
7 of the 7 Tes endpoints ship a recorded example response, and 7 carry a documented response schema — you can code against the real JSON before the first request.
Tes endpoints document their error responses (400, 404, 429 and 503) alongside the success schema, so a block, a rate limit, or a missing record comes back as a typed error rather than silently empty data.
7 hosted MCP tools back the Tes endpoints, so an agent can call the same routes with the same parameters and the same JSON contract, with no custom glue.
Coverage map
These cards are generated from the active endpoint catalog, so the landing page reflects the same API surface used by Docs and Playground.
/tes/jobs/search
/tes/resources/search
/tes/schools/search
Endpoint catalog
/tes/jobs/searchSearches Tes's (tes.com) teaching-jobs board. Returns normalized listing facts (title, employer, location, salary, contract terms/types) plus a short excerpt of the listing description, not the full long-form job-description copy, and the same live faceted-search breakdown (position/subject/workplace category trees with counts, plus contract type/term counts) the real search page renders as its filter sidebar. location is a free-text place name (a UK town/city, an international city, or a bare country name) resolved to coordinates via Tes's own location-autocomplete endpoint; omit for Tes's own default market, "United Kingdom". radius_miles selects the search radius around location. contract_type and contract_term are validated against Tes's own small, closed label sets. position, subject, and workplace are comma-separated passthrough filters -- Tes's own category labels are numerous and can change, so read a prior response's own facets.positions[].value (and facets.positions[].children[].value)/facets.subjects[].value/facets.workplaces[].children[].value for the live, current set rather than guessing. salary_min filters to jobs with an advertised salary at or above that amount (in the searched market's local currency); Tes's own filter panel offers only a minimum, no maximum.
Response notes
- `total` is the upstream's true result count across all pages, not just the current page's length. - `location` in the response is the resolved place name actually searched (Tes's own canonical name for the match, which may differ slightly from what was passed in). - `facets` is the same live faceted-search breakdown Tes's own search page renders as its filter sidebar: `positions`, `subjects`, and `workplaces` are category trees (a `children` array nests subcategories), while `contract_types` and `contract_terms` are flat. Every facet entry carries its own live `count` under the current query and filters already applied -- use these to discover valid `position`/`subject`/`workplace` values rather than guessing. - An unrecognized `position`/`subject`/`workplace` value narrows to zero results rather than erroring, matching Tes's own upstream behavior for an unknown filter. - `employer_logo_url` is empty when the employer hasn't uploaded a logo. Example response: ```json { "code": 200, "msg": "OK", "data": { "keywords": "teacher", "location": "United Kingdom", "page": 1, "page_size": 20, "total": 1504, "jobs": [ { "id": "2340998", "title": "Minibus Driver", "url": "https://www.tes.com/jobs/vacancy/minibus-driver-epsom-2340998", "employer": "Epsom College", "employer_logo_url": "https://l.imgt.es/employer-assets/employers/1039424/brandimages/20181030_1126AM_desktop_1039424_logo.jpg", "location": "Epsom", "salary": "£11,956 per year", "contract_terms": ["Permanent"], "contract_types": ["Part Time"], "short_description": "Epsom College is an independent co educational day and boarding school...", "closing_date": "2026-09-29T23:00:00.000Z" } ], "facets": { "positions": [ { "value": "Teaching and Lecturing", "count": 713, "children": [ {"value": "Cover Supervisor", "count": 120} ] } ], "subjects": [ {"value": "Mathematics", "count": 200} ], "workplaces": [ { "value": "Workplaces", "count": 1000, "children": [ {"value": "Secondary", "count": 400} ] } ], "contract_types": [ {"value": "Full Time", "count": 1036}, {"value": "Part Time", "count": 577} ], "contract_terms": [ {"value": "Permanent", "count": 1192}, {"value": "Fixed Term", "count": 161} ] } } } ```
MCP tool tes_job_search
/tes/jobs/detailReturns normalized detail for one job posting by its numeric id (the id field returned by tes-job-search): employer, location, salary, contract terms/types, dates, and application contact/URL, plus a short excerpt of the listing description rather than the full long-form HTML copy.
Response notes
- An unknown or no-longer-listed job id returns `404`. - `description_excerpt` is plain text with HTML tags stripped and truncated to roughly 600 characters, not the full listing copy. - `employer_url`'s trailing numeric id (e.g. `1039424` in `.../jobs/employer/epsom-college-1039424`) is the `id` [tes-job-employer](tes-job-employer.md) accepts. - `employer_logo_url` and `employer_header_url` are empty when the employer hasn't uploaded that image. Example response: ```json { "code": 200, "msg": "OK", "data": { "id": "2340998", "title": "Minibus Driver", "url": "https://www.tes.com/jobs/vacancy/minibus-driver-epsom-2340998", "employer": "Epsom College", "employer_url": "https://www.tes.com/jobs/employer/epsom-college-1039424", "employer_website": "http://www.epsomcollege.org.uk", "employer_logo_url": "https://l.imgt.es/employer-assets/employers/1039424/brandimages/20181030_1126AM_desktop_1039424_logo.jpg", "employer_header_url": "https://l.imgt.es/employer-assets/employers/1039424/brandimages/20160908_1226PM_desktop_1039424_header.jpg", "location": "Epsom", "salary": "£11,956 per year", "contract_terms": ["Permanent"], "contract_types": ["Part Time"], "description_excerpt": "Epsom College is an independent co educational day and boarding school with approximately 1100 pupils...", "posted_date": "2026-08-31T23:00:00.000Z", "closing_date": "2026-09-29T23:00:00.000Z", "application_url": "https://www.epsomcollege.org.uk/vacancies/minibus-driver/", "application_email": "hr.recruitment@epsomcollege.org.uk", "quick_apply": false } } ```
MCP tool tes_job_detail
/tes/jobs/employerReturns normalized detail for one jobs employer profile by its numeric id (the trailing id in tes-job-detail's employer_url field, e.g. .../jobs/employer/epsom-college-1039424 -- the 1039424): name, location, school type/phase/funding status/gender/age range, an about description, its postal address, and every currently-open position listed on the employer's own profile page (id, title, url -- call tes-job-detail with each id for the full posting).
Response notes
- An unknown employer id returns `404`. - `positions` lists every currently-open posting shown on the employer's own profile page (`id`, `title`, `url` only) -- call [tes-job-detail](tes-job-detail.md) with each `id` for the full posting. Example response: ```json { "code": 200, "msg": "OK", "data": { "id": "1039424", "name": "Epsom College", "url": "https://www.tes.com/jobs/employer/x-1039424", "location": "Surrey, United Kingdom", "type": "Mainstream School", "phase": "Secondary with sixth form", "funding_status": "Independent", "gender": "Mixed", "age_range": "11 - 18 years", "description": "The College has grown significantly in the past seven years...", "address": "College Road, Epsom, Surrey, KT17 4JQ, United Kingdom", "positions": [ { "id": "2340068", "title": "Senior IT Technician", "url": "https://www.tes.com/jobs/vacancy/senior-it-technician-epsom-2340068" } ] } } ```
MCP tool tes_job_employer
/tes/schools/searchSearches Tes's (tes.com) public Schools Directory by school name or location. Returns normalized listing facts (name, logo, a short description, address) for each matching school/employer. Each result's id is the same employer id tes-job-employer accepts, so a caller can go straight from a name/location search to a full employer profile (school type/phase/funding status/gender/age range, and its currently-open positions) without first needing a job posting to discover the id.
Response notes
- `total`/`total_pages` are the upstream's true counts across all pages, not just the current page's length. - A query with no matches returns a well-formed empty `schools` array, not an error. - `address` joins whichever address lines the upstream provides (not every school has all three); it can be empty for a school with no address on file. Example response: ```json { "code": 200, "msg": "OK", "data": { "query": "Epsom", "page": 1, "page_size": 20, "total_pages": 1, "total": 6, "schools": [ { "id": "1039424", "name": "Epsom College", "url": "https://www.tes.com/jobs/employer/epsom-college-1039424", "logo_url": "https://d2yw9izrmawh7r.cloudfront.net/employers/1039424/brandimages/20181030_1126AM_desktop_1039424_logo.jpg", "description": "The College has grown significantly in the past seven years.", "address": "College Road", "country": "United Kingdom", "post_code": "KT17 4JQ" } ] } } ```
MCP tool tes_school_search
/tes/resources/searchSearches Tes's (tes.com) teaching-resources marketplace. Returns normalized listing facts (title, author, price, rating, downloads) -- not full listing descriptions -- out of respect for Tes's general reproduction/republication restriction. query is optional: omit it (alone, or combined with key_stage/subject/on_sale) for pure filter-driven or fully unfiltered browsing, matching Tes's own search API. sort mirrors the real search page's own Sort by dropdown; key_stage and subject mirror its left-hand Refine by filters (both closed, validated enums taken from Tes's own facet taxonomy, and always resolved against Tes's own single GB-taxonomy regardless of country). country controls result currency/localisation only (confirmed live for all seven values); it does not change which key_stage/subject values are valid.
Response notes
- `total` is the upstream's true result count across all pages, not just the current page's length. - `price` is `0` and `free` is `true` for free resources. - `resource_types` and `file_types` reflect Tes's own resource-type/file-type taxonomy and may be empty. - `is_bundle` marks a listing that packages multiple resources under one price (Tes's own "Bundle" badge); `bundle_resource_count` and `bundle_original_price` are only meaningful when `is_bundle` is `true`. - `license` is Tes's own short licence code: one of `Paid`, `CC-BY-SA`, `CC-BY-ND`, `CC-BY`. Example response: ```json { "code": 200, "msg": "OK", "data": { "query": "phonics", "country": "GB", "page": 1, "page_size": 20, "total": 38150, "resources": [ { "id": "13463100", "title": "Phonics Bunting", "url": "https://www.tes.com/teaching-resource/phonics-bunting-13463100", "author": "joannecrozierharbottle", "price": 1, "currency": "GBP", "free": false, "on_sale": false, "rating": 0, "rating_count": 0, "downloads": 143, "resource_types": ["Visual aid/Display"], "file_types": ["Image"], "thumbnail_url": "https://d1e4pidl3fu268.cloudfront.net/example/preview.png", "first_published": "2026-05-03T21:00:33.987Z", "last_published": "2026-05-03T21:00:33.987Z", "is_bundle": false, "license": "Paid" } ] } } ```
MCP tool tes_resource_search
/tes/resources/detailReturns normalized detail for one teaching resource by its numeric id (the id field returned by tes-resource-search). Descriptive facts (title, subject, age range, resource type, author, price, rating, licence label), a per-file attachment list (file type, size, and a preview thumbnail -- metadata only), and the most recent page of reviews are returned -- not the downloadable resource file itself, which robots.txt already disallows scraping regardless.
Response notes
- An unknown or no-longer-listed resource id returns `404`. - `id` resolves purely from the numeric id -- the real listing's slug does not need to be known. - `price` is `0` and `free` is `true` for free resources. - `reviews` is the most recent page of reviews (as many as the resource's own page shows, typically up to 5); `rating_count` is the true total review count across every review, not just `len(reviews)`. A resource with no reviews yet returns an empty `reviews` array. - `license` is the resource's own licence label as Tes's detail page displays it, e.g. `Tes paid licence` or `Creative Commons "Sharealike"`. - `attachments` lists each downloadable file's own type, size, and a preview thumbnail image -- metadata only; the file itself is never fetched (robots.txt already disallows `/teaching-resource/download/`). Example response: ```json { "code": 200, "msg": "OK", "data": { "id": "13463100", "title": "Phonics Bunting", "url": "https://www.tes.com/teaching-resource/x-13463100", "description": "These are A-Z alphabet bunting designed using the Little Wandle phonics scheme.", "author": "joannecrozierharbottle", "author_url": "https://www.tes.com/teaching-resources/shop/joannecrozierharbottle", "subject": "English", "age_range": "3-5", "resource_type": "Visual aid/Display", "price": 1, "currency": "GBP", "free": false, "rating": 4.2, "rating_count": 5, "thumbnail_url": "https://d1e4pidl3fu268.cloudfront.net/example/preview.png", "license": "Tes paid licence", "attachments": [ { "file_type": "png", "size": "256.71 KB", "preview_image_url": "https://d1uvxqwmcz8fl1.cloudfront.net/tes/resources/13463100/one/image?width=500" } ], "reviews": [ { "id": "1313111", "rating": 5, "comment": "Thank you, great resource", "author": "jafgthomas", "author_url": "https://www.tes.com/teaching-resources/shop/jafgthomas", "posted": "8 years ago" } ] } } ```
MCP tool tes_resource_detail
/tes/resources/shopReturns normalized detail for one teaching-resources author/seller shop by its username (from tes-resource-search/tes-resource-detail's author field, or the trailing path segment of author_url): display name, average rating, upload/view/download counts, a bio, and a page of that author's resource listing (id, title, price, thumbnail -- call tes-resource-detail with each id for subject/age-range/resource-type/rating/description). subject narrows the listing to one of the subject tabs shown on the shop's own page; these vary per author and are not a curated enum.
Response notes
- An unknown username returns `404`. - `rating`/`rating_count` are the shop's overall average and review count across every resource, not one resource's. - `uploads`/`views`/`downloads` are Tes's own abbreviated stat display (e.g. `188k+`) resolved to a whole-number lower bound, since the exact figure past the abbreviation threshold isn't shown. - `resources[].price` is `0` and `resources[].free` is `true` for free resources. Only GBP is currently supported (the shop page has no currency/market selector, unlike resource search/detail). Example response: ```json { "code": 200, "msg": "OK", "data": { "username": "jenpower16", "name": "EYFS_Mrs_P", "url": "https://www.tes.com/teaching-resources/shop/jenpower16", "avatar_url": "https://d2w4qhtqw2dbsq.cloudfront.net/profile_live/12345/large.jpg", "bio": "Welcome to my TES shop! I'm Mrs P, a Reception teacher.", "rating": 4.44, "rating_count": 10, "uploads": 459, "views": 188000, "downloads": 86000, "page": 1, "resources": [ { "id": "13305182", "title": "Reception Baseline I Can Statements & Pen Grip", "url": "https://www.tes.com/teaching-resource/reception-baseline-i-can-statements-and-pen-grip-13305182", "price": 3, "currency": "GBP", "free": false, "thumbnail_url": "https://d1e4pidl3fu268.cloudfront.net/example/one.png" } ] } } ```
MCP tool tes_resource_shop
Related APIs
Business & Market Intelligence
Turn public GitHub ecosystem signals into structured data for developer-market research, open-source intelligence, recruiting, and AI-agent workflows.
Business & Market Intelligence
Collect public TrustMRR startup marketplace and leaderboard data as normalized JSON for startup research, SaaS discovery, and market intelligence workflows.
Business & Market Intelligence
Add website and competitive intelligence signals from SimilarWeb to research pipelines.
How to scrape Tes
Crawlora's Tes endpoints return normalized public Tes data as JSON with one API key — no Tes account required. Listing facts are returned rather than full long-form copy, respecting Tes's own reproduction restrictions.
Call /tes/jobs/search for Tes's teaching-jobs board — normalized listing facts (title, employer, location, salary, contract terms and types) plus a short description excerpt and the same live faceted breakdown the real search page renders as its filter sidebar (position/subject/workplace category trees with counts, plus contract type and term counts). location is a free-text place name resolved via Tes's own autocomplete; omit it for Tes's default United Kingdom market.
Pass a numeric job id from the search results to /tes/jobs/detail for employer, location, salary, contract terms, dates, and application contact/URL. Then pass the trailing id from its employer_url to /tes/jobs/employer for that school's type, phase, funding status, gender, age range, postal address, an about description, and every currently-open position on its profile.
Call /tes/schools/search by school name or location for normalized school/employer listings. Each result's id is the same employer id /tes/jobs/employer accepts, so you can go straight from a name or location to a full employer profile without first finding a job posting.
Call /tes/resources/search for the resources marketplace — title, author, price, rating, and downloads, filterable by key_stage, subject, and on_sale, with country controlling currency and localisation only. Pass a resource id to /tes/resources/detail for its subject, age range, resource type, licence label, per-file attachment metadata, and latest reviews, or an author username to /tes/resources/shop for that seller's ratings, upload/view/download counts, bio, and listings.
FAQ
Call /tes/jobs/search for teaching jobs, /tes/resources/search for the teaching-resources marketplace, or /tes/schools/search for the Schools Directory, then the matching detail endpoint (/tes/jobs/detail, /tes/jobs/employer, /tes/resources/detail, /tes/resources/shop) for one record — all return structured JSON with one API key.
No. Job and resource endpoints return normalized listing facts plus a short description excerpt, not the full long-form copy, out of respect for Tes's reproduction restrictions. /tes/resources/detail returns attachment metadata (file type, size, preview thumbnail) but never the downloadable resource file, which Tes's robots.txt disallows regardless.
Yes — /tes/jobs/search returns the same live faceted breakdown the search page renders as its sidebar: position, subject, and workplace category trees with counts, plus contract type and term counts, so you can drive filters off Tes's own live taxonomy.
Search /tes/schools/search by name or location — each result's id is the same employer id /tes/jobs/employer takes, so you can jump straight to the full profile (school type, phase, funding status, gender, age range, and currently-open positions).
No — country controls result currency and localisation only. key_stage and subject are closed, validated enums always resolved against Tes's own single GB facet taxonomy, whichever country you pass.