• Headless APIs
  • Search APIs
  • Search API Basics
  • Search APIs

    Search API Basics

    Generally Available in Liferay DXP 2025.Q4+/Portal 2026.Q1+

    You can search for content from a Liferay search page, but you can also use the /search API endpoints. If you’re running Liferay locally, while logged in you can visit http://localhost:8080/o/api?endpoint=http://localhost:8080/o/search/v1.0/openapi.json to explore the API.

    Use the included commands to generate and search for sample content. The commands below work with basic authentication and assume that you are running Liferay locally at http://localhost:8080 and authenticating as an administrator with the email test@liferay.com and the password learn.

    Run the following command to generate a blog post:

    curl \
       -H "Content-Type: application/json" \
       -X POST \
       "http://localhost:8080/o/headless-delivery/v1.0/sites/L_GUEST/blog-postings" \
       --data-raw '{"articleBody": "Foo", "headline": "Able"}' \
       -u "test@liferay.com:learn"
    
    Note

    The default site in a Liferay bundle or Docker container uses the external reference code (ERC) L_GUEST. If your instance does not have a site with the L_GUEST ERC, replace it in the above call with the site ID or ERC you’re targeting.

    The /search API contains GET and POST endpoints for returning a search results page. Both are demonstrated and discussed here.

    Simple Query

    Here is a simple GET query for the keyword able:

    curl \
    	"http://localhost:8080/o/search/v1.0/search?search=able" \
    	--user "test@liferay.com:learn"
    

    Here is a simple POST query for the keyword able:

    curl \
    	-H "Content-Type: application/json" \
    	-X POST \
    	"http://localhost:8080/o/search/v1.0/search?search=able" \
    	-d "{}" \
    	-u "test@liferay.com:learn"
    

    In both cases the response returns a search result with information about the blog post.

    {
      "items" : [ {
        "dateModified" : "2023-07-20T17:15:32Z",
        "description" : "Foo",
        "itemURL" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/35384",
        "score" : 318.95966,
        "title" : "Able"
      } ],
      "lastPage" : 1,
      "page" : 1,
      "pageSize" : 20,
      "totalCount" : 1
    }
    

    Simple Query with Embedded Items

    To return not only the search result, but the returned entity’s fields according to its own API schema, set the nestedField parameter to embedded. This applies to both GET and POST searches. For example, this POST query for the keyword able also requests embedded items:

    curl \
    	-H "Content-Type: application/json" \
    	-X POST \
    	"http://localhost:8080/o/search/v1.0/search?nestedFields=embedded&&search=able" \
    	-d "{}" \
    	-u "test@liferay.com:learn"
    

    The response now returns many more details on the blog post:

    {
      "items" : [ {
        "dateModified" : "2023-07-20T17:15:32Z",
        "description" : "Foo",
        "embedded" : {
          "actions": {
            "get-rendered-content-by-display-page": {
              "method": "GET",
              "href": "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/38975/rendered-content-by-display-page/{displayPageKey}"
            },
            "get": {
              "method": "GET",
              "href": "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/38975"
            },
            "replace": {
              "method": "PUT",
              "href": "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/38975"
            },
            "update": {
              "method": "PATCH",
              "href": "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/38975"
            },
            "delete": {
              "method": "DELETE",
              "href": "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/38975"
            }
          },
          "alternativeHeadline" : "",
          "articleBody" : "Foo",
          "creator" : {
            "additionalName" : "",
            "contentType" : "UserAccount",
            "familyName" : "Test",
            "givenName" : "Test",
            "id" : 20123,
            "name" : "Test Test"
          },
          "customFields" : [ ],
          "dateCreated" : "2023-07-20T17:15:32Z",
          "dateModified" : "2023-07-20T17:15:32Z",
          "datePublished" : "2023-07-20T17:15:00Z",
          "description" : "Foo",
          "encodingFormat" : "text/html",
          "externalReferenceCode" : "f73109ce-8db6-36e3-f2c7-4505c6454ed8",
          "friendlyUrlPath" : "able",
          "headline" : "Able",
          "id" : 35384,
          "keywords" : [ ],
          "numberOfComments" : 0,
          "relatedContents" : [ ],
          "renderedContents" : [ ],
          "siteId" : 20119,
          "taxonomyCategoryBriefs" : [ ]
        },
        "itemURL" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/35384",
        "score" : 318.95966,
        "title" : "Able"
      } ],
      "lastPage" : 1,
      "page" : 1,
      "pageSize" : 20,
      "totalCount" : 1
    }
    

    You can build more complex requests using the supported parameters and request body properties.

    Search Parameters

    Query parameters can be used to control the results further.

    ParameterNotesMethod
    entryClassNamesA comma separated list of entryClassNames to be searched. Defaults to all searchable types.GET
    POST
    fieldsThe fields parameter requests only specific fields to be enumerated in each of the elements in the response.GET
    POST
    nestedFieldsSupports embedded to get back nested data.GET
    POST
    restrictFieldsExcludes the given field(s) from being returned.GET
    POST
    filterFilters across different fields. Supported fields appear in the table below. For more robust filtering options, use a search blueprint (DXP subscription).GET
    POST
    pageSpecify which page to return.GET
    POST
    pageSizeSpecify how many items you want per page.GET
    POST
    scopeSpecify a list of sites (by ID or ERC) to search. You can mix IDs and ERCs in the same request.GET
    POST
    searchSearch by keyword(s).GET
    POST
    sortSort by ascending or descending order. Supported fields appear in the table below.GET
    POST
    blueprintExternalReferenceCodeSpecify a search blueprint to customize the search request. POST requests must instead use a request attribute.GET
    emptySearchSet this to true to return results when no search keywords are entered in the search parameter.GET
    Warning

    Search Blueprints can also add sort configurations to the search page. Make sure you understand how these blueprint-added sorts interact with the sort parameter if you plan to use both.

    See API Query Parameters for more information.

    Filter Fields

    If they’re present in the headless entity’s response, you can filter and/or sort by these fields:

    API FieldCorresponding Search Index FieldParameter
    creatorIduserIdfilter
    folderIdfolderIdfilter
    groupIdsgroupIdfilter
    objectDefinitionIdobjectDefinitionIdfilter
    objectDefinitionExternalReferenceCodeobjectDefinitionExternalReferenceCodefilter
    objectFolderExternalReferenceCodeobjectFolderExternalReferenceCodefilter
    statusstatusfilter
    taxonomyCategoryIdsassetCategoryIdsfilter
    dateCreatedcreateDate
    createDate_sortable
    filter
    sort
    dateDisplaydisplayDate
    displayDate_sortable
    filter
    sort
    dateExpirationexpirationDate
    expirationDate_sortable
    filter
    sort
    dateModifiedmodified
    modified_sortable
    filter
    sort
    datePublishpublishDate
    publishDate_sortable
    filter
    sort
    dateReviewreviewDate
    reviewDate_sortable
    filter
    sort
    extensionextensionfilter
    keywordsassetTagNames.lowercasefilter
    sort
    scopeGroupIdscopeGroupIdfilter
    titlelocalized_title_[locale].keyword_lowercasefilter
    sort

    This reference reflects the latest quarterly release. Field availability may vary between versions and entities; verify specific field support within your environment’s API schema.

    Important
    • The corresponding search index field names appear for convenience. They can change without notice between versions. See Exploring Indexed Fields to learn more about the indexed fields.
    • The headless API field keyword refers to the entity’s tag names.

    Filtering by Status

    DXP 2025.Q3+

    By default only approved items are returned when searching (i.e., items with a status of 0). Items with other workflow statuses can be returned by filtering on the status field. For example, a filter value for returning both approved and pending items is status eq 0 or status eq 1.

    You cannot filter web content articles by status. There’s a built-in filter that ensures only head versions of the content can be returned by a search. That means only the latest approved content is available to search.

    Search Request Body

    GET searches do not include a request body. For POST searches, empty requests are allowed (e.g., specify {} as the request body), but there are two properties available for influencing the response:

    PropertyDescription
    attributesSet available search context attributes to configure a search blueprint or enable empty search. See Available Search Request Attributes for details.
    facetConfigurationSet the facet configuration to return facets in the response. See Adding Facet Configurations to the Request.

    Adding Attributes to the Search Request

    To search with a blueprint, use this request body syntax:

    {
      "attributes": {
        "search.experiences.blueprint.external.reference.code": ""
      }
    }
    

    Custom Search Request Attributes

    You can add a custom attribute to the API request, then consume its value in a search blueprint. With custom attributes, the search request can receive dynamic contextual data and pass it to a blueprint. In the blueprint you may want to filter by the value, use it to configure the query, or add it to a condition. This allows for creating personalized search experiences using contextual data.

    Prefix custom attributes with search.experiences. This example adds the freshnessInteger and its value to the search request:

    {
      "attributes": {
        "search.experiences.freshnessInteger": 30
      }
    }
    

    Declare the attribute as a parameter in the blueprint, with its type:

       {
          "parameters": {
             "search.experiences.freshnessInteger": {
                "type": "Integer"
             }
          }
       }
    

    Use the value in an element. For example, query for content that was published in the last 30 days, using a range query with the createDate field:

    "query": {
       "range": {
          "createDate": {
             "gte": "now-"${search.experiences.freshnessInteger}d/d",
             "lte": "now/d"
          }
       }
    }
    

    Available Search Request Attributes

    You can set these attributes into the request:

    PropertyDescription
    search.empty.searchSet this to true to return results even if the search parameter is omitted from the request.
    DXP Only
    search.experiences.blueprint.external.reference.code
    (Preferred) Set a search blueprint to control the search query and configuration.
    DXP Only
    search.experiences.blueprint.id
    Set a search blueprint to control the search query and configuration.
    DXP Only
    search.experiences.ip.address
    Set automatically. Only use this to test blueprints with geolocation configured, to simulate different locations.
    DXP Only
    search.experiences.scope.group.id
    Set this when your blueprint uses an element that requires it: Limit Search to the Current Site, Boost Contents in a Category for a User Segment, or Staging Aware.
    DXP Only
    search.experiences.*
    You can pass dynamic data to blueprints-backed headless search requests using custom attributes. Prefix these attributes with search.experiences. and declare them in the blueprint’s parameter configuration.

    Adding Facet Configurations to the Request

    To search with a facet configuration, use this request body syntax:

    {
      "facetConfigurations": [
        {
          "aggregationName": "",
          "attributes": {},
          "frequencyThreshold": "",
          "maxTerms": "",
          "name": "",
          "values": []
        }
      ]
    }
    

    A facet configuration can have several properties:

    PropertyDescription
    aggregationNameChoose a unique name for the aggregation. This is required to distinguish between instances of the same type (i.e., with the same name property).
    attributesSome facets require additional attributes.

    field: The custom, date-range, and nested facets require a String attribute called field to set the field to aggregate results by.
    format and ranges: The date-range facet also requires a format String to specify the date format (e.g., yyyyMMddHHmmss) and a ranges object array to provide the ranges.
    filterField, filterValue, and path: The nested facet requires a filterfield String, a filtervalue String, and a path String.
    vocabularyIds: The vocabulary facet requires a String array of vocabularyIds.
    frequencyThresholdSet the minimum frequency required for terms to appear in the list of facet terms.
    maxTermsSet the maximum number of facet terms to display, regardless of how many matching terms are found for the facet.
    nameSet the type of facet:
    category
    custom
    date-range
    folder
    nested
    site
    tag
    type
    vocabulary
    user
    valuesPost filter the results by selecting values. This is like clicking facet terms in the facet widget.
    Note

    The custom facet recognizes top level fields. Objects and web content structure fields are indexed as nested fields, so you must choose the nested facet for these entities.

    For example, here’s a date-range facet’s ranges attribute:

    {
      "ranges": [
        {
          "label": "range-1",
          "range": "[20220411085757 TO 20230413075757]"
        },
        {
          "label": "range-2",
          "range": "[20230409085757 TO 20230413075757]"
        }
      ]
    }
    

    Aggregations and Search Facets in the Response

    GET searches do not return facets. For POST searches, You can see aggregations and search facets in the API response. To see aggregations,

    1. Add aggregations to a search blueprint.

    2. Set the attribute search.experiences.blueprint.external.reference.code in your search request.

    Search facets are returned if you add a facet configuration to the request. For example, this request body asks for a tag facet:

    {
      "facetConfigurations": [
        {
          "name": "tag"
        }
      ]
    }
    

    In the response, the returned search facet looks like this:

    "searchFacets": {
      "tag": [
        {
          "displayName": "business",
          "term": "business",
          "frequency": 26
        },
        {
          "displayName": "fun",
          "term": "fun",
          "frequency": 1
        }
      ]
    }
    

    Enabling Guest Access to the Search API

    To enable guest access to the API, create a new service access policy as follows:

    FieldEntry
    NameSEARCH
    EnabledChecked
    DefaultChecked
    TitlePublic Access to Search API
    Service Class Namecom.liferay.portal.search.rest.internal.resource.v1_0.SearchResultResourceImpl
    Method Name[postSearchPage OR getSearchPage]