# openroles

> Read an employer's open roles straight from its own applicant tracking system: Greenhouse, Lever, Ashby, Workday, Workable, Teamtailor, Personio, Recruitee, Rippling, JazzHR, Gem, Polymer, Manatal, Homerun, ClearCompany, or any careers site that publishes schema.org JobPosting pages. No API key. Python standard library only. CLI, Python API and MCP server.

## Use it

- CLI: `openroles <company | careers URL | board URL | ats:token> [--query WORDS] [--location TEXT] [--remote] [--since DAYS] [--limit N] [--country NAME] [--details] [--json]`
- Find the board: `openroles detect <company or URL>` prints `ats:token` lines.
- Network allowlist: `openroles hosts <target>...` (Python `openroles.hosts(target)`) prints the hosts a board's reading calls, fetching nothing for `ats:token`.
- Python: `openroles.fetch(target, query=None, location=None, remote=None, since_days=None, limit=None, details=None, country="")` returns a list of `Job`; `openroles.detect(target)` returns a list of `Board`.
- MCP: `openroles mcp` (stdio). Tools: `find_job_board(target)`, `list_open_roles(target, query?, location?, remote_only?, since_days?, limit?, include_descriptions?, country?)`.

## Targets

- `ats:token` for any system: `greenhouse:acme`, `lever:acme`, `ashby:acme`, `workday:<tenant>.wd<N>/<Site>`, `workable:acme`, `teamtailor:acme` (or the careers site's host), `personio:acme`, `recruitee:acme` (or host), `rippling:acme`, `jazzhr:acme`, `gem:acme`, `polymer:acme`, `manatal:acme`, `homerun:acme`, `clearcompany:<site id>`, `careersite:<careers site address>`.
- Board URLs: boards.greenhouse.io/<token>, jobs.lever.co/<token>, jobs.ashbyhq.com/<token>, <tenant>.wd<N>.myworkdayjobs.com/<Site>, apply.workable.com/<token>, <token>.teamtailor.com, <token>.jobs.personio.de, <token>.recruitee.com, ats.rippling.com/<token>, <token>.applytojob.com, jobs.gem.com/<token>, jobs.polymer.co/<token>, careers-page.com/<token>, <token>.homerun.co.
- A careers page URL: the board it links to or embeds is found.
- A company name: tried on every system except Workday, ClearCompany and careersite, which need their address.

## Job fields

id, ats, board, title, url, location, locations, workplace ("remote"/"hybrid"/"onsite"/""), remote (true/false/null), posted (YYYY-MM-DD), department, employment_type, compensation, deadline, description (plain text).

## Notes

- `query` words must all appear in the title. Workday also searches the board with them; a careersite skips job pages whose address names another job.
- Workday, Rippling and Polymer descriptions cost one request each: pass `details=True` (CLI `--details`) or a query.
- Ashby's `posted` is when the posting was last published; Ashby publishes no first-posted date.
- Embedding: `openroles.configure(user_agent=..., robots_agents=[...])` once at start-up.
- Errors: `BoardError` (wrong token, no answer), `RobotsDisallowed` (robots.txt says no, or asks not to be AI input).
- Polite: robots.txt honoured, Crawl-delay kept, two requests per host at a time, stops on 429/503.
