Parse the location strings that job boards actually emit.
The location normalizer from SearchSteward's
ingest pipeline, published as a standalone, dependency-free Python library.
It turns the free-text location field of a job posting into structured
{city, admin, country, remote} components and a clean display string.
Real ATS location fields are not clean data. Across millions of scraped postings we've stored, all of these are actual values from the location field:
| Raw value | What it should mean |
|---|---|
Gretna, Louisiana / R-26-0036719 |
Workday appends the requisition id |
San Francisco, CA / {'name': 'San Francisco, CA'} |
Greenhouse leaks its API dict |
Washington, DC 20004 |
trailing ZIP makes "washington" parse as WA state |
9 Locations |
a count, not a place |
Us - San Francisco, Nyc, 9Th Arrondissement |
.title() corruption |
Mexico, Remote |
remote — but not US-remote |
Hybrid - Chicago, IL |
work arrangement glued onto the place |
Toronto, ontario, CA |
Canada — not California |
Bengaluru, IN |
India — not Indiana |
Memphis, TN <span class=… |
a real city with an HTML tag tail |
FLORIDA - VIRTUAL - FL01 |
an arrangement standing where a city should be |
Byron Center, Michigan |
a real town, not "Byron" plus a facility word |
This library handles all of them.
# One file, stdlib only — vendor it:
curl -O https://raw.githubusercontent.com/searchsteward/location-parser/main/location_parser.pyA PyPI package (steward-location-parser) is planned; the file above is the same code.
from location_parser import (
parse_job_location, normalize_location_text, titlecase_place, is_us_or_remote,
country_and_region, is_canadian_location,
)
parse_job_location("Hybrid - Washington, DC 20004")
# {'remote': False, 'city': 'washington', 'admin': 'DC', 'country': 'US'}
normalize_location_text("Gretna, Louisiana / R-26-0036719")
# 'Gretna, LA, US'
normalize_location_text("Mexico, Remote")
# 'Mexico, Remote' — the country survives; this is NOT a US-remote role
normalize_location_text("Remote - United States")
# 'Remote'
titlecase_place("us - san francisco")
# 'US - San Francisco' — .title() would give 'Us - San Francisco'
is_us_or_remote("EMEA [Remote]") # False — remote, but scoped to a region
is_us_or_remote("California, Remote") # True
country_and_region("Austin, TX") # ('US', 'TX')
country_and_region("Toronto, ontario, CA") # ('CA', 'ON') — not California
country_and_region("Bengaluru, IN") # ('IN', None) — not Indiana
country_and_region("Nowhere In Particular") # (None, None) — never guesses
is_canadian_location("Ontario, CA") # False — the city in California
is_canadian_location("CA / Remote") # True| Function | Returns |
|---|---|
parse_job_location(s) |
{city, admin, country, remote} components |
country_and_region(s) |
canonical (ISO 3166-1 alpha-2, subdivision code), or (None, None) |
normalize_location_text(s) |
a clean single-line rendering |
sanitize_location_for_display(s) |
strips known corruptions without parsing |
is_remote(s) / is_us_or_remote(s) / is_canadian_location(s) |
bool predicates |
titlecase_place(s) |
casing that survives US, NYC, McLean, 9th |
strip_markup_tail(s) / is_location_list(s) / join_places(parts) |
helpers |
- 300+ country/state/city aliases —
"deutschland"→DE,"bombay"→mumbai,"sf bay area"→san francisco, full US state names and abbreviations, Canadian provinces. - Remote detection that keeps the geography.
"Remote - Philippines"is remote and Philippine — collapsing it to bare"Remote"is how foreign roles sneak into US-only feeds. Bare"Remote"with no other geography defaults to US-remote. - ATS corruption stripping: Workday trailing req-ids, both Greenhouse
dict-leak formats (YAML-flow and Python-repr, including unclosed-brace
variants), trailing US ZIPs,
"N Locations"placeholders, work-arrangement prefixes ("hybrid in", "on-site in", "based in"). - Casing that doesn't destroy meaning.
titlecase_placepreserves acronyms (NYC,EMEA), already-uppercase state codes (Portland, OR— where.lower()would turn Oregon into a disjunction), ordinals (9th), and mixed case (McLean). - Canada, correctly.
"Toronto, ON, CA"is the single most common Canadian shape in the wild, and a naive parser reads the trailingCAas California and drops the province.country_and_regionandnormalize_location_textboth resolve Canada through one predicate so they can never disagree — while"Ontario, CA"still stays the city in California. - ISO2 codes that collide with US states.
"Bengaluru, IN"is India, not Indiana;"Bogota, CO"is Colombia, not Colorado. A colliding code only flips to the country when another token in the same string corroborates it, so"Denver, CO"is unaffected. - HTML that leaked into the location field. A scraped
"Memphis, TN <span…"keeps its city; a location field holding a whole job description resolves to nothing rather than to whatever country name the prose happened to contain. - Work arrangements standing where a city should be.
"FLORIDA - VIRTUAL - FL01"and"USA, CT, Home Office"do not yield cities called "Virtual" and "Home" — which were, respectively, the most common "cities" in our corpus before this was fixed. - Facility words stripped without truncating real towns.
"Denver Service Center"is a building;"Byron Center, Michigan"is a municipality 20 miles from Byron. Both resolve correctly. - Garbage rejection:
"Not specified","TBD","Various","Flexible"parse to nothing instead of fake places.
- Rule-based and deterministic — no geocoding service, no network, no
dependencies. Ambiguity is resolved conservatively (e.g.
"CA"is never guessed between California and Canada from casing alone). - North-America-centric admin parsing: US states and Canadian provinces are
first-class and round-trip to ISO 3166-2 subdivision codes; other international
locations resolve to
city + country.
Location parsing originally ported and adapted from Feashliaa/job-board-aggregator (MIT).
Built and maintained by SearchSteward — a job-search radar that watches 50,000+ company career pages and scores every new opening against your résumé. A location filter is only as good as its parser, so we publish the parser.
MIT. Contributions welcome — see CONTRIBUTING.md.