Zendesk REST API tutorial - PHP edition

In this tutorial, you'll create and use a PHP function that can create, read, update, or delete data on your Zendesk using the Zendesk REST API.

This article describes how to use the function as a simple PHP client, but there's also an official PHP client library. Check out this blog post for more details.

If you'd like to use Python or Perl instead, see the Python tutorial or the Perl tutorial.

Disclaimer: Zendesk provides this article for instructional purposes only. Zendesk does not support or guarantee the code. Zendesk also can't provide support for third-party technologies such as PHP. Please post any issue in the comments section or search for a solution online.

Installing PHP on your computer

On a Mac, the setup is pretty simple. PHP is pre-installed. You don't need to do anything.

In Windows, the setup is trickier because you need to download and install PHP. You can use the all-in-one XAMPP installer from Apache Friends. XAMPP is an Apache web server distribution that includes both PHP and MySQL. Installing it is easy.

You can also get a stand-alone distribution of PHP from php.net. Click here for the most recent binaries for Windows. This option will require more configuration to get it to work with a local web server.

You'll need the command prompt to perform some of the setup tasks. To open the command prompt, open the Start Menu, type cmd in the search box, and press Enter.

Option 1 - Installing XAMPP for Windows

  1. Download and install XAMPP for Windows. See the instructions.

  2. Enable the cURL extension in PHP by uncommenting the following line (i.e., deleting the opening semi-colon) in the php.ini file in the \xampp\php folder:


  3. Start the Apache server with the XAMPP control panel (Start > Programs > Apache Friends > XAMPP > XAMPP Control Panel).
  4. Test the installation by navigating to the c:\xampp\php folder using the command prompt and entering the following PHP command:

    php --help

    If you get an error message, then something is not right. If you get a list of all the PHP options, then PHP is running normally.

Option 2 - Installing a stand-alone distribution of PHP

  1. Download the zip file from php.net or click here for the most recent binaries for Microsoft Windows, and extract it to C:\PHP so that the full path to php.exe is C:\PHP\php.exe.
  2. Update your path variable by entering the following command at the command prompt:

    SET PATH=%PATH%;C:\php

  3. Close and reopen the command prompt window.
  4. Test the installation by entering the following PHP command at the prompt:

    php --help

    If you get an error message, then something is not right. If you get a list of all the PHP options, then PHP is running normally.

Creating a PHP function to access the Zendesk API

In this section, you'll create a PHP function that can perform CRUD operations with the Zendesk REST API. CRUD stands for "Create, Read, Update, and Delete," the four basic API operations. The function relies on cURL, or Client URL Library, a PHP library that lets you connect to and communicate with many different types of servers with many different types of protocols.

To create the function

  1. In a text editor, create a new document and save it as script.php.
  2. Paste the following code in your document. This curl wrapper makes it easier to talk to Zendesk by only requiring you to call one function and have it set all the necessary properties.

  3. Specify values for the following variables:
    • ZDAPIKEY - Specify your API key. The key is listed in Zendesk on the Channels/API page (Admin > Channels > API)
    • ZDUSER - Specify your email address registered with Zendesk
    • ZDURL - Replace "subdomain" in the URL with the subdomain of your Zendesk account. For example, if your URL is https://mondocam.zendesk.com, then replace subdomain with mondocam
  4. Call the curlWrap() function from your scripts. See the next section for details.

Accessing the Zendesk API with the function

Use your newly crafted curlWrap() function to access resources in the Zendesk REST API. The function's specification is as follows:

curlWrap(url, json_payload, action)

The function takes the following arguments:

  • url - Specifies the path of the endpoint after https://{subdomain}.zendesk.com/api/v2. Example: "/groups/{id}/users.json". See the REST API documentation for the URLs you can use.
  • json_payload - Contains the data you want to put or post to Zendesk. Use null if you're not sending anything. The data must be JSON encoded. See "Packaging data for PUT or POST requests" below. 
  • action - Specifies the type of request. Valid values are 'GET', 'POST', 'PUT', and 'DELETE'.

For example, the following statement retrieves a list of recent tickets:

$data = curlWrap("/tickets/recent.json", null, "GET");

For POST or PUT requests, include your JSON-encoded data in the $json argument:

$data = curlWrap("/tickets.json", $json, "POST");

In the examples above, you'll get a variable called $data that contains the response. Because the function decodes the JSON data, the variable contains a PHP data structure, not JSON. See "Accessing data in the response" and "Packaging data for PUT or POST requests" below for more information.

See "Troubleshooting" if you run into problems.

Accessing data in the response

The curlWrap() function converts the JSON in the response into a PHP object and then returns it. To access the data in the object, first consult the Zendesk REST API docs to figure out the structure of the data. For example, according to the List Groups doc, the JSON returned by a call to the "list groups" endpoint has the following structure:

You can deduce from this information that the data returned by the function contains an object consisting of one property named 'groups'. The property contains a list of groups, with each item in the list consisting of an object describing a group. Armed with this information, you can access the information in the response. For example, the following PHP snippet calls the "list groups" endpoint and then accesses and prints the name of the first group (i.e., groups[0]) in the object:

$data = curlWrap("/groups.json", null, "GET");
print("First group = " . $data->groups[0]->name );

See Google for details about the differences between objects and arrays in PHP. The short version is that array values are accessed as array[index] and object properties are accessed as object->property.

Packaging data for PUT or POST requests

Before making a put or post request, you must package the data in a PHP data structure matching the structure of the JSON expected by the API. Then you must encode the data to JSON. Consult the REST API doc for the expected JSON. For example, if you want to create a ticket, the API expects the following JSON:

{ "ticket": { "subject: "My printer is on fire!", "comment": { "body": "The smoke is very colorful." }}}

Accordingly in PHP, package your data as nested associative arrays matching the JSON structure:

$subject = "My printer is on fire!";
$body = "The smoke is very colorful.";
$payload = array('ticket' => array('subject' => $subject, 'comment' => array('body' => $body)));

Next, encode the data structure to JSON:

$json = json_encode($payload);

You should always encode your data to prevent characters like quotes from breaking the JSON. For example, the quotes in the following body would prematurely end the string and cause an error: "body": "Learn <a href="faq.html">more</a>." Encoding the data escapes the quotes. Example: "Learn <a href=\"faq.html\">more</a>."

Finally, make the post request:

$data = curlWrap("/tickets.json", $json, "POST")


If you think something went wrong and you want to check the results of an API call, use the var_dump() function. Example:

$data = curlWrap("/users.json", null, "GET");

To catch errors raised by the cURL library, locate the following statement in the curlWrap() function, $output=curl_exec($ch);, and then insert this statement after it:

echo 'Curl output: ' . curl_error($ch);

Make sure to comment out the line when you're done testing.

The first step in resolving a problem is to run the curl call on the command line. This tells you if there's something wrong with the URL or  credentials you're using. To install and use curl from the command line, see the curl website.  See the REST API documentation for sample CURL calls and credentials you can use. With each call, include the -v option (verbose) and include the output in the comments below if you want help from the forum. Before posting, please make sure you remove all passwords, API keys, and IP addresses from any comments, code, or attachments.

If the curl call returns JSON, then there's probably an issue with the PHP code. If you don't get anything, then a problem with the system configuration is more likely.

Several customers have gotten NULL responses when using the curlWrap() function. There are a lot of reasons why this may happen.

Windows XAMPP or WAMP problems

Windows is not the best operating system to develop on when it comes to PHP and cURL. The majority of people on Windows use XAMPP or WAMP (Windows-Apache-MySQL-PHP) and find that the cURL library is not enabled by default. If you use XAMPP for Windows, make sure you enable curl as described in this stackoverflow.com answer.

CA certificate errors

Several Windows users have issues with their root certificates. The problem is usually identified when testing with curl_error($ch) -- see above. Ensure your CA root certificates are up to date. This can be done by running Windows Update. If you find that after updating you're still having issues with null values being returned, do the following:

  1. Go to http://curl.haxx.se/docs/caextract.html and download the cacert.pem file.
  2. Add the following line to the curlWrap() function definition:
    curl_setopt($ch, CURLOPT_CAINFO, "C:/cacert.pem");
  3. Make sure that the C: path listed above points to the file you downloaded in step 1. Use forward slashes even if Windows uses backslashes.

If you're still having trouble, you're welcome to post a comment here. If you're a Zendesk customer, you can submit a ticket to support@zendesk.com.


Commenters below! Before posting, please make sure you remove all passwords, API keys, and IP addresses from any comments, code, or attachments.

Have more questions? Submit a request


  • 0

    Seems to be incomplete example.

    $data = curlWrap("/tickets.json", $json, "POST");

    what is $json? seems to be an array of fields but how to define it?

    curl_setopt($ch, CURLOPT_POSTFIELDS, $json);

  • 0

    Hi Antonio,


    The $json is if you want to post or put or some data to the API. In order to post or put you need to include the data you are sending (in the JSON format). You can post JSON in PHP by using JSON_ENCODE. Take a look at this article: http://php.net/manual/en/function.json-encode.php


    Hope this helps,


    Love Your Zendesk - The Fastest Way to Great Customer Support!

    Adam Panzer | Customer Advocate | support@zendesk.com

  • 0

    I tryed it but:

    $arr = array("subject"=>"Help, my printer is on fire!",

    "description"=>"help I need some help",



    $json = json_encode($arr);

    print $json;

    $data = curlWrap("/tickets.json", $json, "POST");



    the response was:

    {"subject":"Help, my printer is on fire!","description":"help I need some help","recipient":"support@company.com"}object(stdClass)#1 (3) {


    string(13) "RecordInvalid"


    string(24) "Record validation errors"


    object(stdClass)#2 (1) {


    array(1) {


    object(stdClass)#3 (2) {


    string(38) "Descripción: no puede estar en blanco"


    string(43) "Base Descripción: no puede estar en blanco"





  • 0

    Here is a sample:


    $create = json_encode(array('ticket' => array('subject' => $arr['z_subject'], 'description' => $arr['z_description'], 'requester' => array('name' => $arr['z_name'], 'email' => $arr['z_requester']))), JSON_FORCE_OBJECT);


    What's the difference?

    If you look at what you output from your JSON create:

    {"subject":"Help, my printer is on fire!","description":"help I need some help","recipient":" support@company.com"}

    Compare that to what my output is:

    {"ticket": { "subject": "subject", "description": "description", "requester": { "name": "name", "email": "email@example.com"}}}

    Hope this helps,


    Love Your Zendesk - The Fastest Way to Great Customer Support!

    Adam Panzer | Customer Advocate | support@zendesk.com

  • 0

    Thanks a lot, now its working fine. So complete code is attached for helping others.



  • 0

    how do I edit my post...

  • 0

    Hi Daniel,


    Two things: 


    1.) You are using a put on /tickets.json. You want to use a GET since you are GETting the list of tickets not modifying them. 

    2.) I will delete your post and recreate it without the attachment for security reasons

    3.) please regenerate your API key as it was visible publicly. 

    4.) Yes, you use your email address that is attached to the admin account that you are using to access the API. 


    Original Post:


    A couple questions: f

    under ZDUSER: do you just want our email we use to log in? so like 

    define("ZDUSER", " sam@me.com"); would work??

    Also, Im having trouble getting this to run... So Im trying to print out all of the current ticket id's that I have.. Could you guys check out my file and see what Im doing incorrectly?


    Code posted:

    $data = curlWrap("/tickets.json", null, "PUT");

    foreach($data in $tickets){

    print $tickests->tickets->id . "</br>";


  • 0

    also, re-reading this, you spelled tickets wrong in your for loop.

  • 0

    I'm new to this and was wondering where do I generate my api key kinda lost here

  • 0

    Hi Jmortiz,


    I saw that you submitted a ticket on this question. Do you still need me to follow up? If so, you can go to settings -> channels -> api to generate an api key.


    Love Your Zendesk - The Fastest Way to Great Customer Support!

    Adam Panzer | Customer Advocate | support@zendesk.com

  • 0

    thank you I found my api key

    now I have the issue where it not creating the ticket

    my output is

    {"ticket":{"subject":"testing","description":"312PPrinter is having the following problem: test","requester":{"name":"Jose Ortiz","email":"email@example.com"}}}{"ticket":{"subject":"testing","description":"312PC26 is having the following problem: test1","requester":{"name":"Jose Ortiz","email":"email@example.com"}}}

    but I never see a ticket being created

  • 0

    You have two of the same json strings stuck together, but submitting this: 

    {"ticket":{"subject":"testing","description":"312PC26 is having the following problem: test1","requester":{"name":"Jose Ortiz","email":"emailaddress"}}}

    should work. I removed your email address from both this post and your post you included above. 


  • 0

    so if I can't send multiple arrays

    like if I have multiple tickets to create at one time?

  • 0

    The app I am making a person can drag a machine or multiple machines into our problem section

    write what is wrong for each one and send it.

    what I want to do is for each one that is that area create a ticket for each one

    does that make sense?

  • 0

    Each ticket creation needs to be a separate request. 

  • 0

    Jose: I removed your original post because you included your api key. Here is what you wrote:

    I modified it now my var_dump is

    string(165) "{"ticket":{"subject":"testing","description":"312PPrinter is having the following problem: test","requester":{"name":"Jose Ortiz","email":"jmortiz@tcicollege.edu"}}}" 

    string(162) "{"ticket":{"subject":"testing","description":"312PC27 is having the following problem: test1","requester":{"name":"Jose Ortiz","email":"jmortiz@tcicollege.edu"}}}"


    I attached my php file

  • 0

    oops sorry

    so it seems no matter what I create it will not ever send the ticket with multiple request

    even if I make a foreach statement

  • 0

    Hi Jose,

    You can do a foreach statement but you need to put the everything that isn't the curlWrap function in that statement. See below:

    foreach(xyz as abc){
    $arr = array("z_subject"=>"testing",
    "z_description"=>$valname . " is having the following problem: " . $problem[$key],
    "z_name"=>"Jose Ortiz",
    $create = json_encode(array('ticket' => array('subject' => $arr['z_subject'], 'description' => $arr['z_description'], 'requester' => array('name' => $arr['z_name'], 'email' => $arr['z_requester']))), JSON_FORCE_OBJECT);
    $data = curlWrap("/tickets.json", $create, "POST");
    print $data->ticket->id. "

    print "\n";

    Understand that when you call curlWrap you actually execute the statement. For each ticket you wish to create you need to execute curlWrap with the individual ticket json.

  • 0

    so I create a new foreach inside the foreach that I already have?


    foreach($blah ad $key => $valname){

    foreach(xyz as abc){

    $arr = array("z_subject"=>"testing",
    "z_description"=>$valname . " is having the following problem: " . $problem[$key],
    "z_recipient"=>" bob@example.com",
    "z_name"=>"Jose Ortiz",
    "z_requester"=>" example@example.com"
    $create = json_encode(array('ticket' => array('subject' => $arr['z_subject'], 'description' => $arr['z_description'], 'requester' => array('name' => $arr['z_name'], 'email' => $arr['z_requester']))), JSON_FORCE_OBJECT);
    $data = curlWrap("/tickets.json", $create, "POST");
    print $data->ticket->id. "

    print "\n";



  • 0

    I am putting the correct API key, user (email address), and the url, but I keep getting back "

    Couldn't authenticate you

    ". Any idea what I might be missing?

  • 0

    I had to use a sandbox url, now I'm able to connect. However, the following call results in "NULL" being returned, even though I can see that there are at least 2 unresolved tickets: 

    $data = curlWrap("/tickets.json", null, "GET");

    var_dump($data); //this returns as NULL

  • 0

    Make sure you have token based authentication turned on by going to channels -> api 

    Are you able to pull in a result if you just look the ticket up by ID number? In other words, can you actually get a result if you go to [zd].zendesk.com/api/v2/tickets/123.json?

  • 0

    Hi Adam, thanks for replying. Trying to visit that URL that you sent me, it asks me for username/password. I couldn't get in using my sandbox username and password. 

    And on a different note, I'm able to use /views/active.json to get all my views and loop through them and just print out the specific view (unresolved tickets) that I need. However, trying to only call the specific view also results in null as well, i.e. /views/123.json

  • 0

    That wasn't *technically* a link. You were supposed to replace [zd] with your zendesk instance and use that in your php script. 

    If you are able to get something back from the API then you're doing it right in terms of using PHP and CURL. Now you just need to carefully read the API documentation. Make sure that you have the right method (POST/PUT/GET/DELETE) and make sure you are sending the correct JSON. 

    Also test your calls in the command line as well before testing PHP.

  • 0

    Hi Adam, sorry for the multiple questions. 

    I am able to get a single view when I use this url: [zd].zendesk.com/api/v2/views/123.json?

    However, using this returns null: /views/123.json

    *I'm using a sandbox account, so in the function, my ZDURL is defined as https://[zd]12345.zendesk.com

  • 0

    @farid: are you using the define statements?

  • 0

    Yes kinda. I actually converted that function file into a class, so I'm using const ZDURL = "https://......", but I can verify that my constant is correctly passed in the class by echoing out self::ZDURL.$url

  • 0

    I would say that converting it to a class presents it's own issues and while you're welcome to make changes, I would suggest using the code as-is first, verifying that everything works. In my opinion, it seems, that your define statement isn't working. To get more details try the following:


    Add a curl setopt for verbose:

    curl_setopt($ch, CURLOPT_VERBOSE, 1);

    And run the PHP file from the command by doing:

    php -f <filename>

    Let me know if this helps

    Love Your Zendesk - The Fastest Way to Great Customer Support!

    Adam Panzer | Customer Advocate | support@zendesk.com

  • 0

    Thanks Adam. Looks like I wasn't defining my ZDURL correctly: 

    I had it like this: https://[zd].zendesk.com

    Changed it to this: https://[zd].zendesk.com/api/v2

    Everything seems to be working now

  • 0

    Glad I could help!

Powered by Zendesk