Audit job ads for pay transparency
Article 5 of the pay transparency directive puts two duties on whoever writes the advert. Tell the applicant the initial pay or its range before the interview, and never ask what they earn now or earned before. Checking that by hand means opening forty adverts and typing the answers into a spreadsheet. Below is how I read them straight from the systems the adverts are published on instead, and why one distinction decides whether a verdict is worth anything at all.
The questionHow do I check whether our job adverts comply with the EU pay transparency directive?
How the working version is put together
Read the board through its own API
Seven applicant tracking systems publish their postings openly and I read all seven: Greenhouse and Lever and Ashby and Recruitee, plus Workable and SmartRecruiters and Personio. None of them needs a key or a login, and there is no proxy or browser anywhere in the chain. Give it a career page instead and the JobPosting JSON-LD is read where the page publishes it, with the job links followed where it does not.
Decide whether pay is actually stated
A posting comes back as PROVIDED when a real figure or range appears, VAGUE when the advert only gestures at pay, and MISSING when there is nothing. Vague wording is matched in seven languages, so leistungsgerechte Vergütung and rémunération compétitive are caught rather than read as silence. European number formats and currencies are parsed properly: 45.000 bis 55.000 euro is a range, and so is 25 000 zł miesięcznie.
Keep pay history apart from pay expectations
This is the one that decides whether any of it means anything. The directive bans asking about pay history, not pay expectations, so asking what somebody wants to earn is lawful and asking what they currently earn is not. Both are recorded, in separate fields, and only the first ever counts against a posting. A payroll job listing Gehaltsabrechnung among its duties is not asking anyone for a payslip either, so document wording only registers when the advert is asking the applicant to send one.
Work out whether the directive applies at all
Country comes from the posting's own location field, or from the JSON-LD, or from the location text as a last resort. The EU-27 plus Iceland, Liechtenstein and Norway are in scope. A role advertised across the Union with no single country is kept as EU rather than thrown away, because dropping the awkward rows is how an audit quietly flatters itself.
Combine it into a verdict, and name what broke
OK when pay is stated and no history is asked for. ATTENTION when the wording is vague, or pay is stated but history is asked for anyway. LIKELY_NON_COMPLIANT when an in-scope posting states no pay at all. OUT_OF_SCOPE outside the EU and EEA, and UNREADABLE when a posting was fetched but carried no usable text. Any source that fails is reported by name with its HTTP status rather than quietly dropped.
What it found on real adverts
These are public adverts I pointed it at while testing, not clients, and nobody was told. A GetYourGuide account executive role for the DACH market came back LIKELY_NON_COMPLIANT with no pay stated anywhere in it. Qonto and orderbird roles came back VAGUE, matched on rémunération compétitive and Attraktives Gehaltspaket. Channable and Skroutz roles came back compliant with real parsed ranges, which is the answer you want and the one nobody writes a case study about.
The point of showing you those is the evidence column. Every finding carries the exact sentence it was matched on, so you can check the machine rather than trust it. A compliance tool you cannot audit is just a different opinion with a nicer interface.
What a verdict here does not mean
It is a text audit of public job adverts. It is not legal advice and it does not tell you an employer is breaking the law. Member states transposed the directive at different times and some went further than the minimum, so what a given advert has to say depends on where the job actually is.
The pay duty can also be met before the interview rather than in the advert itself. An employer who tells every candidate the range on the first call is complying while their advert still reads as MISSING here. So MISSING means one thing only: the posting does not state pay. Treat it as a place to look, never as a finding.
Two practical gaps as well. Teamtailor is not supported, because its jobs API needs a key issued per company, so give the career page URL instead. Personio's public feed often ships with the description empty and its job pages sit behind a bot challenge, so a Personio board can come back UNREADABLE even though the adverts read fine in a browser. When that happens the run says so rather than guessing.
What it costs to run
One event per posting that was fetched and audited. A posting with no usable text costs nothing, a board that 404s costs nothing, and a source that errors costs nothing. Run it with no sources at all and you get three sample rows free, which is enough to see the output shape before pointing it at anything real.
There is one optional extra. Paste an OpenAI key and postings where the rules found no pay wording get a second look from a model, which catches unusual phrasing and languages the lexicon does not cover. It never overrides a figure the rules already found, and those rows are marked so you can tell them apart. Leave it empty and the audit still runs in full.
When I would tell you not to bother
If you run one board with nine adverts on it, open them and read them. This earns its keep across a few hundred postings, or when the same boards need rechecking every month as roles turn over.
If what you actually need is a legal opinion on your exposure, buy one. What this gives you is the evidence a lawyer would otherwise ask an intern to gather: which adverts state pay, which do not, and the exact sentence in each one that made the difference.
What lands on your desk
- One row per posting, with the verdict and the evidence sentence behind it
- Parsed pay figures with currency and period, from the ATS field or the advert text
- Pay history and pay expectations reported separately, so a verdict cannot over-claim
- Seven applicant tracking systems read through their own public APIs, no key needed
- If you want this running monthly into a report somebody actually reads, that is the work I do
Open the proof
Published case studies of systems that are running. Each links to the thing itself.
The hosted Actor on Apify ↗Directive (EU) 2023/970, the text itself ↗My n8n creator profile ↗
Questions this page answers
- Which job boards can it read?
- Seven applicant tracking systems through their own public postings APIs: Greenhouse, Lever, Ashby, Recruitee, Workable, SmartRecruiters and Personio. None of them needs a key. Teamtailor isn't supported because its API needs a key issued per company, so give the career page URL instead.
- Does asking for salary expectations break the directive?
- No, and this is the distinction that matters most. The directive bans asking about pay history, not pay expectations. Both get recorded, in separate fields, and only pay history ever counts against a posting.
- What does a MISSING verdict actually mean?
- It means the advert itself does not state pay, and nothing more than that. The duty can be met before the interview rather than in the advert, so an employer who gives every candidate the range on the first call is complying while their advert still reads as MISSING here.
- Is this legal advice?
- It isn't. It's a text audit of public job adverts, and member states transposed the directive at different times with some going further than the minimum, so what a given advert has to say depends on where the job is.
- What happens when a board can't be read?
- The source is reported by name with its HTTP status rather than quietly dropped, and a posting fetched with no usable text comes back as UNREADABLE. Neither one is charged.
Want this built and handed over working?
One line is enough to start. You get an honest answer on fit, and a number rather than a discovery call.
Start a brief