Zoho API v2 with PHP integration guide and examples Nice and simple integration with Zoho's API v2 and you don't even need their SDK, saving learning time, resources and tons of potential bugs

on January 15, 2020
(5 minute read)

Since 2020, Zoho has deprecated its old API v1 in favor of API v2 that features OAuth2 for authentication. This has made both newcomers and veteran users search for solutions to quickly and effortlessly implement this new protocol.

I’ve got invites for a free bank transfer!

The sad thing about it is that even though Zoho is one of the most popular and complete CRM suites out there, their documentation leaves a lot to be desired, just looking at the README.md of Zoho’s PHP SDK official repository in Github makes you cringe, someone copy-pasted it in there and ran away. Same bad luck when searching for the documentation online, scattered on several outdated pages, sections and forums, where desperate developers ask for help after hours of frustration. More often than not, Zoho employees take up to three weeks to reply and in some cases don’t even know what they’re talking about, in some cases other users had to correct them…

So before you waste a dozen hours trying to figure out the most effective and less time-consuming way to integrate your system with Zoho’s incomplete and rushed API v2 in PHP, here’s how I did it.

What you’ll get after reading this article

You will be able to use the API v2 via REST/JSON, without using their SDK, which I found complicated and couldn’t get it to work, encountering several errors from which I couldn’t find any troubleshooting info online. And this was from an out of the box Ubuntu server LAMP setup so 99% chance it wasn’t my server’s fault.

In all fairness, their REST API is pretty well done, easy to use, and the documentation for it is great. So no need to learn just another SDK.

I will be simplifying the code so it’s easy to read and adapt to your framework, please create your own components and methods once you understand the whole structure and process.

Authenticating in Zoho API v2 via PHP

There’s a few things you are going to end up with, probably storing them in your $params:

  • Client ID and Client Secret: Needed to generate the Refresh Token.
  • Redirect URI: A URL in your server that can receive a callback, I haven’t needed it really, so any URL in your domain works.
  • Self client Token: Needed once to generate the Refresh Token, expires after just 10 minutes so you’ve got to be quick.
  • Refresh Token: Never expires, used to request an Access Token.
  • Access Token: Used to make requests to the API, expires after 1 hour and you can get a new one with the Refresh Token.

Client ID and Client Secret

Read the entirety of this part before doing anything, one of the tokens given here will expire in 10 minutes and you will need to start again.

Depending on where your data is hosted, you will need to access one of these URLs:

  • https://accounts.zoho.com/developerconsole
  • https://accounts.zoho.eu/developerconsole

Click on “Add Client ID” and grab the Client ID and the Client Secret, save it somewhere.

Zoho API v2 Client ID

Self client Token

Then click on the three dots at the end and choose “Self client“.

In the Scope field type this:


You can see the full list of scopes here, if you change them, remember to separate scopes with a comma and no spaces and include the aaaserver.profile.READ at the beginning. Otherwise it may fail with silent errors and blank pages and you’ll spend some time pulling your hair.

For Expiry choose 10 minutes so we have enough time.

This will give us the Self client token which expires in 10 minutes so let’s be quick.

Refresh Token

Now we need to make a POST request to this URL (choose one depending on your location):

  • https://accounts.zoho.com/oauth/v2/token
  • https://accounts.zoho.eu/oauth/v2/token

I used Postman for this since it’s a one time request and it made no sense to implement this step into my code.

The request must include the following Header:

Key Value
Content-Type application/x-www-form-urlencoded

And a Body (x-www-form-urlencoded) with:

Key Value
code 1000.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
redirect_uri https://www.yourdomain.com/callback
client_secret xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
grant_type authorization_code

Change all the above fields with your details except for the grant_type.

code is the Self client code you got before, the one that expires. You have 10 minutes to make the request or else generate a new one.

This will give us the Refresh Token in a JSON response, it never expires so once you get here, congrats!

Access Token

Now we make a similar request to Zoho’s token endpoint:

  • https://accounts.zoho.com/oauth/v2/token
  • https://accounts.zoho.eu/oauth/v2/token

But replacing grant_type with refresh_token and removing redirect_uri in the Body:

Key Value
client_secret xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

This will give us the Access Token that we will use for our REST calls, and the Expires In.

It is a good idea and good practice to store both access_token and expires_in somewhere, either in a Database or in an internal file so you don’t need to request a new Access Token every time (it takes ~4 seconds to generate and Zoho may limit the number of Access Tokens you can generate repeatedly). Also, when checking the expiry date, subtract a minute or two to be safe.

API Requests in Zoho API v2 via PHP

The hard part is over, now comes the requests to Zoho’s REST API which is pretty standard.

Endpoints are as follow:

  • https://www.zohoapis.com/crm/v2/
  • https://www.zohoapis.eu/crm/v2/

And the Headers should include the Access Token:

Key Value
Zoho-oauthtoken ACCESS_TOKEN

The documentation for the REST API is well written, all in one page and includes examples so I won’t be rewriting everything, check it out here.

Contacts and Leads are explained in the Records API section, you simply type Contacts or Leads in the URL.

Find a Contact by its email address

Here’s a quick example on how to find a Contact by its email address:

$endpoint = 'https://www.zohoapis.eu/crm/v2/';
$email = '[email protected]';
$url = $endpoint.'Contacts/search?criteria='.rawurlencode('(Email:equals:'.$email.')');


Note that when making Updates/Upserts/Inserts, the info needs to be inside a data array and JSON-encoded:

$post_data = json_encode([
    'data' => [
        'Last_Name' => 'Griffin',
        'Email' => '[email protected]',

I’ve got invites for a free bank transfer!

Also note that fields are case-sensitive so very prone to mistakes.

Summing up

So that pretty much wraps everything up, you will get a nice and simple integration with Zoho’s API v2 and you don’t even need their SDK, saving some resources and tons of potential bugs. I am quite happy to be able to deal with the REST API since it’s something you are used to when working with other third parties, instead of having to learn a new SDK and all their methods and outputs.

Free 100% online banking account

💳 Get your free debit Mastercard


Treasure Chest

Get notified of new projects I make
Usually one email every 3 months

Follow me for cool new products and interesting findings on graphic design, web development, marketing, startups, life and humor.

/*Twitter*/ !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs"); /*Facebook (function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) {return;}js = d.createElement(s); js.id = id;js.src = "//connect.facebook.net/en_GB/all.js#xfbml=1&appId=28624667607";fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk'));*/ /*Google+*/ window.___gcfg = {lang: 'en-GB'};(function() {var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;po.src = 'https://apis.google.com/js/plusone.js';var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);})();