JobScore Jobs API
Query jobs from JobScore boards through one API with ?source=jobscore — plus how JobScore's Atom feed and its structured salary really behave.
JobScore is an applicant tracking system for small and mid-sized employers.
Boards live on a shared host at careers.jobscore.com/careers/<company>, and
every one of them publishes an Atom feed carrying the whole board with
descriptions inline. One unauthenticated GET, no token, no detail pass. On the
board we tested that was 30 jobs, all 30 with descriptions.
Thirty. Nobody is building an ATS integration for thirty jobs, which is the honest pitch for a platform this size: the work isn't the fetch, it's that this is one of dozens of boards that each need their own quirks learned, and JobScore has two worth learning.
Salary, and why this one is different#
Salary is the weakest field we serve. Across the whole dataset it's populated on
36.0% of postings — has_salary=true
throws away roughly two thirds of everything before your other filters even run.
That's not a collection failure so much as a publishing one: most employers don't
post pay, and most of the platforms that let them don't model it. Where salary
does exist, it usually exists as prose, in the body, and getting a number out
means mining a sentence and hoping.
JobScore publishes a real one. It extends Atom with a j: namespace, and inside
each entry sits a j:publicCompensation block:
<j:publicCompensation>
<j:salaryMinimum>95000</j:salaryMinimum>
<j:salaryMaximum>125000</j:salaryMaximum>
<j:currencyCode>USD</j:currencyCode>
<j:interval>year</j:interval>
<j:formatted>$95,000 - $125,000 / year</j:formatted>
</j:publicCompensation>Minimum, maximum, currency, interval, and the employer's own formatted string — structured, typed, no regex over a paragraph. That is rare for an ATS at this end of the market and it's the most interesting thing about the platform. Ashby, Rippling and Pinpoint publish structured comp too; a small-business ATS bothering to is unusual.
How JobScore boards actually work#
One GET, no auth, the whole board:
curl "https://careers.jobscore.com/jobs/<company>/feed.atom"Note the path: the feed is under /jobs/<company>/, while the human-facing board
is /careers/<company> on the same host. <company> is the slug either way, and
it's a path segment on a shared host rather than a per-tenant subdomain like
most of this pool — which is why JobScore has something almost nobody here does:
a native index. careers.jobscore.com/sitemaps/careers.xml.gz lists 504 slugs,
live and sanctioned by robots.txt. On most platforms every customer is an isolated
subdomain and the board list has to be assembled from nothing. Here you can just
ask.
There is no pagination cap, which we probed rather than assumed: one board returned 60 entries in a single document, and every board's Atom count matched its careers page exactly — 60/60, 30/30, 11/11, 5/5. The feed has no paging controls because it doesn't need any.
Per entry you get <title>, <content> with the full body, <updated> as an ISO
8601 timestamp with a zone, and a rel="alternate" link to the posting.
What bites you#
Categories are scheme-keyed, not order-keyed. Location and department both
arrive as <category> elements, distinguished only by their scheme attribute:
<category scheme="…#department" term="Clinical"/>
<category scheme="…#location" term="Remote in Joinville, Santa Catarina, Brazil"/>Boards emit several categories and there is no fixed order, so reading "the first category" or "the second" gets you a department in your location field on some boards and not others. Match the scheme.
An empty feed is a live board, and this one cost us. A JobScore tenant with
zero openings serves a valid, empty feed at 200. Only a 404 means the
board is gone. Requiring at least one <entry> as proof of life scored the
platform at 5% live and discarded real boards that simply weren't hiring that
week — a healthy employer between roles is not a dead tenant, and treating it as
one deletes it permanently.
The id is buried in a tag URI. Entries identify themselves as
tag:jobscore.com,2026-04-22:/dFF1n7ML1lS7CdX9T3vco-. The job id is the trailing
path segment, and unlike most of this pool it's globally unique rather than
tenant-local, so it doesn't need scoping to dedupe.
Or use ours#
JobScore is one of the few platforms here you could reasonably do by hand: one GET, no auth, no pagination, and a sanctioned sitemap so even the board list is just a download. If that's your whole requirement, take the feed.
?source=jobscore is served, and what it adds is the normalisation and the
judgement calls. j:publicCompensation lands as the same
salary object with annual_min and annual_max that every
other source normalises onto, so JobScore's typed band is directly comparable to a
number mined out of prose elsewhere — and salary data is
honest about where that comparison strains. Categories are read by their scheme
rather than their position. An empty feed is treated as a live board between
roles, not a dead tenant, which is the mistake that once scored this platform at
5% live. Ids come off the tag URI, already globally unique.
One caveat that outlives all of this:
/v1/meta → sources is the authority on what you can
filter on. source is a csv filter, not an enum, so a miscased ?source=JobScore
returns an empty array rather than a 400 — indistinguishable from a board that
simply isn't hiring.
Every source above normalises onto the same schema, with the hiring company already joined onto each posting.
Get an API key