api
Keyless access
What /public/* gives you with no account: real rows, one page at a time, bounded so that evaluating is free and extracting is not.
1What you get without an account
Every read shape the API has, against the live ledger, with no key: rows, companies, facets, coverage, providers, freshness, plans and the spec.
In depthWhy it exists, what it is not, what people get wrong
The keyless surface is not a sandbox and it is not sample data. It reads from the same projection as /v1, so a row you see here is a row you would receive on a paid key, with the same fields and the same provenance block. That is the whole point: the product's claim is that its data is auditable, and a claim you have to sign up to check is not one.
What you cannot do without a key is walk the corpus. Keyless queries return one page; there is no cursor. You can change the filters and see different rows as many times as you like, which is what evaluation looks like, and you cannot page through two million rows, which is what extraction looks like.
Exact contractTypes, defaults, ranges, errors, edge cases
| Keyless endpoint | Keyed equivalent | Difference |
|---|---|---|
/public/jobs | /v1/jobs | 50-row cap, no cursor, paid-only parameters ignored. |
/public/jobs/{slug} | /v1/jobs/{id} | Slug or uuid on both. The keyless one always includes the description. |
/public/companies | /v1/companies | Capped page, no deep paging. |
/public/companies/{slug} | /v1/companies/{slug} | Same row. |
/public/facets | /v1/meta/facets | Identical data. |
/public/providers | /v1/meta/providers | Identical data. |
/public/freshness | /v1/meta/freshness | Identical data. |
/public/coverage | — (no keyed twin) | The canonical coverage report. |
/public/coverage/countries | — | Per-country coverage. |
/public/coverage/employers | — | Per-employer coverage. |
/public/stats | — | Convenience summary; see why it looks different. |
/public/plans | — | The price list. |
/public/openapi.json | /v1/openapi.json | The same document. |
/public/ai-prompt | — | An integration brief, from the API host, for a model. |
| — (no keyless twin) | /v1/changes | The delta feed is licensed. |
| — | /v1/jobs/expired | Same. |
| — | /v1/jobs/closed | Same. |
| — | /v1/export | Bulk export is licensed. |
$ A=https://api.jobopportunitiesapi.org $ curl -s "$A/public/coverage" | jq '{live_listings, employers, countries, measured_at, stale}' { "age_seconds": 10059, "closed_listings": 5690972, "countries": 249, "definitions": { "closed_listings": "Roles that came off their source, retained with their closure date and reason.", "ledger_rows": "live_listings + withheld_listings + closed_listings. It reconciles exactly.", "live_listings": "Rows a caller can obtain from /v1/jobs: not delisted, not opted out, and not withheld by the quality gate. This is the number you can reproduce by paging the API.", "measured_at": "When these figures were counted. The report is measured on a schedule, not per request, because counting it per request took 24 seconds and returned 503 instead.", "withheld_listings": "Rows present in the ledger and deliberately not served. quality_removed breaches the employer-direct guarantee or comes from a discovery-only source; quality_gated is reversible doubt; optout_hidden is a verified employer opt-out." }, "employers": 189424, … 111 more lines
2The limits, exactly
About two requests a second sustained, a burst of ten, forty a minute, fifty rows a page, and a ten-minute block if you go through all of that.
In depthWhy it exists, what it is not, what people get wrong
These numbers were chosen against two measured profiles rather than picked. A person evaluating the product peaks at ten to fifteen requests in a minute — clicking the preset buttons on the API page, then a few queries of their own. An extractor does not stop. Forty a minute is comfortably above the first and pointless for the second.
The page cap matters more than the rate. Before it existed, /public/jobs served 294 rows a second to an unauthenticated caller with working keyset pagination: the entire live ledger in a bit over two hours single-threaded, or about twenty minutes across eight workers. The paid entry plan is €80 a month for sixty thousand records, and the keyless path was handing that over every three and a half minutes.
Exact contractTypes, defaults, ranges, errors, edge cases
| Limit | Value | Applies to |
|---|---|---|
| Sustained rate | 2 requests/second | Per client IP, refilled continuously. |
| Burst | 10 requests | The bucket's capacity. |
| Window ceiling | 40 requests/minute | Per client IP. |
| Block on breach | 10 minutes | 429 with Retry-After. Not permanent. |
| Page size | 50 rows | limit above 50 is clamped, not refused. |
| Paging | none | A cursor on a keyless request is a 402, not a 401. |
$ curl -s 'https://api.jobopportunitiesapi.org/public/jobs?limit=2&cursor=anything' { "error": "key_required_to_page", "message": "Keyless queries return one page. Change the filters to see different rows, or take a free Explore key at https://jobopportunitiesapi.org/login — 1,000 records a month, no card, and it pages.", "docs": "https://jobopportunitiesapi.org/api" }
Three exemptions exist and none of them helps a third party: requests that did not arrive through the edge (the website's own server-side rendering, talking to the API over the internal network), our own egress addresses (so the QA harness does not rate-limit itself), and authenticated /v1 callers, who already have a per-key limit and a record meter — two limiters on one request is how you get a support ticket nobody can reproduce.
3When keyless stops being enough
The moment you need a second page, a change feed, or more than about forty requests a minute. The free key removes the first and third of those.
In depthWhy it exists, what it is not, what people get wrong
The free Explore plan is not a trial — it does not expire, and it needs no card. It replaces the IP-based throttle with a per-key request limit and a monthly record allowance, and it turns on cursor paging. For a lot of small projects that is the end of the story.
- You need page two. Keyless returns one page; any key pages. This is the most common reason.
- You need the delta feed.
/v1/changesand/v1/jobs/expiredrequire a plan withdelta_feed. Polling a listing endpoint and diffing is the alternative, and it costs far more records than the feed does. - You need bulk export.
/v1/exportrequiresbulk_export. - You need throughput. The per-minute ceiling on a paid plan is measured in hundreds rather than tens.
- You need closed rows.
status=closedand/v1/jobs/closedare paid-only.
Exact contractTypes, defaults, ranges, errors, edge cases
| Plan | Price | Records / month | Requests / day | Requests / min | Delta feed | Bulk export |
|---|---|---|---|---|---|---|
Exploreexplore | Free | 1,000 | 5,000 | 30 | no | no |
Growthgrowth | €80/month | 60,000 | 100,000 | 120 | yes | no |
Signalsignal | €299/month | 400,000 | 400,000 | 300 | yes | yes |
Scalescale | €899/month | 2,000,000 | 1,000,000 | 600 | yes | yes |
Rendered from /public/plans when this page was built. Only purchasable plans appear there, so this table is the price list — if a plan is not here you cannot buy it. Compare on /pricing.
Note that the ladder is about records, not requests, above the free tier. That is the single most commonly misunderstood thing about the pricing and it is explained properly in the record meter — read it before comparing the numbers above with another vendor's, because a request-priced API and a record-priced one are not comparable line by line.
This page was rendered 11 September 2026, 16:47 UTC. Every figure on it comes from the endpoint named beside it, and every published request is re-sent against the live API before this site is allowed to build. If something here is wrong, the thumbs-down above reaches a person.