Search Jobs¶
Search lets you find jobs from multiple sources. Adzuna API is the primary source (free, structured JSON). Seek and LinkedIn scrapers are available as fallback.
Usage¶
# Default: Adzuna API, interactive country/city selection
uv run kairos jobs search "software engineer" -i
# Direct flags
uv run kairos jobs search "rust developer" --country AU --location Perth
# Multiple platforms
uv run kairos jobs search "backend" -c AU -l Sydney --platforms adzuna,seek,linkedin
# With filters
uv run kairos jobs search "python" -c US -l "San Francisco" \
--salary-min 120000 \
--job-type fulltime \
--posted-within 7 \
--max-results 30
Search Criteria¶
| Field | Flag | Description | Example |
|---|---|---|---|
| Keywords | (positional) | Role titles or skills | "software engineer" |
| Country | --country, -c |
Country code | AU, US, UK, CA, NZ |
| Location | --location, -l |
City from predefined list | Perth, Sydney, "New York City" |
| Interactive | --interactive, -i |
Pick country & city from numbered list | |
| Platforms | --platforms, -p |
Comma-separated sources | adzuna, seek, linkedin |
| Salary Min | --salary-min |
Minimum salary | 70000 |
| Job Type | --job-type, -t |
Employment type | fulltime, parttime, contract |
| Posted Within | --posted-within, -d |
Days since posted | 7, 14, 30 |
| Max Results | --max-results, -n |
Maximum jobs to return | 50 |
Available Cities¶
Each country has 15 predefined cities (14 cities + Remote):
| AU | US | UK | CA | NZ |
|---|---|---|---|---|
| Sydney | New York City | London | Toronto | Auckland |
| Melbourne | Los Angeles | Manchester | Vancouver | Wellington |
| Brisbane | Chicago | Birmingham | Montreal | Christchurch |
| Perth | San Francisco | Leeds | Calgary | Hamilton |
| Adelaide | Seattle | Edinburgh | Ottawa | Tauranga |
| ... | ... | ... | ... | ... |
Use --interactive (-i) to see the full list.
How It Works¶
sequenceDiagram
participant U as User
participant CLI as CLI
participant API as kairos-api
participant AZ as Adzuna API
participant SK as Seek Scraper
participant DB as PostgreSQL
U->>CLI: kairos jobs search "developer"
CLI->>API: POST /api/v1/jobs/search
API->>AZ: GET api.adzuna.com/v1/api/jobs/au/search/1
API->>SK: GET seek.com.au/developer-jobs
AZ-->>API: JSON (structured)
SK-->>API: HTML (parsed)
API->>DB: UPSERT jobs (deduplicated, owner-scoped)
API-->>CLI: Job list
Data Sources¶
Adzuna API (Primary)¶
- Method: HTTP API calls to
api.adzuna.com - Auth: Free API key from developer.adzuna.com
- Data: Structured JSON — title, company, location, salary, description
- Coverage: AU, US, UK, CA, NZ + 14 more countries
- Limits: ~250 requests/day (free tier)
- Reliability: High — official API, no scraping
Seek.com.au (Fallback Scraper)¶
- Server-side rendered pages with embedded Apollo GraphQL JSON
- Rate limit: 2-5 second delay between requests
- ~22 results per page
- No login required
LinkedIn (Fallback Scraper)¶
- Public job listings via guest API (no login required)
- Rate limit: 2-6 second delay between requests
- 25 results per page
~~Indeed~~ (Deferred)¶
- Requires headless browser (Cloudflare protection)
- High ban risk — deferred in favor of Adzuna API
Deduplication¶
Jobs are deduplicated by (owner_id, platform, platform_job_id) using PostgreSQL UPSERT. Re-running a search updates the same user's existing jobs rather than creating duplicates; two users can independently track the same listing without colliding.
Web frontend¶
The same search is available from the web app: every CLI flag (--country, --location, --platforms, --salary-min, --job-type, --posted-within, --max-results) has a UI control. Press Enter to submit; per-platform errors render inline so a Seek scraping failure doesn't hide successful Adzuna results.
Work Rights¶
Jobs carry three work rights fields (populated during JD analysis):
| Field | Meaning |
|---|---|
| PR Required | Employer requires permanent residency |
| Citizenship Required | Employer requires citizenship |
| Visa Sponsorship | Employer is willing to sponsor a visa |
These fields are nullable — many JDs don't state requirements explicitly.