•
2 min read
Canberra job radar

I got tired of scrolling the same listings on three sites, so I built a service that does the first pass for me.

What it does

  • On a schedule, it pulls new IT listings in Canberra from Seek, LinkedIn and Indeed, and fetches the full ad text.
  • Cheap deterministic rules run first: location, duplicates across sites, and hard requirements I cannot meet. Nothing reaches the paid API unless it survives those.
  • The rest go to an LLM together with my resume and preferences. It returns structured JSON: eligibility, a fit score, a one-line summary, and where I match or fall short.
  • A small web UI lists everything by score, and strong matches are pushed to my phone.

What turned out to matter

Rate limits. Every call shares one pacing window. It doubles on HTTP 429, honours Retry-After, and eases back after successes, so one throttle slows the whole run instead of every job hitting the limit separately. Errors that will not fix themselves, such as an exhausted quota, stop the run straight away.

Silent failure. A blocked scraper rarely throws an error. It gets a challenge page, an empty list, or 403s on the detail pages. Each source now records what it saw on every run, and a change of status sends one alert, a reminder after 24 hours if it is still broken, and one more when it recovers.

Prompt caching. The system prompt, profile and resume are identical on every call, about 2,700 tokens. The provider’s default cache breakpoint sat after the job ad, so every call wrote the whole prompt to the cache at a premium and never read it back. Splitting the message and marking the breakpoint after the fixed part made every call after the first a cache hit, and cut the input cost per call by about 60%.

Stack

Python, SQLite, an OpenAI-compatible LLM API, Telegram and ntfy for notifications, Docker Compose on my home server. It pulls its own updates from my Git server and restarts when the code changes.