Skip to content
WP Engine |Headless Platform

Using the Find API

The Find API allows you to search for documents in your index using a powerful GraphQL interface. You can perform full-text searches, apply strict filters, sort results, and use advanced features like semantic search, geographic filtering, and aggregations.

This guide will walk you through the API’s capabilities, starting with basic examples and gradually introducing more advanced topics.

The simplest way to search is by providing a search term to the query parameter. This performs a full-text search across the indexed fields of your documents.

query FindSimpleQuery {
find(query: "comedy sports") {
total
documents {
id
score
data
}
}
}

The query returns a SearchResult object containing:

  • total: The total number of documents that match the query, regardless of pagination.
  • documents: An array of matching documents, each with its id, relevance score, and the document data (which includes fields like post_title, post_content, post_type, categories, author, etc.).

Query vs. Filter: Understanding the Difference

Section titled “Query vs. Filter: Understanding the Difference”

It is crucial to understand the distinction between the query and filter parameters. Though they can be used together, they serve different purposes.

The query parameter is primarily designed for full-text searching. It uses a flexible and powerful syntax that makes it ideal for processing user-generated search input from a search bar.

When used in conjunction with the semanticSearch parameter, the query string is used to perform a semantic or hybrid search, searching by meaning and intent rather than just keywords.

The syntax supports the following operators:

  • Logical Operators:
    • AND requires all terms to be present (case-sensitive)
    • OR requires at least one term to be present (case-sensitive)
    • NOT excludes documents containing the term (case-sensitive)
    • + requires a term to be present
    • | signifies OR
    • - excludes a term
  • Default Operator: If no operator is specified, OR is used by default. For example, red leather couch is interpreted as red | leather | couch.
  • Phrase Queries: Wrap text in double quotes (") to search for an exact phrase (e.g., "gourmet coffee beans").
  • Wildcards: Use ? to replace a single character and * to replace zero or more characters (e.g., qu?ck bro*).
  • Precedence Grouping: Use parentheses (( and )) to group clauses and control operator precedence. For example, (red | blue) AND shoes will search for documents that contain the term shoes and either red or blue.
  • Escaping Characters: To use one of the special operator characters literally, escape it with a preceding backslash (\). Reserved characters include: + - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /

The filter parameter is used for strict, structured filtering. It applies exact-match conditions to narrow down the dataset before the main query is executed. It uses a strict parser, and any syntax errors will prevent the query from running.

The syntax supports features like:

  • Field Targeting: post_type:post. You can use dot notation (e.g., author.user_nicename:"admin") to target nested fields.
  • Boolean Operators: AND, OR, and NOT (also written as &&, ||, and !) for combining clauses. For example:
    • post_type:post AND author.user_nicename:"admin"
    • categories.name:Comedy OR categories.name:Drama
    • post_type:post AND NOT categories.name:Drama
  • Required/Prohibited Terms: Use + (must be present) and - (must not be present). For example:
    • +post_type:post -categories.name:Drama (must be post, must not be Drama category)
  • Grouping: Use ( ) to group clauses and control precedence. For example:
    • (categories.name:Comedy OR categories.name:Drama) AND post_type:post
  • Wildcards: Use ? to replace a single character and * to replace zero or more characters:
    • qu?ck matches “quick” or “quack”
    • auth* matches “author”, “authority”, “authentication”, etc.
    • Note: A maximum of 5 wildcards (* or ?) are allowed per filter query
  • Ranges: Inclusive ranges with [min TO max] and exclusive ranges with {min TO max}:
    • ID:[10 TO *] finds documents with ID 10 or greater
    • post_date_gmt:[2024-01-01 TO 2024-12-31] finds documents in 2024
    • Simplified syntax: ID:>10, ID:>=10, ID:<10, ID:<=10
    • Combined: ID:(>=10 AND <20) or ID:(+>=10 +<20)
  • Exists Check: Check for the presence of a field using _exists_:title.
  • Escaping Special Characters: Use backslash (\) to escape reserved characters: + - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /
    • Example: \(1\+1\)\=2 to search for the literal string “(1+1)=2”

Think of filter as the tool for applying non-negotiable criteria, such as filtering by a specific category, post type, or other structured data.

Here is an example that demonstrates how to use both parameters together. This query searches for “modern furniture” but only within documents that are of the post type.

query CombinedQueryAndFilter {
find(query: "coach", filter: "post_type:post AND categories.name:Sports") {
total
documents {
id
data
}
}
}

The query parameter supports a rich syntax to give you fine-grained control over your search.

You can combine terms with logical operators using symbols or keywords.

  • AND: Requires all terms to be present. Use the AND keyword.

    query: "comedy AND sports"
  • OR: Requires at least one of the terms to be present. Use the OR keyword or | symbol. If no operator is specified, OR is the default.

    query: "comedy OR drama"

    Or using the | symbol:

    query: "comedy | drama"

    Note: If no operator is specified, OR is the default:

    query: "comedy drama" // This is equivalent to "comedy | drama"
  • NOT: Excludes documents containing the term. Use the NOT keyword or - symbol.

    query: "sports NOT football"

    Or using the - symbol:

    query: "sports -football"
  • Required Term: Use + to require a specific term to be present.

    query: "+coach"

    This ensures “coach” must be in the results.

You can combine these operators for complex queries:

query: "(comedy OR drama) AND (coach NOT soccer)"

Use wildcards for pattern matching.

  • Wildcards: Use ? for single character and * for multiple characters.
    query: "qu?ck bro*"
    This matches “quick brown”, “quack brown”, “quick bronze”, etc.
  • Exact Phrases: Use double quotes for exact phrase matching.
    query: "\"quick brown fox\""

Use parentheses to control operator precedence and create complex queries.

query: "(quick | fast) AND fox"

This requires “fox” and either “quick” or “fast” to be present.

query: "(New York | Los Angeles) AND (restaurant | cafe)"

This finds documents containing either “New York” or “Los Angeles” AND either “restaurant” or “cafe”.

When filtering on text fields, you may need to distinguish between a partial match and an exact match. For example, a filter on categories.name:Car might incorrectly match both “Car” and “Car Sales”.

To ensure an exact match, append .keyword to the field name. This forces the filter to match the entire term precisely.

query KeywordFilterQuery {
find(query: "coach", filter: "categories.name.keyword:Comedy") {
total
documents {
id
}
}
}

This query will only return documents where the category is exactly “Comedy”.

You can target a filter to a specific field.

query FieldSpecificFilter {
find(query: "*", filter: "post_title:\"Ted Lasso\"") {
total
documents {
id
}
}
}

Combine multiple filter conditions using boolean operators.

query BooleanFilterQuery {
find(
query: "*"
filter: "post_type:post AND (categories.name:Comedy OR categories.name:Sports) AND NOT categories.name:Drama"
) {
total
documents {
id
}
}
}

You can also use the + and - operators:

query RequiredProhibitedFilter {
find(
query: "*"
filter: "+post_type:post -categories.name:Drama +author.user_nicename:admin"
) {
total
documents {
id
}
}
}

Use wildcards in filter expressions for pattern matching.

query WildcardFilterQuery {
find(query: "*", filter: "post_title:Ted*") {
total
documents {
id
}
}
}

This matches titles starting with “Ted” like “Ted Lasso”, “Ted Talk”, etc.

You can filter for values within a range in a specific field. Inclusive ranges are specified with square brackets [min TO max] and exclusive ranges with curly brackets {min TO max}.

query RangeFilterQuery {
find(query: "*", filter: "comment_count:[5 TO 19]") {
total
documents {
id
}
}
}

This finds documents where the comment_count field has a value from 5 to 19, inclusive.

You can mix inclusive and exclusive bounds:

query MixedRangeFilterQuery {
find(query: "*", filter: "price:[100 TO 200}") {
total
documents {
id
}
}
}

This finds prices from 100 (inclusive) up to but not including 200.

query UnboundedRangeFilterQuery {
find(query: "*", filter: "comment_count:[10 TO *]") {
total
documents {
id
}
}
}

This finds documents where comment_count is 10 or greater.

For simpler range queries, you can use comparison operators:

query SimplifiedRangeQuery {
find(query: "*", filter: "age:>18") {
total
documents {
id
}
}
}

Available operators: >, >=, <, <=

To combine upper and lower bounds:

query CombinedRangeQuery {
find(query: "*", filter: "age:(>=18 AND <65)") {
total
documents {
id
}
}
}

Filter by date ranges using the same syntax:

query DateRangeFilterQuery {
find(query: "*", filter: "post_date_gmt:[2024-01-01 TO 2024-12-31]") {
total
documents {
id
}
}
}

This finds documents where the post_date_gmt is within the year 2024.

You can also include a time component in the date range:

query DateTimeRangeFilterQuery {
find(
query: "*"
filter: "post_date_gmt:[2024-01-01T00:00:00 TO 2024-01-01T12:00:00]"
) {
total
documents {
id
}
}
}

This finds documents published in the first 12 hours of January 1st, 2024.

Or use simplified syntax for dates:

query SimpleDateRangeQuery {
find(query: "*", filter: "post_date_gmt:>2024-01-01") {
total
documents {
id
}
}
}

The orderBy parameter is an array of objects that specify the sorting criteria.

query OrderByDate {
find(
query: "my search query"
orderBy: [{ field: "post_date_gmt", direction: desc }]
) {
documents {
id
}
}
}

To sort by a string field, you must append .keyword to the field name.

query OrderByString {
find(
query: "my search query"
orderBy: [{ field: "post_title.keyword", direction: asc }]
) {
documents {
id
}
}
}
query OrderByNumber {
find(
query: "my search query"
orderBy: [{ field: "price", direction: asc }]
) {
documents {
id
}
}
}

You can provide multiple sorting clauses. Each subsequent clause is used as a tie-breaker.

query OrderByMultiple {
find(
query: "my search query"
orderBy: [
{ field: "price", direction: asc }
{ field: "post_date_gmt", direction: desc }
]
) {
documents {
id
}
}
}

Use offset and limit for simple pagination.

query OffsetPagination {
find(query: "articles", offset: 20, limit: 10) {
documents {
id
}
}
}

For more stable, high-performance pagination, use searchAfter. This requires a stable sort order. The _score field alone is not stable, so it must be combined with a tie-breaker field like a timestamp or unique ID.

First, perform an initial search and retrieve the sort values from the last document.

query CursorInitialQuery {
find(
query: "test"
orderBy: [{ field: "_score" }, { field: "post_date_gmt", direction: desc }]
) {
documents {
id
sort
data
}
}
}

The result might include a document like this:

{
"id": "post:17",
"sort": ["4.680951", "2024-01-23T11:06:25"]
}

Use these sort values in the searchAfter parameter of your next query to fetch the subsequent page.

query CursorNextPageQuery {
find(
query: "test"
orderBy: [{ field: "_score" }, { field: "post_date_gmt", direction: desc }]
searchAfter: ["4.680951", "2024-01-23T11:06:25"]
) {
documents {
id
sort
}
}
}

You can influence the relevance score by assigning weights to different fields.

The top-level fields parameter applies weights to all document types.

query GlobalFieldWeights {
find(
query: "coach"
fields: [{ name: "post_title", weight: 2 }, { name: "post_content" }]
) {
documents {
id
}
}
}

Use options.fields for more granular control, assigning different weights based on the document type.

query TypeSpecificFieldWeights {
find(
query: "search term"
options: {
fields: {
typeFieldName: "post_type"
types: [
{
type: "post"
fields: [
{ name: "post_title", weight: 3 }
{ name: "post_content", weight: 1 }
]
}
{ type: "page", fields: [{ name: "page_title", weight: 2 }] }
]
}
}
) {
documents {
id
}
}
}

By default, the Find API uses stemming to match words. Stemming reduces words to their root form, so a search for “running” would also match “run” and “ran”.

If you need to account for misspellings, you can use fuzzy matching instead by setting the tolerance parameter. This is useful for handling typos in user input.

query FuzzySearch {
find(query: "Austn", tolerance: { name: fuzzy, fuzzyDistance: 1 }) {
documents {
id
}
}
}

In this example, fuzzyDistance: 1 allows for a one-character difference, so the query for “Austn” would correctly match “Austin”.

Boost the relevance of more recent documents using timeDecay.

query TimeDecaySearch {
find(
query: "search term"
options: {
timeDecay: [{ field: "post_date", scale: "30d", decayRate: 0.5 }]
}
) {
documents {
id
}
}
}

Improve precision by re-scoring the top documents with a more expensive algorithm, like phrase matching.

query QueryRescorer {
find(
query: "juicy cucumber"
options: {
queryRescorer: {
windowSize: 10
fields: ["post_title"]
queryWeight: 0.9
rescoreQueryWeight: 1.1
}
}
) {
documents {
id
}
}
}

Leverage machine learning to search by meaning and intent, not just keywords.

query FindWithSemanticSearch {
find(
query: "american coach managing british team"
semanticSearch: { searchBias: 10, fields: ["post_title", "post_content"] }
) {
documents {
id
}
}
}

The searchBias parameter controls the balance between semantic and keyword search:

  • 0 = Full-text search only
  • 10 = Semantic search only
  • 1-9 = Hybrid search with varying weights

Filter documents based on geographic location using the geoConstraints parameter. You can provide one or more circular or rectangular areas to search within.

query GeoSearch {
find(
query: "coffee shop"
geoConstraints: {
circles: [
{ center: { lat: 37.7749, lon: -122.4194 }, maxDistance: "5km" }
]
}
) {
documents {
id
}
}
}

This example finds documents tagged “coffee shop” within a 5km radius of the specified coordinates.

Aggregate data to create faceted navigation. You can get term counts or range buckets.

Get a count of unique terms in a field.

query TermsAggregation {
find(query: "sport", aggregate: { terms: [{ field: "categories.name" }] }) {
aggregations {
terms {
field
terms {
term
count
}
}
}
}
}

To filter by a facet, use the filter parameter.

query FilteredTermsAggregation {
find(
query: "coach"
filter: "categories.name:Sports"
aggregate: { terms: [{ field: "categories.name" }] }
) {
total
aggregations {
terms {
field
terms {
term
count
}
}
}
}
}

Group documents into numeric or date ranges.

query RangesAggregation {
find(
query: "*"
aggregate: {
ranges: [
{
field: "price"
ranges: [{ from: 0, to: 100 }, { from: 101, to: 200 }]
}
]
}
) {
aggregations {
ranges {
field
ranges {
from
to
count
}
}
}
}
}

Control which fields are returned in the data object to reduce response size.

  • includeFields: Whitelist of fields to return.
  • excludeFields: Blacklist of fields to omit.
query FieldSelection {
find(
query: "blog posts"
options: {
includeFields: ["post_title", "post_excerpt", "post_date", "author"]
}
) {
documents {
id
data
}
}
}

The schema defines the GraphQL query type for searching documents in an index. It includes various parameters to customize the search query.

Field Description
find Searches for documents in an index based on specific parameters.
Parameter Type Description
query String! The search query string, supporting Simple Query String syntax for full-text search. This is required.
filter String A strict filter string (using Query String syntax) to narrow down the documents to be searched.
geoConstraints GeoConstraintsInput Multiple geographic constraints for proximity-based or area-based filtering with OR logic.
orderBy [OrderBy] An array of fields to sort the results by. Defaults to relevance score (`_score`).
searchAfter [String!] A set of sort values for cursor-based pagination.
offset Int The number of results to skip for offset-based pagination.
limit Int The maximum number of results to return.
fields [SearchField] A global list of fields to search in, with optional weights. Overridden by `options.fields`.
tolerance SearchOption The search tolerance, such as `fuzzy` or `stemming`. Defaults to stemming.
meta MetaInput Optional metadata for logging purposes.
aggregate AggregateInput Defines aggregations (facets) to be computed on the result set.
semanticSearch SemanticSearchInput Configuration for performing semantic or hybrid search.
options OptionsInput Additional options for controlling search behavior, like type-specific fields and time decay.
Field Type Description
name String! The field name to search for in the document.
weight Int The weight of the field, affecting the order of returned documents.

Optional meta data input for logging

Field Type Description
action String Performed action e.g. index
system String The requester system name
source String The requester hostname
Field Type Description
terms [TermAggregateInput] Aggregation based on terms.
ranges [RangeAggregateInput] Aggregation based on ranges.
Field Type Description
field String! Field name we want to aggregate on
size Int To retrieve additional terms, employ the "size" parameter. By default, the terms aggregation fetches the top ten terms with the highest document counts.
minCount Int If minCount is zero, zero count results are returned (else we omit them)
Field Type Description
field String! Field name we want to aggregate on
ranges [RangeInput] Range Input options
size Int To retrieve additional terms, employ the "size" parameter. By default, the terms aggregation fetches the top ten terms with the highest document counts.

A multi-bucket value source based aggregation that enables the user to define a set of ranges - each representing a bucket

Field Type Description
to Float From value (Inclusive)
from Float To value (Exclusive)
Field Type Description
field String! The field to order the documents by.
direction OrderByDirection The sort direction (asc or desc).
unmappedType String Deprecated: Going to be removed soon. When its present default weight score is applied for ordering
Field Type Description
name SearchOptionEnum! The search option name (fuzzy or stemming).
fuzzyDistance Int Optional fuzzy distance. Applicable only if the fuzzy search option is selected. It represents the number of one-character changes needed to turn one term into another.

Semantic Search query input

Field Type Description
searchBias Int! The search bias of the semantic search query vs full text search - 0 = Full text search only, 10 = Semantic search only - 1-9 mix of both weighted respectfully
fields [String!]! Fields for search
type SEMANTIC_SEARCH_TYPE Semantic Search type

Additional options for controlling search behavior and result formatting

Field Type Description
fields TypeFields Type-specific field searching. Allows you to specify different fields to search for different document types (e.g., search "title" and "content" for post_type:post, but "name" and "description" for post_type:page).
includeFields [String!] Fields to include in the search result. When specified, only these fields will be returned in the document data.
excludeFields [String!] Fields to exclude from the search result. These fields will be omitted from the document data.
timeDecay [TimeDecayInput!] Time-based decay functions to apply to search scoring. Allows boosting more recent documents.
queryRescorer QueryRescorerInput Query rescorer configuration for improving search precision by re-scoring top documents.

Allows you to specify different search fields for different document types. This is useful when different content types have different field structures.

Field Type Description
typeFieldName String The name of the field that contains the type information. Defaults to "post_type".
types [Type!]! List of type-specific field configurations.

Defines which fields to search for a specific document type.

Field Type Description
type String! The type name (e.g., "post", "page", "product").
fields [SearchField!]! The fields to search for this specific type, with optional weights.

Input for applying continuous, time-based decay to search scores

Field Type Description
field String The field to apply the decay function to. Defaults to 'post_date' if not specified.
scale String! The duration from 'now' at which the score multiplier will drop. Must match format like '30d', '12h', '90m', '3600s'. Defaults to '14d' if not specified. This is required.
decayRate Float The score multiplier at the 'scale' distance. Defaults to 0.25 if not provided. Value should be between 0 and 1.
offset String Sets a time offset from 'now' before the decay begins. Defaults to '7d'. Example: '7d'. Uses same format as scale.

Multiple geographic constraints for query results with OR logic combination:

  • Multiple circles: OR (matches if within ANY circle)
  • Multiple bounding boxes: OR (matches if within ANY bounding box)
  • Circles and bounding boxes: OR (matches if within ANY circle OR ANY bounding box)
Field Type Description
circles [CircleConstraint!] Circle constraints - results must be within specified distance from center points. Multiple circles are combined with OR logic.
boundingBoxes [BoundingBoxConstraint!] Bounding box constraints - results must be within rectangular areas. Multiple bounding boxes are combined with OR logic.

Circular area constraint defined by center point and maximum distance.

Field Type Description
center GeoPointInput! Center point of the circular area
maxDistance Distance! Maximum distance from center point

Bounding box rectangular area defined by southwest and northeast corners.

Field Type Description
southwest GeoPointInput! Southwest corner of the bounding box. This is the minimum latitude and longitude point.
northeast GeoPointInput! Northeast corner of the bounding box. This is the maximum latitude and longitude point.

Geographic coordinate input

Field Type Description
lat Float! Latitude in decimal degrees. Valid range: [-90.0, 90.0]
lon Float! Longitude in decimal degrees. Valid range: [-180.0, 180.0]
Field Type Description
total Int The total number of documents returned.
documents [SearchDocument] The list of documents matching the search.
Field Type Description
id ID! The Search ID of the document.
score Float The Search score of the document.
sort [String] Values used to sort documents. Can be used in combination with searchAfter for cursor pagination.
data Map! The document data.
Name Description
fuzzy The fuzzy search option.
stemming The stemming search option.
Name Description
asc Sort in ascending order.
desc Sort in descending order.
Value Description
BASIC Basic Search type

Distance value with unit. Format: <number><unit>

Supported units:

  • km (kilometers) - e.g., "5km"
  • mi (miles) - e.g., "10.5mi"
  • m (meters) - e.g., "1000m"
  • ft (feet) - e.g., "500ft"
  • yd (yards) - e.g., "100yd"

Examples: "5km", "10.5mi", "1000m"

Notes:

  • Negative values are not allowed
  • Must be positive numeric values
  • Unit is required