All platforms

Dover Jobs API

How Dover's cross-tenant job-board feed actually works, why you cannot filter it by client, and what ?source=dover returns in the HyperJobs API.

?source=doversource reference

Dover is a recruiting-automation ATS aimed at startups, with boards at jobs.dover.com/<slug>. It has one property that almost nothing else in this pool has, and it is worth more than its size: a public cross-tenant feed. One unauthenticated call enumerates every client on the platform and every job they have open.

The cross-tenant feed, and why it matters#

BASH
curl "https://app.dover.com/api/v1/job-board/jobs/?limit=500&offset=0"

That is the whole platform: 334 jobs across 167 clients, in one request. A standard paginated envelope, {count, next, previous, results}, and limit is genuinely respected — which is rarer than it should be. Ask for 500 and you get all 334.

The reason this is notable is the thing almost every other page in this collection has to explain. Most ATS platforms have no global index. Every customer is an isolated subdomain or slug, nobody publishes a customer list, and so the hard part of the work is not fetching a board but knowing the board exists. Lever and Greenhouse both make you assemble that list yourself. Dover just hands it over. Only a handful of platforms do.

The honest caveat: Dover is small. Around 334 jobs platform-wide, roughly two per client. Free discovery on a small corpus is still free discovery, but it won't move volume.

The feed rows have no body. The description is one detail call away:

BASH
curl "https://app.dover.com/api/v1/job-board/jobs/<id>/"   # user_provided_description

What bites you#

The feed cannot be filtered by client, and it pretends it can

?client=, ?client_slug=, ?slug=, ?client_id= and ?company= all return HTTP 200 with the full 334-job feed, other clients' jobs included. None of them error. None of them warn.

Trust any one of them and you attribute every job on the platform to a single company. The only correct approach is to pull the feed once and group it by client.slug locally.

The top-level workplace_type is authoritative, and it disagrees with locations[]. 28 jobs carry location entries marked both REMOTE and HYBRID. The two fields also use different vocabularies for the same idea: the top-level one says ONSITE where locations[] says IN_OFFICE. Point one shared enum map at both and it drops whichever spelling it doesn't know.

compensation carries equity bounds too, not only salary. Read the field without checking what kind of number it is and you will store an equity range as a pay band.

City and state are empty strings on country-level postings. They aren't null, they're "", so a truthiness check passes and joins them into a location that is just commas. Fall through to the parts that are actually filled.

There is no Cloudflare wall, despite appearances. jobs.dover.com/<slug> returns 200 with an empty React shell. That looks like a block and isn't — it's just a client-rendered page. The data was always in the API.

What you get#

date_posted filled on 334 of 334 rows with a proper zoned timestamp, descriptions complete on every tenant measured, and client.name giving the employer's real name, which makes Dover one of the stronger platforms for the company join. Compensation runs about 35% platform-wide, so a tenant with none is normal rather than a parsing failure.

Query it with ?source=dover, and the grouping the raw feed forces on you is already done: rows arrive attributed to the client they belong to rather than to the platform. The value is the public slug, and the same string keys /v1/metasources — that endpoint is the live vocabulary, and worth reading rather than trusting a list on a page.

See all platforms · coverage

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

Get an API key