Building a Job Board in an Afternoon with Next.js and JobsDataAPI
Building a job board from scratch usually means building far more than a UI. You need job ingestion, search, filters, pagination, location normalization, salary parsing, SEO pages, and a way to keep listings fresh. JobsDataAPI removes most of that infrastructure by giving you a normalized jobs API that can power a Next.js job board directly.
What You Get
The API returns stable job objects instead of raw ATS-specific responses. A typical job includes:
titlelocationlocationsapplication_urlpublishedhas_remoteemployment_typesalary_minsalary_maxsalary_currencysalary_textexperience_levelexperience_levelscompany- optional
description
That structure is enough to build cards, detail pages, filters, SEO landing pages, and analytics without writing custom parsers.
Starter Architecture
A simple Next.js job board can be built around three layers:
- API client — fetches jobs from JobsDataAPI with
Authorization: Bearer <key>. - Server actions — call the API from the server and pass typed data to the UI.
- Next.js pages — render listing pages, detail pages, feeds, and SEO content.
This keeps API keys server-side and lets the frontend work with clean JSON.
Basic Job Request
curl "$CLEANJOBDATA_API_BASE_URL/jobs?title=frontend%20engineer&remote=true&experience_level=SE&location=US&limit=20" \
-H "Authorization: Bearer $CLEANJOBDATA_API_KEY"
The response shape is:
{
"data": [],
"pagination": {
"limit": 20,
"next_page": "opaque-cursor",
"prev_page": null
},
"meta": {
"query_time_ms": 42,
"filters_applied": []
}
}
Use next_page and prev_page for cursor pagination. Preview keys may omit pagination tokens by design.
Rendering a Job Card
type Job = {
id: number;
title: string;
location: string | null;
salary_min: number | null;
salary_max: number | null;
salary_currency: string | null;
experience_level: string | null;
company: {
name: string;
logo: string | null;
} | null;
};
function formatSalary(job: Job) {
if (!job.salary_min && !job.salary_max) return null;
const currency = job.salary_currency ?? "USD";
const min = job.salary_min ? Intl.NumberFormat("en-US", { style: "currency", currency }).format(job.salary_min) : null;
const max = job.salary_max ? Intl.NumberFormat("en-US", { style: "currency", currency }).format(job.salary_max) : null;
return [min, max].filter(Boolean).join(" - ");
}
export function JobCard({ job }: { job: Job }) {
return (
<article>
<h2>{job.title}</h2>
<p>{job.company?.name}</p>
<p>{job.location}</p>
{formatSalary(job) ? <p>{formatSalary(job)}</p> : null}
{job.experience_level ? <p>{job.experience_level}</p> : null}
</article>
);
}
Keep the presentation flexible because salary and company data can be missing depending on the source.
Adding Filters
The same API powers your search and filter UI:
curl "$CLEANJOBDATA_API_BASE_URL/jobs?title=engineer&city_id=123&remote=true&experience_level=MI,SE&salary=100000,180000&sort_by=relevance" \
-H "Authorization: Bearer $CLEANJOBDATA_API_KEY"
Useful query parameters:
titleorsearchcity_id,state_id,country_id, orlocationremote=trueexperience_level=EN,MI,SE,EXsalary=min,maxpublished_after=2026-06-01max_age=7dsort_by=publishedorsort_by=relevance
The backend validates these filters and returns the applied filters in meta.filters_applied.
Detail Pages
Request the description only when you need it:
curl "$CLEANJOBDATA_API_BASE_URL/jobs/123?fields=title,location,description,company,application_url,published,salary_min,salary_max" \
-H "Authorization: Bearer $CLEANJOBDATA_API_KEY"
The detail route should link to application_url for the actual employer application flow.
SEO and Feeds
Normalized data makes SEO pages easier. You can generate pages for:
- remote jobs
- senior engineer jobs
- city-specific feeds
- state-specific feeds
- salary-filtered feeds
- company-domain feeds
Each page can use the same API filters and render a unique intro, problem statement, and FAQ from your content catalog.
Production Checklist
Before launching, verify:
- API key is stored server-side, never in client bundles.
- Pagination uses
next_pageandprev_page. - Missing salary, company, and location values are handled gracefully.
- Expired jobs are excluded by default.
- Location filters use
city_id,state_id,country_id, orlocationcountry codes. - Detail pages request
descriptiononly when needed. - SEO pages use stable slugs and canonical job data.
The Fast Path
JobsDataAPI gives you the data layer for a modern job board: normalized jobs, reliable filters, cursor pagination, and optional descriptions. Pair that with a Next.js App Router frontend and you can ship a useful job board without building scrapers, parsers, or a search infrastructure from scratch.