Search
The Search API is a unified search API that returns tickets, users, and organizations. You can define filters to narrow your search results according to resource type, dates, and object properties, such as ticket requester or tag.
To search articles in Help Center, see Search in the Help Center API documentation.
To use the API with Python or Perl, see Searching with the Zendesk API.
Note: It can take up to a few minutes for new tickets, users, and other resources to be indexed for search. If new resources don't appear in your search results, wait a few minutes and try again.
Results limit
The Search API returns up to 1,000 results per query, with a maximum of 100 results per page. See Pagination. If you request a page past the limit (page=11
at 100 results per page), a 422 Insufficient Resource Error is returned.
If you need to retrieve large datasets, Zendesk recommends breaking up the search into smaller chunks by limiting results to a specific date range. You can also use the Export Search Results endpoint.
Alternatively, if you only want incremental changes based on the most recent change, consider using one of the Incremental Export endpoints.
The count
property shows the actual number of results. For example, if a query has 5,000 results, the count
value will be 5,000, even if the API only returns the first 1,000 results.
Query basics
You specify search queries in a URL parameter named query
:
.../api/v2/search.json?query={search_string}
Syntax examples
The query syntax for {search_string}
is detailed in the Zendesk Support search reference, but this section gives a quick overview. The syntax gives you a lot of flexibility. Examples:
Query | Returns |
---|---|
query=3245227 |
The ticket with the id 3245227 |
query=Greenbriar |
Any record with the word Greenbriar |
query=type:user "Jane Doe" |
User records with the exact string "Jane Doe" |
query=type:ticket status:open |
Open tickets |
query=type:organization created<2015-05-01 |
Organizations created before May 1, 2015 |
query=status<solved requester:[email protected] type:ticket |
Unsolved tickets requested by [email protected] |
query=type:user tags:premium_support |
Users tagged with "premium_support" |
query=created>2012-07-17 type:ticket organization:"MD Photo" |
Tickets created in the MD Photo org after July 17, 2012 |
query=custom_field_455214213:Shoes |
Tickets where the value of the custom ticket field identified as 455214213 is "Shoes" |
How it works:
-
The
:
character is the equality operator. Other operators include<
and>
, the minus sign-
, and the wildcard character*
. Learn more about the operators. -
Double quotes,
""
, specify a search phrase. The API uses exact word matches. For example, the search results for "top" will include data that match the string "top" as a single word, or a single word in a longer phrase ("top tier"). It will not include the prefix of a longer word ("topology"), or data where the string "top" appears in the middle or end of a word ("stopwatch", "desktop"). -
The
type
property returns records of the specified resource type. Possible values includeticket
,user
,organization
, orgroup
. See Using the 'type' keyword in Zendesk help. -
The
status
property returns tickets set to the specified status. Possible values includenew
,open
,pending
,hold
,solved
, orclosed
. See Ticket property keywords in Zendesk help. You can also search by user, organization, and group properties. -
Properties such as
created
,updated
, andsolved
can return records for a specific time, on or before a certain time, and on or after a certain time. The API supports ISO 8601 formatted dates such as 2021-09-01 (YYYY-MM-DD) or date-times such as 2021-09-01T12:00:00-08:00. See Searching by date and time in Zendesk help.
URL-encoding the search string
Because a search string is sent in a HTTP request, the string must be url-encoded. Example:
?query=type%3Aticket+status%3Aopen
Most HTTP libraries provide tools for url-encoding strings. In cURL, you can use the --data-urlencode
option with the -G
flag:
curl "https://{subdomain}.zendesk.com/api/v2/search.json" \
-G --data-urlencode "query=type:ticket status:open" \
-v -u {email_address}:{password}
In Python 3, you can use the urlencode()
method in the urllib.parse module.
For examples, see Searching with the Zendesk API.
Side-loading
The Search API supports side-loading, which lets you retrieve related records as part of a single request. To make the request, add an include
parameter specifying the associations you want to load.
For example, the following request searches for records that contain "help" and specifies the type tickets
to sideload the ticket's user data.
/api/v2/search.json?query=help&include=tickets(users)
For more information, see Side-Loading.
JSON format
Search are represented as JSON objects with the following properties:
Name | Type | Read-only | Mandatory | Description |
---|---|---|---|---|
count | integer | true | false | The number of resources returned by the query corresponding to this page of results in the paginated response |
facets | string | true | false | The facets corresponding to the search query |
next_page | string | true | false | URL to the next page of results |
previous_page | string | true | false | URL to the previous page of results |
results | array | true | false | May consist of tickets, users, groups, or organizations, as specified by the result_type property in each result object |
Example
{
"count": 1,
"facets": null,
"next_page": null,
"previous_page": null,
"results": [
{
"created_at": "2018-04-06T03:17:05Z",
"default": false,
"deleted": false,
"description": "",
"id": 1835972,
"name": "Ragtail",
"result_type": "group",
"updated_at": "2018-04-06T03:17:05Z",
"url": "https://example.zendesk.com/api/v2/groups/1835972.json"
}
]
}
List Search Results
GET /api/v2/search?query={query}
Use the ampersand character (&) to append the sort_by
or sort_order
parameters to the URL.
For examples, see Searching with Zendesk API.
Pagination
- Offset pagination only
Offset pagination may result in duplicate results when paging. You can also use the Export Search Results endpoint, which uses cursor-based pagination and doesn't return duplicate results. See Pagination for more information.
Allowed For
- Admins, Agents and Light Agents
Errors JSON Format
Errors are represented as JSON objects which have the following keys:
Name | Type | Comment |
---|---|---|
error | string | The type of error. Examples: "unavailable", "invalid" |
description | string |
Example Error
{
"error": "unavailable",
"description": "Sorry, we could not complete your search query. Please try again in a moment."
}
Parameters
Name | Type | In | Required | Description |
---|---|---|---|---|
query | string | Query | true | The search query. See Query basics above. For details on the query syntax, see the Zendesk Support search reference |
sort_by | string | Query | false | One of updated_at , created_at , priority , status , or ticket_type . Defaults to sorting by relevance |
sort_order | string | Query | false | One of asc or desc . Defaults to desc |
Code Samples
curl
curl "https://{subdomain}.zendesk.com/api/v2/search.json" \
-G --data-urlencode "query=type:ticket status:open" \
-v -u {email_address}:{password}
Go
import (
"fmt"
"io"
"net/http"
)
func main() {
url := "https://example.zendesk.com/api/v2/search?query=https%3A%2F%2Fsubdomain.zendesk.com%2Fapi%2Fv2%2Fsearch.json%3Fquery%3Dtype%3Aticket+status%3Aclosed%26sort_by%3Dstatus%26sort_order%3Ddesc&sort_by=&sort_order="
method := "GET"
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Basic <auth-value>") // Base64 encoded "username:password"
client := &http.Client {}
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
Java
import com.squareup.okhttp.*;
OkHttpClient client = new OkHttpClient();
HttpUrl.Builder urlBuilder = HttpUrl.parse("https://example.zendesk.com/api/v2/search")
.newBuilder()
.addQueryParameter("query", "https://subdomain.zendesk.com/api/v2/search.json?query=type:ticket status:closed&sort_by=status&sort_order=desc")
.addQueryParameter("sort_by", "")
.addQueryParameter("sort_order", "");
Request request = new Request.Builder()
.url(urlBuilder.build())
.method("GET", null)
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", Credentials.basic("your-email", "your-password"))
.build();
Response response = client.newCall(request).execute();
Nodejs
var axios = require('axios');
var config = {
method: 'GET',
url: 'https://example.zendesk.com/api/v2/search',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic <auth-value>', // Base64 encoded "username:password"
},
params: {
'query': 'https%3A%2F%2Fsubdomain.zendesk.com%2Fapi%2Fv2%2Fsearch.json%3Fquery%3Dtype%3Aticket+status%3Aclosed%26sort_by%3Dstatus%26sort_order%3Ddesc',
'sort_by': '',
'sort_order': '',
},
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
Python
import requests
url = "https://example.zendesk.com/api/v2/search?query=https%3A%2F%2Fsubdomain.zendesk.com%2Fapi%2Fv2%2Fsearch.json%3Fquery%3Dtype%3Aticket+status%3Aclosed%26sort_by%3Dstatus%26sort_order%3Ddesc&sort_by=&sort_order="
headers = {
"Content-Type": "application/json",
}
response = requests.request(
"GET",
url,
auth=('<username>', '<password>'),
headers=headers
)
print(response.text)
Ruby
require "net/http"
uri = URI("https://example.zendesk.com/api/v2/search")
uri.query = URI.encode_www_form("query": "https://subdomain.zendesk.com/api/v2/search.json?query=type:ticket status:closed&sort_by=status&sort_order=desc", "sort_by": "", "sort_order": "")
request = Net::HTTP::Get.new(uri, "Content-Type": "application/json")
request.basic_auth "username", "password"
response = Net::HTTP.start uri.hostname, uri.port, use_ssl: true do |http|
http.request(request)
end
Example response(s)
200 OK
// Status 200 OK
{
"count": 1234,
"facets": null,
"next_page": "https://foo.zendesk.com/api/v2/search.json?query=\"type:Group hello\"&sort_by=created_at&sort_order=desc&page=2",
"previous_page": null,
"results": [
{
"created_at": "2009-05-13T00:07:08Z",
"id": 211,
"name": "Hello DJs",
"result_type": "group",
"updated_at": "2011-07-22T00:11:12Z",
"url": "https://foo.zendesk.com/api/v2/groups/211.json"
},
{
"created_at": "2009-08-26T00:07:08Z",
"id": 122,
"name": "Hello MCs",
"result_type": "group",
"updated_at": "2010-05-13T00:07:08Z",
"url": "https://foo.zendesk.com/api/v2/groups/122.json"
}
]
}
Show Results Count
GET /api/v2/search/count?query={query}
Returns the number of items matching the query rather than the items. The search string works the same as a regular search.
Parameters
Name | Type | In | Required | Description |
---|---|---|---|---|
query | string | Query | true | The search query |
Code Samples
curl
curl "https://{subdomain}.zendesk.com/api/v2/search/count.json" \
-G --data-urlencode "query=type:ticket status:open" \
-v -u {email_address}:{password}
Go
import (
"fmt"
"io"
"net/http"
)
func main() {
url := "https://example.zendesk.com/api/v2/search/count?query=https%3A%2F%2Fsubdomain.zendesk.com%2Fapi%2Fv2%2Fsearch.json%3Fquery%3Dtype%3Aticket+status%3Aclosed"
method := "GET"
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Basic <auth-value>") // Base64 encoded "username:password"
client := &http.Client {}
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
Java
import com.squareup.okhttp.*;
OkHttpClient client = new OkHttpClient();
HttpUrl.Builder urlBuilder = HttpUrl.parse("https://example.zendesk.com/api/v2/search/count")
.newBuilder()
.addQueryParameter("query", "https://subdomain.zendesk.com/api/v2/search.json?query=type:ticket status:closed");
Request request = new Request.Builder()
.url(urlBuilder.build())
.method("GET", null)
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", Credentials.basic("your-email", "your-password"))
.build();
Response response = client.newCall(request).execute();
Nodejs
var axios = require('axios');
var config = {
method: 'GET',
url: 'https://example.zendesk.com/api/v2/search/count',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic <auth-value>', // Base64 encoded "username:password"
},
params: {
'query': 'https%3A%2F%2Fsubdomain.zendesk.com%2Fapi%2Fv2%2Fsearch.json%3Fquery%3Dtype%3Aticket+status%3Aclosed',
},
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
Python
import requests
url = "https://example.zendesk.com/api/v2/search/count?query=https%3A%2F%2Fsubdomain.zendesk.com%2Fapi%2Fv2%2Fsearch.json%3Fquery%3Dtype%3Aticket+status%3Aclosed"
headers = {
"Content-Type": "application/json",
}
response = requests.request(
"GET",
url,
auth=('<username>', '<password>'),
headers=headers
)
print(response.text)
Ruby
require "net/http"
uri = URI("https://example.zendesk.com/api/v2/search/count")
uri.query = URI.encode_www_form("query": "https://subdomain.zendesk.com/api/v2/search.json?query=type:ticket status:closed")
request = Net::HTTP::Get.new(uri, "Content-Type": "application/json")
request.basic_auth "username", "password"
response = Net::HTTP.start uri.hostname, uri.port, use_ssl: true do |http|
http.request(request)
end
Example response(s)
200 OK
// Status 200 OK
{
"count": 6
}
Export Search Results
GET /api/v2/search/export?query={query}
Exports a set of results. See Query basics for the syntax of the query
parameter.
This endpoint is for search queries that will return more than 1000 results. The result set is ordered only by the created_at
attribute.
The search only returns results of a single object type. The following object types are supported: ticket, organization, user, or group.
You must specify the type in the filter[type]
parameter. Searches with type in the query string will result in an error.
Allowed For
- Agents
Pagination
- Cursor pagination
See Pagination.
Returns a maximum of 1000 records per page. The number of results shown in a page is determined by the page[size]
parameter.
Note: You may experience a speed reduction if you request 1000 results per page and you have many archived tickets in the results. Try reducing the number of results per page.
The cursor specified by the after_cursor
property in a response expires after one hour.
For more information on cursor-based pagination, see the following articles:
Limits
This API endpoint is rate-limited to 100 requests per minute per account. The limit also counts towards the global API rate limit.
Response Format
Name | Type | Comment |
---|---|---|
links[next] | string | URL to the next page of results |
meta[has_more] | string | Boolean indicating if there are more results |
meta[after_cursor] | string | Cursor object returned from the Search Service |
results | array | May consist of tickets, users, groups, or organizations, as specified by the filter_type parameter |
The response is similar to the response of GET /api/v2/search.json?
, with a few changes:
links
- Has the following nested properties:prev
andnext
. These replace thenext_page
andprev_page
links. Theprev
property is always null because backward pagination is not supported. Thenext
property may include an auto-generated link to the next page of results.meta
- Has the following nested properties:has_more
andafter_cursor
. Thehas_more
property indicates whether the next page has more results. Theafter_cursor
property is the cursor used to paginate to the next page. It expires after one hour.
There's no count
property.
Parameters
Name | Type | In | Required | Description |
---|---|---|---|---|
filter[type] | string | Query | false | The object type returned by the export query. Can be ticket , organization , user , or group . |
page[size] | integer | Query | false | The number of results shown in a page. |
query | string | Query | true | The search query. See Query basics above. For details on the query syntax, see the Zendesk Support search reference |
Code Samples
curl
curl "https://{subdomain}.zendesk.com/api/v2/search/export.json" -G \
--data-urlencode 'query=hello' \
--data-urlencode 'page[size]=100' \
--data-urlencode 'filter[type]=ticket' \
-v -u {email_address}:{password}
Go
import (
"fmt"
"io"
"net/http"
)
func main() {
url := "https://example.zendesk.com/api/v2/search/export?filter[type]=&page[size]=&query=https%3A%2F%2Fsubdomain.zendesk.com%2Fapi%2Fv2%2Fsearch.json%3Fquery%3Dtype%3Aticket+status%3Aclosed%26sort_by%3Dstatus%26sort_order%3Ddesc"
method := "GET"
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Authorization", "Basic <auth-value>") // Base64 encoded "username:password"
client := &http.Client {}
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
Java
import com.squareup.okhttp.*;
OkHttpClient client = new OkHttpClient();
HttpUrl.Builder urlBuilder = HttpUrl.parse("https://example.zendesk.com/api/v2/search/export")
.newBuilder()
.addQueryParameter("filter[type]", "")
.addQueryParameter("page[size]", "")
.addQueryParameter("query", "https://subdomain.zendesk.com/api/v2/search.json?query=type:ticket status:closed&sort_by=status&sort_order=desc");
Request request = new Request.Builder()
.url(urlBuilder.build())
.method("GET", null)
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", Credentials.basic("your-email", "your-password"))
.build();
Response response = client.newCall(request).execute();
Nodejs
var axios = require('axios');
var config = {
method: 'GET',
url: 'https://example.zendesk.com/api/v2/search/export',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic <auth-value>', // Base64 encoded "username:password"
},
params: {
'filter[type]': '',
'page[size]': '',
'query': 'https%3A%2F%2Fsubdomain.zendesk.com%2Fapi%2Fv2%2Fsearch.json%3Fquery%3Dtype%3Aticket+status%3Aclosed%26sort_by%3Dstatus%26sort_order%3Ddesc',
},
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
Python
import requests
url = "https://example.zendesk.com/api/v2/search/export?filter[type]=&page[size]=&query=https%3A%2F%2Fsubdomain.zendesk.com%2Fapi%2Fv2%2Fsearch.json%3Fquery%3Dtype%3Aticket+status%3Aclosed%26sort_by%3Dstatus%26sort_order%3Ddesc"
headers = {
"Content-Type": "application/json",
}
response = requests.request(
"GET",
url,
auth=('<username>', '<password>'),
headers=headers
)
print(response.text)
Ruby
require "net/http"
uri = URI("https://example.zendesk.com/api/v2/search/export")
uri.query = URI.encode_www_form("filter[type]": "", "page[size]": "", "query": "https://subdomain.zendesk.com/api/v2/search.json?query=type:ticket status:closed&sort_by=status&sort_order=desc")
request = Net::HTTP::Get.new(uri, "Content-Type": "application/json")
request.basic_auth "username", "password"
response = Net::HTTP.start uri.hostname, uri.port, use_ssl: true do |http|
http.request(request)
end
Example response(s)
200 OK
// Status 200 OK
{
"facets": null,
"links": {
"next": "https://example.zendesk.com/api/v2/search/export.json?filter%5Btype%5D=ticket&page%5Bafter%5D=eyJmaWVsZCI6ImNyZWF0ZWRfYXQiLCJkZXNjIjp0cnVlLCJ0aWVCcmVha0ZpZWxkIjoiaWQiLCJ0aWVCcmVha0Rlc2MiOmZhbHNlLCJzb3J0VmFsdWVzIjpudWxsLCJleHBvcnRlZFRodXNGYXIiOjAsInNlc3Npb25TdGFydCI6MTYwNzAzOTI1Mzk4NSwiY3JlYXRlZEF0IjoxNjA3MDM5MjUzOTg1LCJzYWx0ZWRSZXF1ZXN0SGFzaCI6LTQ5ODM0ODc3LCJzYWx0ZWRDdXJzb3JIYXNoIjotMjQwMzQ4MjgwfQ%3D%3D&page%5Bsize%5D=100&query=hello%26page%5Bsize%5D%3D100%26filter%5Btype%5D%3Dticket",
"prev": null
},
"meta": {
"after_cursor": "eyJmaWVsZCI6ImNyZWF0ZWRfYXQiLCJkZXNjIjp0cnVlLCJ0aWVCcmVha0ZpZWxkIjoiaWQiLCJ0aWVCcmVha0Rlc2MiOmZhbHNlLCJzb3J0VmFsdWVzIjpudWxsLCJleHBvcnRlZFRodXNGYXIiOjAsInNlc3Npb25TdGFydCI6MTYwNzAzOTI1Mzk4NSwiY3JlYXRlZEF0IjoxNjA3MDM5MjUzOTg1LCJzYWx0ZWRSZXF1ZXN0SGFzaCI6LTQ5ODM0ODc3LCJzYWx0ZWRDdXJzb3JIYXNoIjotMjQwMzQ4MjgwfQ==",
"before_cursor": null,
"has_more": true
},
"results": []
}