hiringindex
A Python client for the HiringIndex API. The API covers more than a million and a half live job postings, read directly from the applicant tracking systems employers hire on: Workday, SmartRecruiters, Greenhouse, Workable, Lever, Ashby, Recruitee, Teamtailor, Breezy and Personio. It has three endpoints. One searches postings with a filter, one returns aggregates over the same filter (salary percentiles, top employers, city, country and seniority splits, posting age), and one fetches a single posting by id. This package is a thin wrapper around those endpoints. It uses only the standard library, sends your filters to the API unchanged, and returns the JSON responses as plain dictionaries.
Install
pip install hiringindex
Requires Python 3.9 or newer. There are no other dependencies.
You need a RapidAPI key with a subscription to HiringIndex. Pass it to the client, or set it in the environment:
export RAPIDAPI_KEY=your-key
Quickstart
from hiringindex import HiringIndex
client = HiringIndex() # reads RAPIDAPI_KEY; or HiringIndex(api_key="...")
# Remote data engineering roles posted in the last week
page = client.search(job_titles=["Data Engineer"], remote_flag=["true"], days_ago=7, limit=20)
print(page["total_count"], "postings")
for job in page["jobs"]:
print(job["title"], job.get("company_name"), job.get("apply_url"))
# Berlin roles whose advertised yearly pay reaches EUR 60,000
page = client.search(
cities=["Berlin"],
salary={"min": 60000, "period": ["year"], "currency": ["EUR"], "match": "overlaps"},
limit=20,
)
# One posting by id: the `_id` from a search result, used verbatim
job = client.job(page["jobs"][0]["_id"])
# What the Software Engineer market looks like in San Francisco
market = client.insights(job_titles=["Software Engineer"], cities=["San Francisco"])
print(market["headline"]["row_count"], "rows,", market["headline"]["company_count"], "employers")
for band in market["salary"]: # one entry per currency and pay period
print(band["currency"], band.get("period"), band["count"], band.get("min", {}).get("p50"))
# Data Engineers in Germany and the Netherlands, one block per country
by_country = client.insights(
job_titles=["Data Engineer"],
country_codes=["DE", "NL"],
group_by="country_code",
percentiles=True,
)
for group in by_country["groups"]:
print(group["country_code"], group["row_count"])
Filters
Filters are keyword arguments with the API's own names. The client does not rename or check them.
| Key | Type | Matches |
|---|---|---|
job_titles |
list of str | the posting title |
keywords |
list of str | the title or the description; several keywords match as OR |
cities |
list of str | city names as employers write them |
country_codes |
list of str | ISO 3166-1 alpha-2 codes such as DE (a country name matches nothing) |
company_name |
str | the employer name, whole and case-insensitive |
handles |
list of str | ATS board handles, as the handle field reports them |
remote_flag |
list of str | ["true"] for remote, ["false"] for on-site |
employment_type |
list of str | as the vendor writes it |
seniority |
list of str | as the seniority field reports it |
source_platforms |
list of str | workday, greenhouse, lever, ashby and the other sources |
salary |
dict | min, max, currency (ISO 4217 list), period (list of year, month, week, day, hour), match (contains or overlaps) |
days_ago |
int | published no earlier than N days ago |
page, limit |
int | search only, 1 to 100 each |
insights takes the same filter without page and limit, plus group_by
(city, city_only or country_code), city_aliases, min_rows and
percentiles.
A key the API does not know is rejected with a 422 that lists the valid keys,
so a typo raises an error instead of returning the wrong slice. Salary amounts
are never converted between currencies, so send currency and period with a
salary filter.
Fields a source did not state are left out of a posting rather than set to
None, so read optional fields with job.get(...). remote_flag on a posting
is a string such as "true", not a boolean.
Paging
iter_jobs walks the pages for you and yields postings one at a time. It stops
after the last page, or after page 100, the furthest the API pages (so at most
100 * limit rows per filter). Each page is one request, made only when the
loop reaches it.
from itertools import islice
for job in client.iter_jobs(job_titles=["Data Engineer"], cities=["Berlin"], limit=100):
print(job["_id"], job["title"])
first_50 = list(islice(client.iter_jobs(keywords=["Kubernetes"], limit=50), 50))
Errors
Every error response raises HiringIndexError, with status, code,
message, meta and request_id attributes. Branch on code, not on
message, because the wording of messages may change.
from hiringindex import HiringIndex, HiringIndexError, KeywordTooCommon
client = HiringIndex()
try:
page = client.search(keywords=["excel"])
except KeywordTooCommon as err:
# The term matches too many postings. Narrowing by city or country does not help:
# search the role with job_titles, or use a rarer keyword.
print(err.term, err.estimated_matches, err.limit)
except HiringIndexError as err:
print(err.status, err.code, err.message, err.request_id)
| Status | code |
When |
|---|---|---|
| 400 | invalid_request |
the body is not a JSON object |
| 401, 403 | None |
missing or invalid key, or no subscription (answered by RapidAPI) |
| 404 | not_found |
job() with an id the API did not issue |
| 422 | invalid_request |
an unknown filter key or a malformed value |
| 422 | keyword_too_common |
raised as KeywordTooCommon, a subclass of HiringIndexError |
| 429 | None |
plan quota or rate limit reached (answered by RapidAPI) |
| 503 | busy |
capacity exhausted for the moment; wait err.retry_after_seconds |
| 503 | timeout |
the filter did not finish inside the API's time limit; narrow it or retry |
If no HTTP response arrives at all, for example on a connection failure or a
timeout, the standard library's OSError subclasses (urllib.error.URLError,
TimeoutError) propagate unchanged. The default timeout is 40 seconds, because
insights over a wide slice can take up to about 30 seconds on a cold cache.
Links
- API listing, plans and full reference: https://rapidapi.com/starnikovoleg/api/hiringindex
- Website: https://hiringindex.org
License
MIT
Metadata
Release files for hiringindex 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| hiringindex-0.1.1.tar.gz | 12.0 kB | With provenance | Details for hiringindex-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| hiringindex-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for hiringindex-0.1.1-py3-none-any.whl |
Total release size: 22.0 kB
Release files / hiringindex-0.1.1.tar.gz
| Download URL | hiringindex-0.1.1.tar.gz |
|---|---|
| Size | 12.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
98ab58264a145d3651bfb982ba236b20daed14de9a278a7d1798d0d25fe8cc38
|
|
BLAKE2b-256 checksum How to use checksums |
f75cf9126815bb9c9db075df3a3aa7c20e995f104ab178dc3a7d62d2a5e7079c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logRelease files / hiringindex-0.1.1-py3-none-any.whl
| Download URL | hiringindex-0.1.1-py3-none-any.whl |
|---|---|
| Size | 10.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9a1a5fac5eb642f8656f7ff30f5933419027f460dc8d27932be8dd0f3db24513
|
|
BLAKE2b-256 checksum How to use checksums |
ea2bb16439e8c730a07da2bbdc3e64bff61df7468f9377fdfccb7681b1fb7c7b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logSecurity for hiringindex 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for hiringindex-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.