2026-05-18 14:14:33 -04:00
2026-05-18 14:14:33 -04:00

build a small python cli tool called usajobs.py for exploring usajobs results.

goal:

  • query the official usajobs api
  • apply strict local filters
  • show results in a readable terminal table
  • let me select rows to export into an org-mode file

stack:

  • python 3.11+
  • click for cli args
  • requests for api
  • rich for table + row selection prompt
  • pathlib/json/csv stdlib only otherwise

env vars:

  • USAJOBS_EMAIL
  • USAJOBS_KEY

basic command: python jobs.py search --location "Washington, DC" --radius 25 --salary-min 150 --grade-min 15 --grade-max 15 --series 2210 --series 0340 --clearance 3 --clearance 4

option behavior:

  • --radius 25 means 25 miles
  • --salary-min 150 means $150,000
  • --grade-min/--grade-max filter locally against low/high grade
  • --series may repeat; pass to api as semicolon list
  • --clearance may repeat; pass to api as semicolon list
  • --pay-plan may repeat, default gs and gg
  • --limit defaults 100
  • --out defaults jobs.org
  • --cache-dir defaults .cache/usajobs

search behavior:

  1. call https://data.usajobs.gov/api/search using official headers: host: data.usajobs.gov user-agent: $USAJOBS_EMAIL authorization-key: $USAJOBS_KEY
  2. request fields=full, resultsperpage=500, sortfield=opendate, sortdirection=desc
  3. cache raw json response per query/page under .cache/usajobs
  4. apply local filters after fetching:
    • pay plan in allowed pay plans
    • low_grade >= grade_min
    • high_grade <= grade_max
    • salary max >= salary_min, or salary min >= salary_min if max absent
    • location string contains/near requested location as available
  5. output table with columns: idx, title, agency, grade, salary, location, close date, clearance match, url
  6. selection/export:
  • after displaying table, allow user to arrow/highlight and mark for export
    • pick sensible defaults, eg x or m for mark, u for unmark, a for all, e for export
  • export selected jobs to new file with short slug name + datetime stamp
  • org output format:
** <shortened job title> [[url][link]]
:properties:
:agency: <agency>
:grade: <pay plan> <low>-<high>
:close_date: <date>
:end:

salary: <salary range>
location: <location>
travel: <travel percentage or unknown>
clearance: <clearance/security text or unknown>

*** posting
<raw posting text>
  1. (stretch) Cache each query, allow arrow/scroll through them like a cli, recall, or save filters. could be too much.

implementation notes:

  • write clean functional code:
    • build_params()
    • fetch_page()
    • fetch_all()
    • normalize_job()
    • passes_filters()
    • render_table()
    • parse_selection()
    • export_org()
  • normalize both official api shape and frontend-ish shape if present:
    • api jobs may use MatchedObjectDescriptor
    • details may be under UserArea.Details
  • raw posting text should combine title, summary, duties, requirements, qualifications, evaluations, other info, key requirements.
  • shortened job title should be max 80 chars, strip all-caps screaming where reasonable, preserve meaning.
  • include helpful errors if env vars missing.
  • include a --offline flag that only reads cached json and does not call api.
  • include a --debug flag that prints api params and counts before/after filtering.

acceptance test:

  • running the command with --salary-min 150 --grade-min 15 --grade-max 15 --radius 25 should not show gs/gg-13 jobs after local filtering.
  • selecting none exits without writing.
Description
better way to interact w usajobs api
Readme 45 KiB
Languages
Python 100%