Skip to main content

Get Vacancy Facets

NL EN

Updated June 15, 2026

GET /api/v1/vacancies/facets
curl -X GET \
  "https://app.recruitsome.com/api/v1/vacancies/facets" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
const response = await fetch('https://app.recruitsome.com/api/v1/vacancies/facets', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Accept': 'application/json',
  },
});

const data = await response.json();
console.log(data);
use Illuminate\Support\Facades\Http;

$response = Http::withToken('YOUR_API_KEY')
    ->acceptJson()
    ->get('https://app.recruitsome.com/api/v1/vacancies/facets');

$data = $response->json();

The facets endpoint provides aggregated data about available filter options for vacancies. This is ideal for building dynamic search interfaces with faceted navigation, showing users what filters are available and how many results each filter would return.

This endpoint is more efficient than using ?include=facets on the main listing endpoint when you only need facet data without the actual vacancy listings — for example to render the filter sidebar before the first search.

Authentication

Requires an API key with the vacancies:read scope (included in the Career website preset). A key without this scope receives a 403 response.

Query parameters

ParameterTypeRequiredDescription
languagestringNoFilter by language code (exactly 2 characters, e.g. en, nl)
location_id[]arrayNoFilter by location ID. Accepts array notation, but only the first element is applied — extra elements are ignored.
company_location_id[]arrayNoFilter by company location ID (work site, for agency tenants). Accepts array notation, but only the first element is applied on this endpoint.
department_id[]arrayNoFilter by department ID. Accepts array notation, but only the first element is applied.
searchstringNoSearch in title and summary
experience_levels[]arrayNoFilter by experience level IDs (multiple values supported)
job_types[]arrayNoFilter by job type IDs (multiple values supported)

education_levels[] and tags[] are not accepted on this endpoint — passing them is silently ignored and does not change the counts. To get facet counts narrowed by education level or tag, use GET /vacancies?include=facets on the listing endpoint instead, which does apply those filters.

For genuinely multi-valued filtering on company_location_id[], use the listing endpoint (GET /vacancies), which applies all elements of the array.

Example

Get all available facets without any filters:

curl -X GET https://app.recruitsome.com/api/v1/vacancies/facets \
  -H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch('https://app.recruitsome.com/api/v1/vacancies/facets', {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

const facets = await response.json();
import requests

response = requests.get(
    'https://app.recruitsome.com/api/v1/vacancies/facets',
    headers={'Authorization': 'Bearer YOUR_API_KEY'}
)

facets = response.json()

Response 200

JSON
{
  "data": {
    "locations": [
      {
        "id": 1,
        "name": "Amsterdam Office",
        "city": "Amsterdam",
        "country_code": "NL",
        "country_name": "Netherlands",
        "count": 45,
        "selected": false
      },
      {
        "id": 2,
        "name": "Rotterdam Office",
        "city": "Rotterdam",
        "country_code": "NL",
        "country_name": "Netherlands",
        "count": 23,
        "selected": false
      }
    ],
    "company_locations": [
      {
        "id": 10,
        "name": "Shell Pernis Refinery",
        "city": "Rotterdam",
        "country_code": "NL",
        "country_name": "Netherlands",
        "count": 12,
        "selected": false
      },
      {
        "id": 11,
        "name": "ASML Veldhoven HQ",
        "city": "Veldhoven",
        "country_code": "NL",
        "country_name": "Netherlands",
        "count": 8,
        "selected": false
      }
    ],
    "departments": [
      {
        "id": 5,
        "name": "Engineering",
        "count": 32,
        "selected": false
      },
      {
        "id": 6,
        "name": "Sales",
        "count": 21,
        "selected": false
      }
    ],
    "experience_levels": [
      {
        "id": 2,
        "name": "Junior",
        "count": 20,
        "selected": false
      },
      {
        "id": 3,
        "name": "Medior",
        "count": 35,
        "selected": false
      },
      {
        "id": 4,
        "name": "Senior",
        "count": 28,
        "selected": false
      }
    ],
    "job_types": [
      {
        "id": 1,
        "name": "Full-time",
        "count": 68,
        "selected": false
      },
      {
        "id": 2,
        "name": "Part-time",
        "count": 12,
        "selected": false
      }
    ],
    "education_levels": [
      {
        "id": 8,
        "name": "Bachelor",
        "count": 45,
        "selected": false
      },
      {
        "id": 9,
        "name": "Master",
        "count": 28,
        "selected": false
      }
    ],
    "tags": [
      {
        "id": 1,
        "name": "Engineering",
        "count": 42,
        "selected": false,
        "sub_tags": [
          {
            "id": 5,
            "name": "Maintenance Engineer",
            "count": 25,
            "selected": false
          },
          {
            "id": 6,
            "name": "Service Technician",
            "count": 12,
            "selected": false
          }
        ]
      },
      {
        "id": 2,
        "name": "Sales",
        "count": 28,
        "selected": false,
        "sub_tags": [
          {
            "id": 10,
            "name": "Account Executive",
            "count": 15,
            "selected": false
          }
        ]
      }
    ],
    "languages": [
      {
        "code": "en",
        "name": "English",
        "count": 52,
        "selected": false
      },
      {
        "code": "nl",
        "name": "Nederlands",
        "count": 34,
        "selected": false
      }
    ]
  },
  "meta": {
    "generated_at": "2026-05-20T10:30:00+00:00",
    "filters_applied": []
  }
}

Filtered example

Get facets with active filters to see how selections affect other options:

curl -X GET "https://app.recruitsome.com/api/v1/vacancies/facets?location_id[]=1&language=en" \
  -H "Authorization: Bearer YOUR_API_KEY"
const params = new URLSearchParams({
  'location_id[]': 1,
  'language': 'en'
});

const response = await fetch(
  `https://app.recruitsome.com/api/v1/vacancies/facets?${params}`,
  {
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY'
    }
  }
);

const facets = await response.json();
import requests

params = {
    'location_id[]': 1,
    'language': 'en'
}

response = requests.get(
    'https://app.recruitsome.com/api/v1/vacancies/facets',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
    params=params
)

facets = response.json()

When filters are applied:

  • The selected field shows true for active filters
  • Counts in other facet categories are updated to reflect only the filtered results
  • The filters_applied array in meta shows which filters are active

Understanding facet counts

Without filters

When no filters are applied, each facet shows the total count of active vacancies with that attribute:

  • Location "Amsterdam Office" count 45 = total active vacancies in Amsterdam
  • Department "Engineering" count 32 = total active vacancies in Engineering

With active filters

When filters are applied, counts show how many results match both the facet AND the current filters:

  • If filtering by Amsterdam: Department "Engineering" count 12 = vacancies in Amsterdam AND Engineering
  • The Amsterdam location itself will show "selected": true

Response structure

Facet object structure

Each facet type contains an array of options with:

FieldTypeDescription
idintegerUnique identifier for the option
namestringHuman-readable name (translated)
countintegerNumber of vacancies with this attribute
selectedbooleanWhether this filter is currently active

Location facets

Location facets include additional geographic information:

FieldTypeDescription
idintegerUnique location identifier
namestringLocation name
citystring|nullCity name (from locality field)
country_codestring|nullISO 3166-1 alpha-2 country code
country_namestring|nullFull country name in current locale
countintegerNumber of vacancies at this location
selectedbooleanWhether this filter is currently active

For remote positions or locations without a specific city, the city field may be null.

Company location facets

Company location facets represent the actual work sites (client company locations) for agency tenants. This array will be empty for non-agency tenants or when no vacancies have a company location assigned. The fields are the same as the location facets above.

The company_locations facet does not include the company name to prevent accidental disclosure of client relationships. Use the include_company_name parameter on the vacancy list/detail endpoints if you need company names.

Tags facets (hierarchical)

Tags facets use a hierarchical structure with main categories containing nested sub-tags:

FieldTypeDescription
idintegerUnique tag identifier
namestringTag name (translated)
countintegerTotal vacancies in this category (including sub-tags)
selectedbooleanWhether this filter is currently active
sub_tagsarrayArray of sub-category tags

Each sub_tags item contains: id, name, count, and selected.

Zero-count entries are pruned from the tags facet: sub-tags with no matching vacancies are omitted from sub_tags, and a main tag whose entire branch (itself plus all sub-tags) has zero matches is omitted from tags altogether. Don't expect the full tag catalog here — only branches with at least one active vacancy.

Language facets

Language facets have a slightly different structure:

FieldTypeDescription
codestringISO 639-1 language code
namestringLanguage name
countintegerNumber of vacancies in this language
selectedbooleanWhether this filter is currently active

Use cases

Building dynamic filters

Use facet data to create filter interfaces that show available options and counts:

JavaScript
// Build location filter dropdown with enhanced geographic info
const locationFilter = facets.data.locations.map(location => ({
  value: location.id,
  label: `${location.name} (${location.count})`,
  description: location.city && location.country_name
    ? `${location.city}, ${location.country_name}`
    : location.country_name || '',
  disabled: location.count === 0,
  checked: location.selected
}));

Smart filter updates

When a user selects a filter, fetch updated facets to show how it affects other options:

JavaScript
async function onFilterChange(filters) {
  // Fetch new facets with current filters
  const facets = await fetchFacets(filters);

  // Update UI to show new counts
  updateFilterCounts(facets);

  // Disable options with zero results
  disableEmptyFilters(facets);
}

Pre-loading filter options

Load facets on page load to immediately show available filters:

JavaScript
// On page load
const [vacancies, facets] = await Promise.all([
  fetchVacancies({ page: 1 }),
  fetchFacets({})
]);

// Initialize filters with facet data
initializeFilters(facets);

Performance considerations

  1. Caching: Facet data changes less frequently than vacancy listings, making it ideal for caching
  2. Parallel loading: Fetch facets in parallel with initial vacancy data for faster page loads
  3. Debouncing: When implementing live filter updates, debounce facet requests to avoid excessive API calls
  4. Conditional updates: Only refetch facets when filters actually change

Error responses

401 — missing or invalid API key

JSON
{
  "message": "Unauthenticated."
}

Send a valid key as Authorization: Bearer YOUR_API_KEY.

403 — key lacks the required scope

JSON
{
  "message": "This API key does not have the required permission: vacancies:read",
  "error": "INSUFFICIENT_SCOPE",
  "required_scope": "vacancies:read"
}

Add the vacancies:read scope to your key, or regenerate it with the Career website preset.

422 — invalid filter parameter

Returned when a filter value fails validation, e.g. a location_id[] that doesn't exist:

JSON
{
  "message": "The selected location_id.0 is invalid.",
  "errors": {
    "location_id.0": [
      "The selected location_id.0 is invalid."
    ]
  }
}