website
Legal, cookies and crawlers
The legal pages and what each covers, exactly what this site stores in your browser, the AI disclosure, and the crawler policy — including where it differs by bot.
1The legal pages
Four pages, each answering a different question, all linked from the footer of every page on the site.
In depthWhy it exists, what it is not, what people get wrong
- /privacy
- What the website collects about visitors and customers, what the analytics receive, and what is never collected. There is no personal data in the ledger itself — a job row names a company, not a person.
- /terms
- The terms of use for the site and the licence for API data.
- /ai
- The AI disclosure: which fields are machine-generated, by which models, and what is never sent to one. See below.
- /contact?topic=data
- Corrections and takedowns for a single listing.
[email protected]- A real, monitored mailbox, published in the footer of every page.
Exact contractTypes, defaults, ranges, errors, edge cases
Two structural points that matter more than the wording. First, the ledger holds no personal data: there are no candidates, no applications, no CVs and no jobseeker accounts, so most of what a privacy policy usually has to cover simply does not arise. Second, removal is self-service and verified — a site owner does not have to email anybody or wait for a human to act.
2What this site stores in your browser
A signed session cookie when you are signed in, a record of your consent choice, and analytics only if you allow them.
In depthWhy it exists, what it is not, what people get wrong
| Stored | Purpose | Set when |
|---|---|---|
joa_session | Signed sign-in session, 30 days, HTTP-only. Not a database row — the server verifies it cryptographically. | You follow a sign-in link. |
| Consent record | Remembers your cookie choice so the notice does not reappear. | You answer the notice. |
| Analytics | Product analytics, only with consent. | You opt in. |
| Documentation feedback | Which documentation pages this browser has voted on, so the tally is not a click counter. No identifier, first-party, never sent to the API. | You vote on a docs page. |
The consent manager is reachable at any time from Cookie preferences in the footer of every page. The essential category is always on and says so — it covers signing in and remembering the notice itself, which cannot be declined without breaking both.
Exact contractTypes, defaults, ranges, errors, edge cases
Consent decisions are recorded, so there is an auditable record of what was chosen and when, rather than only a cookie in your browser. What the analytics provider does and does not receive is stated explicitly on /privacy rather than left to the provider's own documentation.
3The AI disclosure
Two fields are always machine-generated, three are sometimes, and every row says which. No candidate or customer data is ever sent to a model.
In depthWhy it exists, what it is not, what people get wrong
| Field | Where it comes from | Provenance |
|---|---|---|
title | The posting, unchanged. | always published |
company | The posting. The display name is tidied, never invented. | always published |
location | The posting, or derived by us from it. | field_sources.location says which: published when the employer stated it, inferred when we resolved it against a gazetteer |
apply_url | The employer's own link. Never rewritten, never proxied. | always published |
employment_type | The posting, when the employer states it. | published or absent |
posted_at | The posting or the source feed. | published or absent |
description | The employer's own text, unchanged. | published or absent |
category | Always ours. A model reads the title and assigns a family. | always inferred |
seniority | Always ours. A model reads the title and assigns a level. | always inferred |
remote | Published when the employer says so; inferred when we conclude it from the text. | either — check the row |
salary | Published when the posting carries structured pay; inferred when parsed out of prose. | either — check the row |
source_type | Published when the source declares it; inferred otherwise. | either — check the row |
Nothing else in the ledger is model-generated. There is no generated description text, no generated salary figure, no generated employer and no generated apply link.
Exact contractTypes, defaults, ranges, errors, edge cases
What is sent to a model is the text of a public job posting and a company name — the same words the employer published on their own careers page. No customer data: your account, your API key, your queries and your usage are never used as model input, never used to train anything, and never shared with a model vendor. No candidate data, because none exists.
The disclosure also states where the classification is weakest, which is worth reading if you are building on category or seniority: on a clear job title they are dependable, on a vague or invented one they are a guess, and the marker says inferred either way because the row cannot tell you which case you have. The full page, including which models are used for which pass, is at /ai.
4The crawler policy
Search engines get the whole ledger. Answer engines get the pages that describe the product but not the two million rows that are the product.
In depthWhy it exists, what it is not, what people get wrong
| Who | Allowed | Refused |
|---|---|---|
| Search engines (Googlebot, Bingbot, Applebot, DuckDuckBot, Yandex, Baidu, …) | Everything, including /job/* and /company/*. | /api/, /dashboard, /admin, and the faceted query space /*?. |
| Answer engines (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot, CCBot, …) | The home page, /docs, /api, /about, /coverage, /facts, /compare, /pricing, /provenance, and the job and company indexes. | The per-row corpus: /job/* and /company/*. Plus the same private paths. |
| Link unfurlers (Twitterbot, facebookexternalhit, LinkedInBot, Slackbot, Discordbot) | Everything except the private paths — an unfurler fetches the one URL a human just pasted, which is a citation, not a harvest. | /api/, /dashboard, /admin, /*?. |
| Everything else | Nothing. | The ledger is free to read in a browser and licensed for bulk use through the API; scraping is the one route that is neither. |
This documentation is allowed for everyone, answer engines included. It describes the product; it is not the corpus.
Exact contractTypes, defaults, ranges, errors, edge cases
The reasoning behind the asymmetry, since it is a deliberate choice rather than a reflex: a job page ranking in a search engine sends a human here, and that human can see a price. An answer engine that ingests the same page answers the question inside its own product and the visit never happens. Handing two and a half million rows to a model vendor for free, while selling the same rows through the API, is selling something you are also giving away — to the buyer least likely to come back. What answer engines keep is deliberately generous: enough to describe the dataset accurately, cite it, and tell somebody where to get it.
/*? — the faceted query space — is refused to every crawler. The home page exposes country, city, category, remote and employment as query parameters, so the number of distinct URLs is combinatorial. One crawler enumerating them generated 1,562 requests in three minutes, 97% of them a query string never seen before, so every one missed the page cache and forced a fresh render and a fresh query; that single pattern held the server at 96% CPU for as long as it continued. Nothing of value is lost — a filtered view is duplicate content whose canonical is the bare page, and every individual job and company already has its own indexable URL in the sitemap.
This page was rendered 11 September 2026, 23:05 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.