How we built the EU-remote eligibility filter
The problem sounds like string matching and is not. Given a job posting written by whoever happened to be hiring, decide whether someone living in the EU could hold the role. There is no field for this. There is no standard. Two companies using the same applicant tracking system will express the same constraint in different words, and one of them will not express it at all.
Four gates, in order
A posting is EU-remote eligible only if it clears all four. Order matters: each gate is cheaper than the one after it, and the expensive ambiguity is deliberately last.
- Gate A — is this a technical role at all? Title-based, and it throws away the recruiter spam early.
- Gate B — explicit work type. On-site and hybrid are out. So is an explicit US-only scope. Missing values are treated leniently, because discovery-stage listings often have no scope field yet, and rejecting on absence loses real roles.
- Gate C — at least one location signal that opens the role to Europe.
- Gate D — no non-EU restriction, unless an explicit EU signal overrides it.
Gate C is where the signals get combined
Rather than test one field, we build an enriched location text from every signal the posting carries — the location string, the raw scope, the full location list, and the normalised country names — then match once against that. This is what rescues the listing whose location field says "HQ" and whose real eligibility lives in a structured field three levels down.
// EU_WIDE and WORLDWIDE both mean EU workers are eligible — inject "Europe" so
// Gate C fires even when location is blank or uninformative.
if (remoteScopeDetected is "EU_WIDE" or "WORLDWIDE")
parts.Add("Europe");Worldwide roles count as EU-eligible, deliberately. Someone in the EU is eligible for a role open to everyone — treating "worldwide" as a separate, lesser category was an early mistake that cost us real listings.
Gate D and the override that took longest
The naive version of Gate D rejects anything matching a US pattern. It is wrong often enough to matter. A role whose locations are ["Amsterdam", "Palo Alto, CA"] is open to you; a distributed company simply listed both offices. So a confirmed, explicit EU token overrides the non-EU restriction rather than losing to it.
The US-side patterns are mostly state codes, because they are the signal that survives when nothing else states the constraint. "Remote, New York" and "Remote, Texas" are both US-scoped roles that never use the words "US only".
City names are the genuinely hard part
You cannot match on city names naively, because they collide. Georgia is a US state and a country. Every US state code is also two letters that mean something else somewhere. The rule we settled on: only include a city when it is unambiguous — when it does not share a name with a major EU or UK city — and reach for the state-code catch instead of a contested city name. Georgia-the-country is covered by matching Tbilisi, not by matching "Georgia" and hoping.
These lists are cross-checked by tests, so adding a city to the geo mapping without adding it to the pattern fails the build rather than quietly narrowing the filter.
Where it is still wrong
- A role open only to contractors, or only via an employer of record, is not distinguishable from a normal one. We do not model employment structure at all.
- A worldwide role carries no country list, so it cannot be isolated as its own filter — it is admitted, but it cannot be asked for specifically.
- Leniency on missing scope fields means some roles pass Gate B that a human would reject on reading the description. We prefer this to the alternative: a filter that silently drops early-stage listings is worse than one that occasionally over-admits.
If you have hit a case this gets wrong, we would genuinely like the posting. The filter improved mostly by being shown listings it mishandled.