StepStone has an API, but it does not search jobs. It is for employers and their applicant tracking systems (ATS): it loads job ads onto StepStone and sends applications back to the employer. To pull StepStone or Totaljobs jobs as data, use our StepStone API; the script below saves a search to CSV.
What StepStone's API is for
StepStone's API site documents Jobfeed, where StepStone reads an employer's XML list of vacancies and publishes it, and Apply, which passes applications into the employer's ATS. Greenhouse's Stepstone Connect guide shows the setup: you send Stepstone your job board URL and a job board API key. The feed guides cover Totaljobs and Caterer.com too. If you post jobs, that is the route to use; it returns no one else's listings.
StepStone and Totaljobs jobs with ToolzerHub
We make ToolzerHub. Our StepStone API has two routes, each a GET with the x-api-key header at 1 credit a call:
/v1/stepstone/search?site=...&query=...&location=...returns 25 jobs a page.siteisstepstone_de,stepstone_at,stepstone_be,totaljobs,cwjobsorcaterer. Optional:posted_within_days,work_mode,sortandpage./v1/stepstone/job?url=...takes a job link from any of the six boards and returns the full posting with its headed sections.
export TOOLZERHUB_API_KEY=your_key
curl -G -H "x-api-key: $TOOLZERHUB_API_KEY" \
--data-urlencode "site=totaljobs" --data-urlencode "query=data analyst" \
--data-urlencode "location=London" \
"https://api.toolzerhub.com/v1/stepstone/search"
The first job in data.results, trimmed:
{
"job_id": "108280317",
"title": "Data Analyst",
"url": "https://www.totaljobs.com/job/data-analyst/lorien-resourcing-limited-job108280317",
"company": { "name": "Lorien Resourcing Limited", "id": "1203473" },
"location": "London",
"salary": { "label": "Up to £220 per day", "min": null, "max": 220, "currency": "GBP", "period": "day", "estimated": false },
"posted_at": "2026-11-04T07:51:50.473Z",
"match_type": "exact"
}
On the UK boards min and max come from the pay text the employer wrote. On StepStone Germany the range is often StepStone's own estimate, and estimated: true says so. A search total counts more than matches: that London search reported 463, which total_breakdown splits into 109 exact, 276 nearby and 78 related, and each job's match_type says which it is.
Save StepStone jobs to CSV
The script pages through one search to the board's last page and keeps each job_id once. Each row has the company, location, match type, salary figures and period, work mode and posting time. Save it as stepstone_jobs.py and install requests (pip install requests).
import csv
import os
import sys
import requests
API = "https://api.toolzerhub.com/v1/stepstone/search"
HEADERS = {"x-api-key": os.environ["TOOLZERHUB_API_KEY"]}
FIELDS = ["job_id", "title", "company", "location", "match_type", "salary", "salary_min",
"salary_max", "salary_period", "salary_estimated", "work_mode", "posted_at", "url"]
def search(site, query, location=""):
seen = set()
page = 1
while True:
params = {"site": site, "query": query, "location": location, "page": page}
body = requests.get(API, headers=HEADERS, params=params, timeout=60).json()["data"]
for job in body["results"]:
if job["job_id"] not in seen:
seen.add(job["job_id"])
yield job
if not body["has_more"] or page >= body["max_page"]:
print(f"stopped after page {page}: {body['stop_reason'] or 'last page'}")
break
page += 1
def main():
site, query = sys.argv[1], sys.argv[2]
location = sys.argv[3] if len(sys.argv) > 3 else ""
with open("stepstone_jobs.csv", "w", newline="", encoding="utf-8-sig") as out:
writer = csv.DictWriter(out, fieldnames=FIELDS)
writer.writeheader()
count = 0
for job in search(site, query, location):
salary = job["salary"] or {}
writer.writerow({
"job_id": job["job_id"],
"title": job["title"],
"company": job["company"]["name"],
"location": job["location"],
"match_type": job["match_type"],
"salary": salary.get("label"),
"salary_min": salary.get("min"),
"salary_max": salary.get("max"),
"salary_period": salary.get("period"),
"salary_estimated": salary.get("estimated"),
"work_mode": job["work_mode"],
"posted_at": job["posted_at"],
"url": job["url"],
})
count += 1
print(f"saved {count} jobs to stepstone_jobs.csv")
if __name__ == "__main__":
main()
python stepstone_jobs.py totaljobs "data analyst" London saved 100 jobs from 64 companies in 10 seconds, and python stepstone_jobs.py stepstone_de "Data Analyst" Berlin saved 249 from 165 companies in 44 seconds.
What the jobs show
- Sort order changes what a page holds. Page 1 by relevance had "data analyst" in 21 of 25 Totaljobs titles; by date, 10. On StepStone Germany, "softwareentwickler" went from 25 of 25 to 1 of 25, so relevance is the default.
- Relevance thins out after page 1: "data analyst" was in 10 of the next 75 Totaljobs titles. In Berlin, 60 jobs were exact matches and 189 related.
- One search goes 4 pages deep on the UK boards, 6 in Austria and Belgium, and 10 in Germany.
stop_reasonsays why a walk ended early. - 59 of the 100 Totaljobs jobs showed pay. 44 named a figure: 31 yearly salaries with a median midpoint of £50,000, 11 day rates with a median of £525, and 2 with no period.
- 192 of the 249 Berlin jobs carried a salary range, every one StepStone's estimate. The 182 yearly ranges had a median midpoint of €59,500.
- 152 of the Berlin jobs were marked hybrid; none of the London ones carried a work mode flag.
What it costs
A Totaljobs search of 4 pages costs 4 credits for up to 100 jobs; a Berlin search of 10 pages costs 10 credits for up to 250. The 100 free credits a month cover 25 Totaljobs searches; $19 buys 10,000 credits that never expire; plans for higher volume are on the pricing page. Set your key and run the script: the London search above took 10 seconds.
For other job boards, see Naukri job data as JSON, Bayt jobs as JSON, the Indeed API and the LinkedIn jobs API.
We are not affiliated with StepStone.