GuidesAPI EndpointsAPI Changelog
Live AI Demo
Guides

Job Searching

Index, retrieve, and display HrFlow.ai's Job Searching results.

In this article, we'll take a look at how HrFlow.ai Job Searching API works by interacting with the endpoint ๐Ÿง  Search Jobs indexed in Boards. We are going to test queries through our public HrFlow.ai Postman collection.

The final goal is to allow you to seamlessly integrate HrFlow.ai into your website structure to deliver fast and personalized Job Searching experiences.

๐Ÿ“˜

API Endpoint

Get more information about the endpoint ๐Ÿง  Search Jobs indexed in Boards.

Step 1: Index Jobs in your Boards

Jobs in HrFlow.ai are indexed in special folders called Boards. You can refer to ๐Ÿ”Œ Create a Board to create a Board.

First, you need to adapt your data to the HrFlow.ai Job Object format. All the required fields and values structures are defined at ๐Ÿ“– Job Object. Then, your Job Object is indexed in your desired Board using the indexing endpoint.

You can find a more thorough description of the indexing endpoint at ๐Ÿ’พ Index a Job in a Board.

๐Ÿ“˜

Index Job with a reference

  • Specifying a reference eases Job updates in HrFlow.ai
  • This reference is optional and uniquely identifies a Job in HrFlow.ai.
  • Job always has a unique identification key (Job key) generated by HrFlow.ai.
๐Ÿšง

Indexed Jobs in HrFlow.ai must be kept up-to-date

To prevent outdated results from our Searching API, ensure updating all Jobs on which changes have taken place. Check here for more details on how to ๐Ÿ’พ Edit a Job indexed in a Source.

Step 2: Get your First Searching Results with Postman

We are now ready to use the HrFlow.ai Searching API to make multi-criteria query searches over one or multiple Boards of Jobs.

Let's dig deeper into our Job Searching endpoint possibilities. In the following, we are going to use our Postman Collection and focus on the endpoint that allows us to ๐Ÿง  Search Jobs indexed in Boards.

๐Ÿ“˜

HrFlow.ai Postman

Check our publication on HrFlow.ai Postman to get started with our Postman collection.

Run in Postman

1. Configure your Postman environment

Once you have followed the first steps described in this article, you will land on this page:

2880

First, click on the "Environments" tab on the left side of your Postman window. Then, fill in the Empty - Environment template with the correct values. The compulsory variables for Job Searching are:

Finally, save the environment and ensure that you selected Empty - Environment as your current environment.

1920

2. Multiple Criteria Search in a Board

Now that the environment is selected, we can try our first request to Search Jobs in the specified Board by board_key. Let's try a default request:

1920

Now let's add two filters :

  • names: we are looking for Data Scientist Jobs, names = ["Data Scientist"]
  • skills: we are looking for Jobs requiring Python skills, skills = [{"name":"python", "value":null}]
1920
๐Ÿ“˜

Retrieving query results with pagination

Pagination enables you to have complete control over how you retrieve query results. For example, you can fetch a specific page by using the page parameter, which serves as a page offset combined with the limit parameter that specifies the number of profiles per page.
We strongly recommend using a reasonably low value for the limit and leveraging the pagination system to retrieve all your queries results.

3. Multiple Criteria Search in Multiple Boards

Now, let's search Jobs in two Boards. To do so, we can simply :

  1. Add its board_key to the list of board_keys
  2. Send the request and see the new retrieved hits
1920

For further details on query structure and the different criteria used to filter Jobs in selected Boards, you can refer to ๐Ÿง  Search Jobs indexed in Boards.

๐Ÿšง

Sorting Results

  • By default, the results are sorted with regards to jobs' creation date: sort_by=created_at. Thus, the results start from the most recent to the oldest.
  • Sorting by searching* relevance requires at least one criteria**.

Step 3: Integrate and Display Job Searching Results in your Website

You can integrate the overall setup into one place where users can set multi-criteria filters. First, the Job Searching endpoint is called with the underlying query. Then, the results are displayed in a more user-friendly format.

Hereโ€™s a new code demo using Plain HTML, Plain CSS, and Vanilla Javascript showing how to display Jobs in a website page leveraging HrFlow.ai Job Searching results.

<div id="app">
  <div class="jobs" id="jobs">
  </div>
</div>
#app {
  font-family: -apple-system,system-ui,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';
  letter-spacing: .01em;
  -webkit-font-smoothing: antialiased;
  font-feature-settings: 'calt' 0;
  overflow-x: hidden;
  background: #eee
}

.jobs {
  width: 50%;
  margin: auto;
  padding: 1rem;
}
.card {
  width: 100%;
    padding: 1.5rem;
    border-radius: 5px;
    transition: all 0.2s cubic-bezier(0.41, 0.094, 0.54, 0.07) 0s;
    border-width: 1px;
    border-style: solid;
    border-color: rgb(238, 238, 238);
    box-shadow: rgba(0, 0, 0, 0.05) 1px 2px 4px;
    backface-visibility: hidden;
    background-color: rgb(255, 255, 255);
    border-image: initial;
    margin-bottom: 1.5rem;
}
.content {
    width: 100%;
    display: flex;
    justify-content: flex-start;
    text-decoration: none;
    color: #000;
    position: relative;
}

.info {
    flex: 1 1 auto;
}

.info__company {
    color: #4badad;
    font-size: 14px;
    letter-spacing: 1px;
    font-weight: 600;
}

.info__title {
    margin: 1rem 0 0.5rem 0;
    max-width: 22rem;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-weight: 700;
    font-size: 22px;
    line-height: 24px;
}

.info_details {
    display: flex;
    justify-content: flex-start;
    padding: 0;
    list-style: none;
    color: rgba(151, 153, 157, 0.7);
    margin: 0;
    font-size: 14px;
    letter-spacing: 1px;
}

.details__item {
    margin-right: 1rem;
    max-width: 8rem;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.skills {
    margin-top: 1rem;
    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
}

.skills__item {
    border: 1px solid #4badad;
    color: #4badad;
    border-radius: 2px;
    margin: 4px 6px 2px 0;
    padding: 5px 8px;
    background-color: #fff;
    font-size: 12px;
    border-radius: 2px;
    transition: .4s;
    cursor: pointer;
    display: flex;
    align-items: center;
}
const axiosHrflow = axios.create({
  baseURL: 'https://api.hrflow.ai/v1',
  headers: {
    'X-API-KEY': 'YOUR_SECRET_API_KEY'
  }
});

const  buildQueryString =  (url, queryObject) => {
  let queryString = `${url}?`;
  Object.keys(queryObject).forEach(item => {
  if (typeof queryObject[item] === 'string'
      || queryObject[item] instanceof String) {
    queryString += `${item}=${queryObject[item]}&`;
  } else {
    queryString += `${item}=${JSON.stringify(queryObject[item])}&`;
  }
});
    return queryString; 
}

const displayJobs = jobs => {
  const jobsListHtml =  jobs.map(job => {
    const name = job.name
    const location = job.location.text
    const company = job.tags.filter(tag => tag.name === 'company')[0] && 
          job.tags.filter(tag => tag.name === 'company')[0].value || '';
    const category = job.tags.filter(tag => tag.name === 'category')[0] && 
          job.tags.filter(tag => tag.name === 'category')[0].value || '';
    const type = job.tags.filter(tag => tag.name === 'type')[0] && 
          job.tags.filter(tag => tag.name === 'type')[0].value || '';
    return (
      `<div class="card" key={job.key}>
        <div class="content">
          <div class="info">
            <div class="info__company">
              ${company}
            </div>
            <div class="info__title">
              ${name}
            </div>
            <div class="info__details">
              <span class="details__item">
                <i class="icon fa fa-map-marker-alt"></i>
                ${location}
              </span>
              <span class="details__item">
                <i class="icon fa fa-briefcase"></i>
                ${category}
              </span>
              <span class="details__item">
                <i class="fa fa-file-alt"></i>
                ${type}
              </span>
            </div>
            <div class="skills">
              ${job.skills.map(skill => {
                return (`<span key=${skill.name} class="skills__item">
                          ${skill.name}
                         </span>`
                        )
                  }).join(' ')
                }
             </div>
            </div>
          </div>
        </div>`
    )
  }).join(' ');
  const appElmt = document.getElementById('jobs');
  appElmt.innerHTML = jobsListHtml
}

const query = {
   board_keys: ['YOUR_BOARD_KEY'],
   tags_included: [[], []],
   name: "",
   limit: 10,
   page: 1,
   sort_by: 'searching',
   order_by: 'asc',
   location_distance: 30,
   location_geopoint: {},
   use_agent: 0,
   totalPage : 0,
   status: true,
 }

 const url = buildQueryString('jobs/searching', query);

axiosHrflow.get(url)
  .then( res => {
  const fetchedJobs = { 
    jobs: res.data.data.jobs,
    meta: res.data.meta
  }
  displayJobs(fetchedJobs.jobs);
}).catch( err => {
  console.log('error', err)
});

Advanced Topics

1. Add Custom Attributes

You can further adapt your Job Object by adding custom attributes relevant to your business needs:

  • Tags: uniquely identified by their name and value
  • Float Ranges
  • Data Ranges
    The example below shows how two additional Tags, contract_type and entity, are integrated within a HrFlow.ai Job Object.
{
  "tags": [
    {
      "name": "contract_type",
      "value": "Full Time"
    },     
    {
      "name": "entity", 
      "value": "R&D"
    }
  ]
}
{
  "key": "8450511f364122d3967b04704b165efa0c9816e8",
  "reference": null,
  "name": "Data Scientist",
  "url": "",
  "summary": "",
  "location": {
    "text": "",
    "lat": null,
    "lng": null,
    "gmaps": null,
    "fields": null
  },
  "archived_at": null,
  "updated_at": "2021-12-10T15:18:46+0000",
  "created_at": "2021-12-10T15:18:46+0000",
  "sections": [],
  "skills": [],
  "languages": [],
  "certifications": null,
  "courses": null,
  "tasks": null,
  "tags": [
    {
      "name": "contract_type",
      "value": "Full Time"
    },
    {
      "name": "entity",
      "value": "R&D"
    }
  ],
  "metadatas": [],
  "ranges_float": [],
  "ranges_date": []
}

2. Try it in your Favourite Programming Language

You might want to try the same experience in your favourite programming language. Luckily, Postman automatically provides us with the correct code corresponding to the request in several programming languages.

All you have to do is:

  1. Go to the code tab in the upper right corner,
  2. Then select your target programming language from the dropdown,

And voilร , all you have to do now is copy and paste this code and try it out straight away.

1920

3. Aggregating Job Search Results with the facets Request Parameter

The facets parameter turns a Job Searching request into an aggregation: instead of ranked jobs, it returns counts grouped by a field โ€” how many matching jobs require each skill, which languages they ask for, how they are distributed over time, or where they are located. This is what powers faceted-search sidebars, filters, and dashboards over your Boards.

facets is a top-level parameter โ€” a sibling of the other search filters like board_keys or text_keywords โ€” and it takes a list of field names. When you pass a non-empty list, the request becomes facets-only: the counts are returned under meta, and data.jobs is empty (no ranking is performed). Omit facets for a normal ranked search.

Because a faceted request does not return jobs, results and facets are two separate calls to the same endpoint. This keeps each call fast โ€” a facet request never pays the cost of ranking.

Facetable fields

FieldDescription
tagscounts for every custom Tag defined on the Board
skills, languages, certifications, coursescounts per value
text_languagecounts per value
statushow many jobs are enabled vs. disabled (true / false)
created_at, updated_atdistribution over time
locationgeographic heatmap
tracking_actionstracking actions

Requesting any other field name returns a 400.

Example 1: Top skills and languages

facets

["skills", "languages"]

Request:

curl -G 'https://api.hrflow.ai/v1/jobs/searching' \
  -H 'X-API-KEY: <YOUR_API_KEY>' \
  -H 'X-USER-EMAIL: <YOUR_USER_EMAIL>' \
  --data-urlencode 'board_keys=["<BOARD_KEY>"]' \
  --data-urlencode 'facets=["skills","languages"]'

Response (excerpt):

{
  "code": 200,
  "meta": {
    "total": 13139,
    "facets": {
      "skills":    [ { "value": "sql", "count": 1806 }, { "value": "java", "count": 1637 }, { "value": "agile", "count": 1565 } ],
      "languages": [ { "value": "anglais", "count": 521 }, { "value": "francais", "count": 133 } ]
    }
  },
  "data": { "jobs": [] }
}

Explanation:

  • Counts per value: each entry is a { value, count } โ€” how many of the total matching jobs have that value.
  • Facets-only response: data.jobs is empty because a non-empty facets computes counts, not ranked results.
  • meta.total: the number of matched jobs the counts are computed over.

Example 2: Drilling down (facets react to your filters)

Any standard search filter narrows the set the counts are computed over. Here we keep only jobs that require the skill "sql", then facet on skills again to see which other skills those jobs ask for:

curl -G 'https://api.hrflow.ai/v1/jobs/searching' \
  -H 'X-API-KEY: <YOUR_API_KEY>' \
  -H 'X-USER-EMAIL: <YOUR_USER_EMAIL>' \
  --data-urlencode 'board_keys=["<BOARD_KEY>"]' \
  --data-urlencode 'skills=[{"name":"sql"}]' \
  --data-urlencode 'facets=["skills"]'

Explanation:

  • Combine filters and facets: skills, text_keywords, tags, geo filters, dates, and even raw_filters all narrow the population the counts run over.
  • Faceted navigation: this is the loop behind a facet sidebar โ€” the user clicks a value, you re-send the request with that value as a filter, and the remaining counts update.

Faceting on custom Tags and status

facets: ["tags"] is a generic request: it facets on every Tag defined on the Board at once. The counts come back under meta.facets, keyed by Tag name:

"facets": {
  "contract_type": [ { "value": "Full Time", "count": 3120 }, { "value": "Internship", "count": 486 } ],
  "department":    [ { "value": "Engineering", "count": 1902 } ]
}

status is a boolean facet โ€” it splits the matched jobs into enabled and disabled:

"facets": { "status": [ { "value": "true", "count": 12750 }, { "value": "false", "count": 389 } ] }

Facet response types

Facets are returned under meta, grouped by type:

meta keyFed byShape
facetstags, skills, status, and other keyword / boolean fields{ value, count }
numeric_facetsnumber Tags{ min, max, count, buckets: [{ min, max, count }] }
date_facetscreated_at, updated_at, date Tags{ interval, buckets: [{ key, key_as_string, count }] }
geo_facetslocation{ "geopoint": [{ key, count, centroid: { lat, lon } }] }
  • numeric โ€” min / max / count describe the overall range (ideal for a slider); buckets is a histogram, each bucket a [min, max) range.
  • date โ€” key is epoch milliseconds, key_as_string is ISO 8601; the interval is chosen automatically.
  • geo โ€” each key is a geohash cell; plot the centroid weighted by count to render a heatmap.

Running a faceted request in Postman

To test the facets parameter through Postman, follow these steps:

  • Step 1: Open the HrFlow.ai Collection: navigate to the HrFlow.ai Public Workspace in Postman. Under the Job section, select the ๐Ÿง  Search Jobs indexed in Boards endpoint.
  • Step 2: Set Up the Environment: make sure your environment has x-api-key, x-user-email, and board_key configured.
  • Step 3: Add the facets Parameter: in the request parameters, add the facets field with a JSON array as its value, for example ["skills", "languages", "status"].
  • Step 4: Send the Request: click Send. The meta section of the response will contain the facet counts, and data.jobs will be empty.
  • Step 5: Drill Down: add a filter alongside facets (for example skills = [{"name":"sql"}]) and re-send to watch the counts recompute over the narrowed set.

Faceting is especially useful for building filter sidebars and analytics on top of your Boards โ€” request only the fields you need, and let the counts guide your users through the results.


Whatโ€™s Next

Did this page help you?