WebService::CareJobs
Raku client for the CareJobs.Now healthcare jobs API. Search open US healthcare jobs, see the hourly pay employers post, and look up employers and states.
- Free, no API key.
- Raku 6.d. Depends only on
JSON::Fast. Sends requests with the curl command, which is part of macOS, Windows 10 and later, and nearly every Linux. - One method per question, a
CareJobsKind enum, one X::CareJobs exception. - The same API is a remote MCP server at https://carejobs.now/api/mcp, so any MCP client can use it too.
Where the data comes from
CareJobs.Now is a free US healthcare job board. Its jobs are read daily from the career sites of US hospitals, health systems, clinics and care employers, and the pay is what each posting states (hourly rates before shift differentials and bonuses). Every job links to its page on CareJobs.Now, where the user applies. Data and docs: https://carejobs.now/data.
Data from CareJobs.Now (https://carejobs.now), CC BY 4.0.
Install
zef install WebService::CareJobs
Quick start
use WebService::CareJobs;
my $cj = WebService::CareJobs.new;
my $jobs = $cj.search-jobs(:query<ICU>, :kind(Nurses), :state<TX>, :limit(5));
say "$jobs<total> jobs match";
for $jobs<jobs>.list -> $job {
say "$job<title> | $job<employer> | $job<location> | $job<url>";
}
my $pay = $cj.pay-by-kind(Nurses);
say "Median posted pay for nurses: \$$pay<pay><median>/hr";
# Every answer also carries the server's readable summary:
say $pay.text;
A runnable version is in examples/search.raku: raku -Ilib examples/search.raku OH.
Methods
Every method returns a CareJobs::Result: the tool's data as a hash ($result<jobs>, or $result.data) plus $result.text, a readable summary written by the server. The keys are the ones the API sends (typical_low, open_jobs, and so on). Where the API sends null, the value is an undefined Any. $result.to-json gives the data back as JSON.
Every argument is optional unless said otherwise, and leaving one out leaves that filter off. A kind may be a CareJobsKind value (Nurses) or its string ("nurses").
| Method | What it answers |
|---|
search-jobs(:query, :kind, :employer, :state, :remote, :min-hourly-usd, :sort, :limit) | Open jobs. Data: total, pay, jobs, more. :limit is 1 to 25. :sort is mixed, newest or highest_pay. |
pay(:kind, :employer) | Posted hourly pay: every kind, one kind with the employers posting the most, one employer, or both. |
pay-by-kind($kind) | pay for one kind of job. |
job-market() | How many jobs are open, new in the last 7 days, and the open jobs by kind, state and employer. |
jobs-by-state($state?, :kind, :limit) | Every state, one state, or one kind of job in one state (a kind needs a state). |
states($state?, :kind, :limit) | Alias of jobs-by-state. |
employers(:query, :type, :care, :state, :kind, :limit) | The employers, AI-training programs and job boards the site reads. :limit is 1 to 100. |
employer($slug) | One employer by slug ("kaiser-permanente") or name. |
kinds() | The 17 kinds of job as slugs. |
tools() | The server's tools with their input schemas. |
call-tool($name, %arguments) | Any tool, for a tool this version does not know yet. |
my $ohio = $cj.states('OH', :kind(NursingAssistants));
say "$ohio<open_jobs> CNA jobs in Ohio";
for $cj.employers(:query<Kaiser>, :limit(3))<entries>.list -> $entry {
say "$entry<name> ($entry<open_jobs> open jobs)";
}
Options
my $cj = WebService::CareJobs.new(
base-url => 'https://carejobs.now', # default
timeout => 60, # seconds, default 60
user-agent => 'my-app/1.0', # default "carejobs-raku/<version>"
);
The server answers a query it has not seen before in up to about 25 seconds, then repeats of it in a fraction of one, which is why the default timeout is a minute.
Errors
Every failure throws X::CareJobs. Its reason says which kind:
$cj.jobs-by-state(:kind(Nurses)); # a kind without a state
CATCH {
when X::CareJobs {
say "{.reason}: {.message}";
# "tool": the server ran the tool and said no
# "rpc": a JSON-RPC error (.rpc-code)
# "http": a non-2xx status (.status)
# "network", "timeout": no answer
# "protocol": the answer was not what MCP promises
}
}
Kinds
Nurses (nurses), NursePractitioners (nurse-practitioners), Physicians (physicians), NursingAssistants (nursing-assistants), Caregivers (caregivers), TherapyRehab (therapy-rehab), MentalHealth (mental-health), Pharmacy (pharmacy), Imaging (imaging), Lab (lab), SurgicalTechs (surgical-techs), EmtsParamedics (emts-paramedics), MedicalAssistants (medical-assistants), Dental (dental), Admin (admin), AlliedHealth (allied-health), AiTraining (ai-training).
Tests
prove6 -l t # offline, with a fake transport
CAREJOBS_LIVE=1 prove6 -l t # also calls the live API
Plain JSON
Without this library the same data is plain JSON: https://carejobs.now/roles.json (open jobs by kind of job and employer) and https://carejobs.now/pay.json (posted hourly pay by kind of job).
License
Code: MIT, copyright CareJobs.Now. Data: CC BY 4.0, credit CareJobs.Now.