@maschinenlesbar.org/jobsuche-cli
    Preparing search index...

    @maschinenlesbar.org/jobsuche-cli

    jobsuche-cli

    CI Release npm

    Website: English · Deutsch — command reference, guides and API docs

    Search Germany's federal job database from your terminal. jobsuche is a small command-line tool over the Bundesagentur für Arbeit Jobsuche API (rest.arbeitsagentur.de/jobboerse/jobsuche-service): find job listings by keyword, location, radius or employer, and fetch the full record for any posting — as clean JSON you can pipe straight into jq.

    • Just two commandssearch and details.
    • Clean JSON output — pretty-printed by default, --compact for one-line/scripting.
    • Needs one short setup step — supply the public, freely available API key once (see Authentication below).
    • Full-detail lookups — pass a refnr from any search result; the CLI base64-encodes it for the API automatically.

    Want to use this as a TypeScript library or understand how it's built? See DEVELOPING.md.

    npm i -g @maschinenlesbar.org/jobsuche-cli
    

    This installs the jobsuche command. Requires Node.js 20+.

    Check it works:

    jobsuche --help
    

    The Jobsuche API requires a static X-API-Key. No key is bundled with this package — you supply it once via the JOBSUCHE_API_KEY environment variable or per-call via --api-key. With no key the header is omitted and the API answers 401/403 (exit code 3).

    Precedence is --api-key > JOBSUCHE_API_KEY env var. A blank/whitespace key is ignored (no header sent). You do not have to go and find the key yourself — see Obtain key.

    The Bundesagentur für Arbeit publishes one static key for public use. It is not a secret — it is the same value for everyone, printed in the upstream bundesAPI/jobsuche-api README — but finding and copying it shouldn't be your job either. obtain-key reads it from that published source at run time and prints it:

    jobsuche obtain-key          # -> jobboerse-jobsuche  (provenance note on stderr)
    

    From obtaining the key to having it where it is used, in one line:

    # this shell only
    eval "$(jobsuche obtain-key --export)"

    # or keep it for later — appends one `export …` line to your shell profile
    jobsuche obtain-key --export >> ~/.zshrc # ~/.bashrc on bash

    --export prints a single shell-quoted export JOBSUCHE_API_KEY='…' line on stdout (the "obtained from …" note goes to stderr, so it never lands in your profile). The plain form composes too:

    export JOBSUCHE_API_KEY="$(jobsuche obtain-key)"
    

    Because the key is fetched rather than compiled in, a rotated key needs no release of this CLI. If the upstream source is unreachable or stops publishing a key, obtain-key fails loudly with a non-zero exit rather than printing a guess — it will never invent a value.

    With the key set in your environment, your first search:

    jobsuche search --was Informatiker --wo Berlin --size 10
    

    --was is the keyword/title, --wo the location. The result is a JSON object with stellenangebote (the listings array), maxErgebnisse (total matches), page and size. Pull out just the titles with jq:

    jobsuche search --was Informatiker --wo Berlin --size 10 \
    | jq '[.stellenangebote[] | {titel, arbeitgeber, ort: .arbeitsort.ort}]'

    Take a listing's refnr from those results and fetch its full record:

    jobsuche details 10001-1002716922-S
    
    search   [filters…]     search job listings
    details  <refnr>        full details for one listing
    
    Flag Meaning
    --was <text> job title / keyword (was)
    --wo <text> location (wo)
    --umkreis <km> radius in km around --wo (Umkreis)
    --berufsfeld <text> occupational field (Berufsfeld)
    --arbeitgeber <text> employer name (Arbeitgeber)
    --veroeffentlicht-seit <days> published within the last N days
    --angebotsart <code> offer type code, e.g. 1 regular vacancy, 4 apprenticeship
    --zeitarbeit include temp-work / staffing agencies
    --page <n> 1-based page index
    --size <n> page size

    The flag names mirror the API's German field names — the Glossary decodes every one.

    Pass the refnr from any search result (e.g. 10001-1002716922-S, a hex form like 14225-dafcdd47aabe512d-S, or a purely numeric 1002716922). The CLI base64-encodes it into the API's encryptedJobCode for you. An already-encoded code is also accepted and passed through unchanged.

    A few recipes to get going — see Usage.md for the full, use-case-driven set.

    # Jobs within 50 km of a city
    jobsuche search --was Pflegefachkraft --wo "München" --umkreis 50

    # Only listings published in the last 7 days
    jobsuche search --was Data --wo Hamburg --veroeffentlicht-seit 7

    # Regular vacancies vs. apprenticeships (angebotsart code)
    jobsuche search --was Mechatroniker --wo Stuttgart --angebotsart 1
    jobsuche search --was Mechatroniker --wo Stuttgart --angebotsart 4

    # Page through a large result set (1-based pages)
    jobsuche search --was Kaufmann --size 25 --page 1
    jobsuche search --was Kaufmann --size 25 --page 2

    # Search at a specific employer
    jobsuche search --arbeitgeber "Deutsche Bahn AG" --wo Frankfurt --umkreis 30

    Every command prints pretty JSON to stdout. Errors and diagnostics go to stderr, so piping stdout into jq stays clean.

    # How many total matches for a query?
    jobsuche search --was Pflege --wo Berlin | jq '.maxErgebnisse'

    # Sort radius results by distance
    jobsuche search --was Pflege --wo "München" --umkreis 50 \
    | jq '.stellenangebote | sort_by(.arbeitsort.entfernung)
    | .[] | {titel, ort: .arbeitsort.ort, km: .arbeitsort.entfernung}'

    # Chain search → details without copy-pasting a refnr
    jobsuche details "$(jobsuche search --was Informatiker --wo Berlin --size 1 \
    | jq -r '.stellenangebote[0].refnr')"

    Use --compact for single-line JSON in pipelines and logs:

    jobsuche --compact search --was Informatiker --size 5
    

    --compact (and every global option) works before or after the command — both jobsuche --compact search … and jobsuche search … --compact do the same thing.

    Exit codes make the CLI easy to use in scripts:

    Code Meaning
    0 success (also --help / --version)
    2 bad usage / invalid argument (nothing was sent)
    3 request rejected (401/403) — a missing or wrong API key, or a refused network
    4 listing not found (404)
    1 any other error (network/transport failure, JSON parse error, etc.)
    • command not found: jobsuche — the global npm bin directory isn't on your PATH. Run npm bin -g to find it and add it, or run via npx @maschinenlesbar.org/jobsuche-cli ….
    • Exit 3 / "request rejected" — the API declined the request. Check that JOBSUCHE_API_KEY is set and non-empty, or pass --api-key explicitly — jobsuche obtain-key gives you the current public value (see Obtain key). A 403 with an empty body is ambiguous: the gateway sends the same response for a wrong key and for a network it refuses. If the key matches what obtain-key returns, try from another network.
    • Exit 4 / "not found" — the listing no longer exists. Listings expire; re-run a fresh search to get current refnr values.
    • Exit 1 / "Network error" — connectivity, DNS, or a timeout. Try again or raise the limit with --timeout 60000.
    • Empty stellenangebote — the search matched nothing; broaden --was, widen --umkreis, or drop filters.

    These apply to every command and may be given before or after it:

    Option Description
    -V, --version Print the version number
    -h, --help Show help for the program or a command
    --compact Print JSON on a single line instead of pretty-printed
    --api-key <key> Override or supply the X-API-Key (env JOBSUCHE_API_KEY)
    --base-url <url> API base URL (default https://rest.arbeitsagentur.de)
    --timeout <ms> Per-request timeout (default 30000; at most 2147483647)
    --user-agent <ua> User-Agent header value
    --max-retries <n> Retries for transient 429/503 responses (default 2)
    --max-response-bytes <n> Cap response body size in bytes (0 = unlimited; default 100 MiB)

    If you need to point at a proxy or staging server instead of the live API:

    jobsuche --base-url https://proxy.internal.example search --was Pflege
    

    If the API redirects across an origin boundary (different scheme/host/port), the tool strips your key before following, so it never leaks to another host.

    • SKILLS.md — Claude Code Agent Skills bundled with this repo (market scan, job hunt, employer watch), installable as a plugin.
    • Usage.md — full use-case-driven cookbook.
    • GLOSSARY.md — every flag and domain term explained.
    • DEVELOPING.md — TypeScript library usage, architecture, testing, CI.

    This CLI is a client — it accesses data it does not own or redistribute. The upstream data is © its provider and licensed separately from this tool's code. See DATA_LICENSE.md.

    Warning

    Not open data. Bundesagentur für Arbeit — full copyright, use restricted to job-placement purposes, automated access forbidden by the terms. Personal lookup only; no redistribution or commercial reuse. Records may contain personal data (GDPR applies).

    Dual-licensed — use it under either:

    • AGPL-3.0-or-later (default, free). Note the AGPL's §13 network clause: if you run a modified version as a network service, you must offer that modified source to the service's users.
    • Commercial license (paid), for closed-source / proprietary or SaaS use without the AGPL's obligations.

    See LICENSING.md for details, and CONTRIBUTING.md for the contribution policy (this project does not accept external code contributions). Commercial enquiries: sebs@2xs.org.