Fiber AI
Search & discovery

Job Search

Filter 13M+ open roles by title, pay, and location, then count matches before you pull postings.

Job Search

We built job search so you can treat open roles as data: titles, compensation, seniority, location, and whether the posting is still active. Use it to pull the postings themselves, or as a hiring-intent signal: companies staffing up for roles your product supports.

We cover 13M+ postings, about 1M new per week. Search and count share the same searchParams object; count is how you size a query before you pay for rows.

Each result reads like a listing on a major job board: title, company, location, pay when listed, and whether the req is still open, plus a job_url linking to the full posting. You never need an account on any job board.

Filter by employer the way you'd scan a careers page: Microsoft, Amazon, Google, Salesforce, Stripe — via domain, LinkedIn company URL, slug, or org ID.

This is job postings, not people changing jobs. Role-change alerts live on Tracker (person_changed_company and related career rules). To find candidates from a job description, paste it into natural-language search. US hourly and trade listings are a separate API: Blue-collar job search.

What it does

  1. Filter postings by company, title, status, posted date, applicants, function, industry, pay, experience, modality, employment type, seniority, and country/region.
  2. Count first with the job-posting count: one credit, totalJobsFound, no posting payload.
  3. Page through matches with job-posting search. You pay per posting returned.
  4. Read structured fields on each hit: description, salary range, location, modality, applicant range, and the posting URL.

Operations

JobOperationHTTPWhen to use
CountjobPostingSearchCountPOST /v1/job-search/countIterate filters; "how many?" before pulling rows
SearchjobPostingSearchPOST /v1/job-searchFetch posting records (pageSize 1–1000, default 25)

Both routes allow 120 requests / minute. Count has no pageSize or cursor.

Call functions from @fiberai/sdk. Request and response Zod schemas live on @fiberai/sdk/zod — JobPostingSearchData ↔ zJobPostingSearchData, same pattern for count. Parse untrusted input with those schemas; the function import stays small if you never load /zod.

import {
  jobPostingSearch,
  jobPostingSearchCount,
} from "@fiberai/sdk";
import type {
  JobPostingSearchCountData,
  JobPostingSearchData,
} from "@fiberai/sdk";
import {
  zJobPostingSearchCountData,
  zJobPostingSearchData,
} from "@fiberai/sdk/zod";

const searchParams: JobPostingSearchCountData["body"]["searchParams"] = {
  title: ["Account Executive"],
  isActive: "true",
  jobModality: ["Remote"],
  countryOrRegionCode: ["USA"],
  postedAt: {
    strategy: "relative",
    window: { method: "within", period: "day", upperBound: 30 },
  },
};

const countRequest: JobPostingSearchCountData =
  zJobPostingSearchCountData.parse({
    body: {
      apiKey: process.env.FIBER_API_KEY!,
      searchParams,
    },
  });

const count: Awaited<ReturnType<typeof jobPostingSearchCount>> =
  await jobPostingSearchCount(countRequest);

console.log("Matches:", count.data?.output.totalJobsFound);

const searchRequest: JobPostingSearchData = zJobPostingSearchData.parse({
  body: {
    apiKey: process.env.FIBER_API_KEY!,
    pageSize: 25,
    searchParams,
  },
});

const page: Awaited<ReturnType<typeof jobPostingSearch>> =
  await jobPostingSearch(searchRequest);

console.log("First title:", page.data?.output.data[0]?.title);
console.log("Next page:", page.data?.output.nextCursor);
console.log("Cost:", page.data?.chargeInfo);

Search vs count

CountSearch
Credits1 per request (flat)1 per posting returned (default)
PayloadtotalJobsFounddata[] plus nextCursor
PaginationNonecursor in, nextCursor out
Empty matchesStill 1 credit0 credits for an empty page (unfilled pageSize is refunded)

Unlike company count and people count, job count is not free. It is still the cheap way to tighten filters before you pull descriptions.

Filters (searchParams)

Search and count take the same object. Every field is optional; {} is a valid unfiltered query. Unknown fields are rejected (strict schema).

FilterShapeWhat it does
companiesDiscriminated unionJobs at specific companies. One identifier type per request — do not mix slugs with domains in the same object
titlestring[]Partial match on job titles (e.g. ["Software Engineer", "Manager"])
isActive"true" | "false" | "no_preference"Active only, closed only, or both
postedAtDate selectionWhen the role was posted (absolute range or relative window)
numApplicants{ lowerBound?, upperBound? }Applicant-count range
jobFunctionsenum[]Function categories (Engineering, Sales, Operations, …)
industriesenum[]Standardized industries (Software, Healthcare, Finance, …)
annualSalaryUsd{ lowerBound?, upperBound? }Annual pay in USD
yearsOfExperience{ lowerBound?, upperBound? }Experience required
jobModality"On-site" | "Remote" | "Hybrid"[]Match any listed modality
employmentTypeenum[]Full-time, Part-time, Contract, Temporary, Internship, Volunteer, Other
seniorityLevelenum[]Entry level, Associate, Mid-Senior level, Director, Executive, Internship
countryOrRegionCodestring[]ISO 3166-1 alpha-3 countries (USA, IND, GBR) and/or region codes (X-APAC, X-LATAM, X-ANGLOSPHERE, X-EMEA, …)
jobLocationTypesame as modalityDeprecated. Use jobModality

Full enum members for functions, industries, and region codes are on the OpenAPI pages — send the canonical strings, not free-text synonyms.

Company identifiers

Pick one identifier; value is always an array:

identifiervalue examples
linkedinSlug["microsoft"]
domain["microsoft.com"]
linkedinUrl["https://www.linkedin.com/company/microsoft/"]
linkedinOrgID["1035"] (numeric strings)
{
  "companies": {
    "identifier": "domain",
    "value": ["stripe.com"]
  }
}

Posted date (postedAt)

Discriminated on strategy:

Absolute — a fixed window, regardless of when you run the query:

{
  "strategy": "absolute",
  "range": { "lowerBound": "2026-01-01", "upperBound": "2026-03-31" }
}

Relative — a sliding window from today. Prefer method: "within" with period of day | week | month | quarter | year and at least one of lowerBound / upperBound (counts of that period ago). method: "calendar" with which: "current" | "previous" pins to this or last calendar period.

{
  "strategy": "relative",
  "window": { "method": "within", "period": "day", "upperBound": 14 }
}

What a posting contains

Each item in data includes job_id, title, company_name, company_logo_url, posted_at, job_url, description, status (active | closed), modality, seniority_level, employment_type, job_function, standard_industries, applicant and experience ranges, compensation_range (currency + hr / m / yr / daily), and annual_salary_usd. Location is standardized_location (city, region, country, coordinates).

job_location_type on the response is deprecated; read modality.

Pagination

Pass nextCursor from a response back as cursor. Treat the token as opaque — do not construct or edit it. null means no further page. A bad cursor returns 400 (Invalid cursor value.) and refunds the hold.

Using it effectively

  • Count, then search. Broad title arrays plus a large pageSize is how spend spikes.
  • Prefer jobModality over jobLocationType.
  • Resolve companies with typeaheads before filling companies, so slugs and domains are ones we can match.
  • Honor HTTP 429 (120/min). Failed searches refund the credit hold.
  • chargeInfo is authoritative — published rates can differ per org.

Use cases

  • GTM / AI SDR: companies actively hiring for a role your product supports (the /search catalog frames this as hiring-intent, 1 credit per posting).
  • Recruiting intel: compensation, seniority, and remote mix on live reqs.
  • Competitive staffing: titles and volume at a named competitor (companies).
  • Trend checks: repeat the same searchParams on the job-posting count over time without downloading descriptions.

In the dashboard, the same idea is a company filter: companies whose postings match titles, seniority, pay, modality, and status — see Using the UI. That finds companies; these endpoints return postings.

Trade and blue-collar listings

Hourly and trade roles are a different product. See Blue-collar job search, built for US hourly, warehouse and trade roles.

Credits

OperationDefault credits
jobPostingSearch1 per posting returned
jobPostingSearchCount1 per request

See Billing & credits.

Credit amounts are defaults — custom pricing is available for bulk use cases.

Related: Blue-collar job search · Agentic search · Typeaheads · Tracker · MCP · SDKs

On this page