# The HyperJobs API is live

> A read-only jobs API with the hiring company attached to every posting. What shipped, who it is for, and where the rough edges are.

Source: https://hyperjobs.io/blog/hyperjobs-api-launch

*HyperJobs Team · 14 July 2026 · 6 min read*

HyperJobs is a read-only HTTP API over a continuously refreshed dataset of job
postings, scraped directly from company ATS boards — Greenhouse, Lever, Ashby,
Workday, SmartRecruiters and 60-odd others — enriched with skills, seniority,
salary and location, and joined to firmographics for the company doing the
hiring.

It is available now. There is a free tier, and you can be making requests in
about two minutes.

<Note>
  Revised 2026-07-16: the v1 that launched is a redesign of the surface this
  post originally announced — strict validation, comma = OR, cursor
  pagination, salary range-overlap filters, and four more endpoints shipped
  before any production consumer existed. The "rough edges" section below
  originally listed the draft's traps; it now lists the real ones.
</Note>

## The defining property is the join

Every job carries its company inline:

```json
{
  "id": "ashby:0f2c…",
  "title": "Senior C++ Engineer",
  "company": {
    "slug": "stripe",
    "name": "Stripe",
    "domain": "stripe.com",
    "industry": "Financial Services",
    "employee_count": 8000,
    "hq": { "country": "US", "locality": "South San Francisco" }
  },
  "location": { "remote": false, "countries": ["United States"], "cities": ["New York"] },
  "salary": { "annual_min": 180000, "annual_max": 240000, "currency": "USD" },
  "skills": ["cpp", "cuda", "distributed systems"]
}
```

Most jobs APIs hand you an employer *name* and leave you to resolve it. Here the
company object is already attached, so a query like "remote Rust jobs at Series-B
fintechs with more than 200 employees" is one request instead of a search plus N
lookups. [Why every job carries its company](/blog/the-company-join) is the long
version of that argument.

The data is scraped from employers' own boards, not resold from an aggregator.
That is why `apply_url` points at the real application form, and why postings
disappear when the employer takes them down.

## Who it is for

Four shapes of user, roughly:

- **Job boards and aggregators** that need postings with real apply URLs and
  enough enrichment to build facets over.
- **Recruiting and sourcing tools** that need "who is hiring for X, and what kind
  of company are they".
- **Market and talent intelligence** — demand by skill, by geography, by
  employer, over time.
- **Agents and internal tools** that need a jobs corpus behind a single key
  rather than a scraping project.

If you are considering building the scraping yourself: the hard part is not the
first board. It is the sixty-fifth, plus expiry detection, plus the company
resolution, forever.

## What shipped

All read-only, all `GET`. There are no `POST`/`PUT`/`DELETE` routes, no
webhooks and no streaming.

| Endpoint | Purpose |
| --- | --- |
| [`/v1/jobs`](/docs/api-reference/jobs-list) | The search. ~50 filters, cursor paging, newest-first. |
| [`/v1/jobs/count`](/docs/api-reference/jobs-count) | The same filters, count only. |
| [`/v1/jobs/feed`](/docs/api-reference/jobs-feed) | Newly indexed postings, cursor-paged, for incremental sync. |
| [`/v1/jobs/expired`](/docs/api-reference/jobs-expired) | Ids that died — the purge feed for mirrors. |
| [`/v1/jobs/modified`](/docs/api-reference/jobs-modified) | Postings whose tracked fields changed, with what changed. |
| [`/v1/jobs/{id}`](/docs/api-reference/jobs-retrieve) | One posting. |
| [`/v1/companies`](/docs/api-reference/companies-list) | Firmographic search — industry, size, country, `has_jobs`. |
| [`/v1/meta`](/docs/api-reference/meta) | Counts, freshness, source vocabulary, live per-field coverage. |

Plus [`/v1/health`](/docs/api-reference/health) and `/openapi.json`, both
unauthenticated, free and unmetered — use the first for liveness rather than a
`limit=1` job query, and the second to generate a client.

Alongside the API:

- **[The documentation](/docs)** — every endpoint, parameter and response field,
  plus guides for [filtering](/docs/guides/filtering),
  [feed sync](/docs/guides/feed-sync), [salary](/docs/guides/salary),
  [companies](/docs/guides/companies) and
  [duplicates](/docs/guides/deduplication).
- **An OpenAPI document** at `/openapi.json` — generated from the same spec
  module the server validates against, so it cannot drift from the wire.
- **`/llms.txt`**, because a fair number of you will point a model at this before
  you read a word of it.
- **[A changelog](/docs/changelog)** with the compatibility policy. `/v1` is
  stable; breaking changes ship as `/v2` and `/v1` keeps working.

## Plans

| Plan | Price | Requests / month | Requests / minute |
| --- | --- | --- | --- |
| Free | $0 | 500 | 10 |
| Starter | $49/mo | 50,000 | 60 |
| Growth | $199/mo | 500,000 | 300 |
| Scale | $499/mo | 5,000,000 | 1,000 |

Annual billing is 10× the monthly price — two months free. **Every plan gets the
full dataset and every filter.** Plans differ only in volume; nothing is gated
behind a tier, and there is no "contact us to see the data". Above Scale, or for
flat-file delivery, [talk to us](/docs/support).

The free tier's 500 requests are meant to be enough to answer "is this dataset
what I need" without a card. Limits are per **account**, not per key — ten keys
on a Growth plan share one pool. See [rate limits](/docs/rate-limits).

## The rough edges

The docs are unusually direct about what this API gets wrong, and that is
deliberate. Docs that describe intent produce clients broken in exactly the
ways that cost the most time. So the reference describes actual behaviour —
and where the draft's behaviour was a trap, we fixed the API before launch
rather than documenting around it. What's left is real:

- **Validation is strict.** An unknown parameter or bad value is a
  [`400` naming it](/docs/errors) — never a silent drop. That's a feature, but
  it means requests that "worked" against sloppier APIs will fail loudly here.
- **`skill` is the one AND.** Commas mean OR on every multi-value filter
  *except* `skill`, where every listed token is required. `skill=python,aws`
  means both.
- **Open vocabularies can still return silent zeroes.** `source` and `skill`
  aren't checked enums, and displayed skills don't always round-trip —
  `skill=Machine Learning` is stored `machinelearning`. See
  [skills & taxonomies](/docs/guides/skills-and-taxonomies).
- **`salary_gte` is range-overlap** — "the band *reaches* N", not "pays at
  least N" — and postings with no upper bound don't match it.
  [Salary data](/docs/guides/salary) explains the honest floor filter.
- **Coverage is partial by construction.** Enriched fields are classifier
  outputs; [`/v1/meta`](/docs/api-reference/meta) publishes live per-field
  fill rates (`coverage_pct`) so you can see exactly how partial. No
  competitor tells you this; it's the number your aggregate rests on.
- **Descriptions are omitted unless you ask.** `description_format=text` or
  `html`. They are multi-kilobyte, and fetching 200 you do not read is the
  most common way to make this API feel slow.

[Known gaps](/docs/changelog) lists what's genuinely missing: no webhooks, no
bulk export, no geo/radius search, no configurable sort. Each has a documented
workaround. If one of them blocks you, say so — knowing what people actually
hit is how that list gets shorter.

## Start here

<Steps>
  <Step title="Get a key">
    [Register](/register) and create one from the dashboard. It is shown exactly
    once, so copy it immediately — only a hash is stored.
  </Step>
  <Step title="Run the quickstart">
    [Two minutes](/docs/quickstart), one authenticated request, one enriched
    payload.
  </Step>
  <Step title="Read the filtering guide">
    [Filtering](/docs/guides/filtering) is where the real query surface is, and
    it has two behaviours that will otherwise cost you an afternoon.
  </Step>
</Steps>

Pricing is on the [plans page](/pricing). Questions, bugs, and wrong data go to
[support](/docs/support) — a job id and the request URL is the most useful report
we get.
