All platforms

Freshteam Jobs API

Query jobs from Freshteam boards through one API with ?source=freshteam — plus how Freshteam's job pages really behave, and why the platform is going away.

?source=freshteamsource reference

Freshteam is Freshworks' HR and applicant tracking product for small businesses, with boards at <tenant>.freshteam.com/jobs. Its tenancy is India-heavy, then US, UK and APAC.

Before anything else on this page: Freshworks is sunsetting Freshteam.

The platform is being shut down#

Renewals stopped on 2026-03-07. Existing customers keep access only to the end of their current term, which means the tenant pool doesn't drop off a cliff — it bleeds out through 2026 and 2027, one expiring contract at a time.

A page selling you a jobs API for a dying platform would be a bad page. So, the measurement rather than the pitch. As of 2026-07-15, of 1,605 known tenants:

Tenants
Serving at least one live job710 (~14,384 jobs)
Dead567

What that means if you're deciding whether to build this

Freshteam is still alive today and it is not alive long-term. Both halves are true and neither is the interesting one on its own.

If you need Freshteam data now, it's there and it's free to fetch. If you're costing a scraper as an asset you'll amortise over three years, don't: the asset expires. We built the adapter because it's cheap reuse of a JSON-LD path we already had, not because we think this platform has a future. Every number on this page needs re-measuring before anyone spends anything more on it, and when the live count collapses the right move is to drop the boards, not the code.

That framing also corrects our own earlier call. Freshteam was written off entirely on the sunset announcement, on the reasoning that a dying platform isn't worth an adapter. That was wrong in a specific way: "announced EOL" and "not serving jobs" are different facts, and only one of them had been checked. 710 live tenants is not nothing.

What the adapter reads#

BASH
# List — 1,000 postings per page
curl -L "https://<tenant>.freshteam.com/jobs?page=1"
 
# Detail — one JobPosting JSON-LD per posting
curl -L "https://<tenant>.freshteam.com/jobs/<id>/<slug>"

The official /api/job_postings and /api/v1/job_postings both 401. The public HTML is the only way in, and the body is detail-only, so it's an N+1.

Pagination is real here, unlike several platforms in this pool that ignore the param and hand you page one forever: ?page=2 returns 1,000 ids with zero overlap with page one. Crawled abnhire to exhaustion — 1,000/page × 5 pages = 5,000 distinct, page 6 adds nothing. But ?offset= and ?per_page= are ignored (both return page one at 200), so page is the only lever, and the termination condition is "a page contributed nothing new" rather than a total, because no total is exposed.

What bites you#

A dead tenant answers 200, not 404#

Freshworks serves an ~889-byte "We couldn't find {domain}" stub for any expired or unclaimed subdomain. At HTTP 200.

With the sunset actively killing tenants this is the common case, not an edge case — 567 of 1,605. And the failure is nasty in a specific way: treated as an empty board, a dead tenant expires every job you had for that company, and burns your whole detail budget on nothing. "Gone" and "hiring nothing" have to be told apart explicitly, and the only way to do it is to recognize the stub.

addressLocality and addressRegion are inverted platform-wide#

Freshteam puts the city in addressRegion and the state in addressLocality — the exact opposite of the schema.org spec it's otherwise following. Verified across 371 postings on ~200 tenants:

PostingaddressLocalityaddressRegion
NoidaUttar PradeshNoida
San DiegoCaliforniaSan Diego
StuttgartBaden-WürttembergStuttgart

Why this one is worse than a normal bug

A correct schema.org reader gets this wrong, and gets it wrong silently: it emits "State, City, Country", a downstream parser takes the first part as the city, and every Freshteam posting in your database says its city is California or Uttar Pradesh.

There's no error, no null, no anomaly — just a plausible city name in the city field on 100% of rows. A shared, correct, well-tested helper is the wrong tool here precisely because it's correct. Freshteam needs its own reader.

JSON-LD string values are entity-encoded, including the location#

&#39; in a city name is not cosmetic. "Thompson&#39;s Station" contains a semicolon, a location parser reads ; as a separator, and one city becomes two junk cities: ["Thompson&#39", "s Station"]. Address parts have to be entity-decoded before they're joined, not after.

About 8% of live tenants ship no JSON-LD at all#

5 of 60 tenants sampled (avomind, 219 jobs; oml; 10times; paxcom; openinapp) render the body in JavaScript and carry no description anywhere in the HTML. There's no fallback to reach for. Those postings are skipped rather than stored empty — a bodyless job is not a job.

Two smaller ones#

Job ids are base64url, not numeric (JLX_yOg0d7bW). The obvious /jobs/(\d+) regex matches nothing and the board reads as empty at HTTP 200.

remote="false" is an unchecked default, not an assertion. Freshteam's form has no hybrid concept at all, so false lumps on-site and hybrid together — and it's demonstrably stale on real postings (one is remote="false" with "Mode of Work - Work from Home" in its body). Only "true" is trusted. Reading false as On-site would mislabel every hybrid job on the platform.

What Freshteam simply doesn't have#

Verified across 371 postings, so you don't go looking: baseSalary 0/371. validThrough 0/371. hiringOrganization.sameAs and .logo 0/371 — only name. No comp, no expiry, and no org-domain signal, which makes attributing a Freshteam posting to a real company genuinely hard rather than merely fiddly.

Where this stands#

?source=freshteam is served today. And the unusual thing to say at the bottom of a page like this: it may not stay that way, and that would be the right outcome. Freshteam is a shrinking pool with a known end date. When the live tenant count collapses far enough, covering it stops being worth the request budget, and the right move is the one in the warning above — drop the boards, keep the code. We'd rather tell you that here than have you plan around a source that quietly empties out.

Which is the argument for coding against the keys of /v1/meta.sources rather than against this page: they're the values ?source= actually accepts, with counts, so a source that thins out shows you the count before it shows you an outage. Sources & coverage says the same in prose.

See all platforms · quickstart.

Every source above normalises onto the same schema, with the hiring company already joined onto each posting.

Get an API key