The Hub APIthehub.io ↗
Search and retrieve startup job listings from thehub.io. Filter by keyword, location, role, job type, remote, and paid. Fetch full job details including salary and company info.
What is the The Hub API?
The Hub API exposes 2 endpoints for accessing startup job listings published on thehub.io. search_jobs returns paginated results (15 per page) with facet counts across roles, job types, and remote/paid filters, while get_job delivers full job details including salary indication, equity, company profile, and the external application link when available.
curl -X GET 'https://api.parse.bot/scraper/9beee6a0-d82c-4ac6-9f7e-ec5b873deea5/search_jobs?query=engineer' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace thehub-io-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""Walkthrough: TheHub Jobs SDK — search startup jobs, drill into details."""
from parse_apis.thehub_io_api import TheHub, Role, Sorting, JobNotFound
client = TheHub()
# Search for engineering jobs sorted by newest, capped at 5 results.
for job_summary in client.job_summaries.search(
query="engineer", roles=Role.ENGINEER, sorting=Sorting.NEW_JOBS, limit=5
):
print(job_summary.title, "|", job_summary.company.name, "|", job_summary.url)
# Drill down: take one hit and navigate to its full detail.
hit = client.job_summaries.search(query="backend", limit=1).first()
if hit is not None:
detail = hit.details()
print(detail.title, detail.salary, detail.equity)
print("Remote:", detail.is_remote, "| Country:", detail.country_code)
print("Description length:", len(detail.description_html))
# Scalar page view preserves facets and featured jobs alongside results.
page = client.job_summaries.search_page(country_code="DK")
print(f"{page.total} jobs across {page.pages} pages")
for role_code, count in page.facets.roles.items():
print(f" {role_code}: {count}")
for featured in page.featured_jobs:
print("Featured:", featured.title, featured.company.name)
# Point lookup by a known id; handle not-found gracefully.
if hit is not None:
try:
full = client.jobs.get(job_id=hit.id)
print(full.title, "expires", full.expiration_date)
except JobNotFound:
print("Job no longer listed")
print("exercised: job_summaries.search / job_summaries.search_page / jobs.get / details")
Searches the job offers published on The Hub and returns one page of matching jobs (15 per page, fixed by the site) plus the site's separately promoted featured jobs for the same search and facet counts (jobs per role, per job type, remote, paid) over the whole result set. All filters are optional; with none given the endpoint lists every open position in the default 'Other Europe' (EU) country scope, sorted by popularity. Pagination is caller-controlled through page (1-based); page defaults to 1 and has_more tells whether page+1 exists. total, pages and facets describe the full filtered result set, not just the returned page. Remote filtering: remote_only=true (or country_code=REMOTE) replaces the country scope with remote positions only. Each job id can be passed unchanged to get_job. A search that matches nothing returns total 0 and an empty jobs array.
| Param | Type | Description |
|---|---|---|
| page | integer | 1-based result page; each page holds up to 15 jobs. |
| query | string | Free-text job title or keyword. Omitted = no keyword filter. |
| roles | string | Comma-separated role codes to filter on (e.g. engineer,devops); the scraper splits on commas, do NOT pass an array. Omitted = all roles. |
| sorting | string | Result ordering, matching the site's sort dropdown. |
| location | string | City or area as free text, in the form the site's location box produces, e.g. one shape is 'Aarhus, Denmark'. Omitted = whole country scope. |
| job_types | string | Comma-separated job type codes to filter on (e.g. full_time,part_time); the scraper splits on commas, do NOT pass an array. Omitted = all job types. |
| paid_only | boolean | true restricts results to paid positions. Omitted/false = no paid filter. |
| remote_only | boolean | true restricts results to remote positions (overrides country_code). Omitted/false = positions in the selected country scope. |
| country_code | string | Country scope of the search, matching the site's country selector (EU = Other Europe, REMOTE = remote jobs only). Ignored when remote_only=true. |
{
"type": "object",
"fields": {
"jobs": "array of job summaries: id (pass to get_job), key (URL slug), title, url, location {country, locality, address} (nulls for remote jobs without an office location), is_remote, job_types (array of JobType codes), is_featured, views_week, views_total, company {id, key, name, website, number_of_employees, founded, logo_url}",
"page": "integer, the page returned",
"pages": "integer, total number of pages",
"total": "integer, number of jobs matching the whole search",
"facets": "object: roles (role code -> job count), job_types (JobType code -> job count), remote (count of remote jobs), paid (count of paid jobs) over the whole result set",
"has_more": "boolean, whether page+1 exists",
"page_size": "integer, jobs per page (15)",
"featured_jobs": "array of promoted job summaries (same shape as jobs) shown alongside the search; may be empty"
},
"sample": {
"data": {
"jobs": [
{
"id": "6a9f507f64eac57366bf2ffc",
"key": "lunar-1-senior-backend-engineer-8327879",
"url": "https://thehub.io/jobs/lunar-1-senior-backend-engineer-8327879",
"title": "Senior Backend Engineer",
"company": {
"id": "61b70f0b59c1540629b6d307",
"key": "lunar-1",
"name": "Lunar",
"founded": "2015",
"website": "https://www.lunar.app/en/personal",
"logo_url": "https://thehub.io/files/s3/20211213085354-844fb13062f372e692a0594472d3d638.jpeg",
"number_of_employees": "200+"
},
"location": {
"address": "Aarhus, Denmark",
"country": "Denmark",
"locality": "Aarhus"
},
"is_remote": false,
"job_types": [
"full_time"
],
"views_week": 366,
"is_featured": false,
"views_total": 1040
}
],
"page": 1,
"pages": 1,
"total": 4,
"facets": {
"paid": 4,
"roles": {
"analyst": 1,
"engineer": 4,
"customerservice": 1,
"backenddeveloper": 2
},
"remote": 5,
"job_types": {
"full_time": 4
}
},
"has_more": false,
"page_size": 15,
"featured_jobs": []
},
"status": "success"
}
}About the The Hub API
What the API covers
The Hub is a Nordic-focused startup job board. The API surfaces the same job data available on thehub.io: titles, locations, company details, job types, salary and equity indications, and application links. Coverage includes both standard listings and the site's featured (promoted) jobs, which are returned separately in the featured_jobs array on every search_jobs call.
search_jobs endpoint
search_jobs accepts a query string for keyword filtering, a location free-text field (e.g. 'Aarhus, Denmark'), comma-separated roles and job_types filter codes, remote_only and paid_only boolean flags, and a page integer for pagination. The response includes a jobs array of summaries (each carrying an id for use with get_job), total and pages counts, a has_more flag, and a facets object breaking down result counts by role code, job type code, remote count, and paid count across the whole result set — not just the current page.
get_job endpoint
get_job takes a single job_id (the 24-character hex identifier from search_jobs) and returns the complete job record. Key fields include the full HTML description, salary and equity as free-text strings published by the employer, is_remote, status (e.g. ACTIVE), and a company object with name, website, what_we_do, number_of_employees, founded, and logo_url. When the job is hosted externally, the external apply URL is included. Publication and expiry dates are also returned.
Pagination and filtering notes
Page size is fixed at 15 by the site and cannot be changed via the API. The remote_only flag overrides any country_code filter. Role and job type filters use code strings (e.g. engineer, full_time) passed as comma-separated values — do not pass an array. Omitting query returns all jobs matching the other active filters.
The The Hub API is a managed, monitored endpoint for thehub.io — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when thehub.io changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official thehub.io API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Aggregate Nordic startup job listings filtered by role code (e.g.
engineer,devops) into an internal talent tracker - Monitor new remote-only positions on thehub.io by polling
search_jobswithremote_only=true - Build a salary and equity benchmarking dataset from the
salaryandequityfields returned byget_job - Feed a job alert system using
totalandpagesfromsearch_jobsto detect new listings for a given keyword - Enrich a company database with startup metadata (
founded,number_of_employees,what_we_do) from thecompanyobject inget_job - Surface featured/promoted jobs separately from organic results using the
featured_jobsarray insearch_jobs
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does thehub.io offer an official developer API?+
What does the facets object in search_jobs actually contain?+
facets object contains four keys: roles (a map of role code to job count), job_types (a map of job type code to job count), remote (integer count of remote jobs), and paid (integer count of paid jobs). These counts reflect the full result set for the query, not just the current page, so you can use them to build filter UI without fetching every page.Can I retrieve company profiles or founder information independent of a job listing?+
get_job response, within the company object. There is no standalone company search or profile endpoint. You can fork this API on Parse and revise it to add a dedicated company lookup endpoint.What are the pagination limits, and can I change page size?+
page parameter or any other input. Use has_more, pages, and total from search_jobs to iterate through results. The page parameter is 1-based.Does the API return application deadline or expiry dates for jobs?+
get_job response. They are not returned in the search_jobs summary array; you need to call get_job with the job's id to retrieve them.