How to create Magento’s Custom API?

It is not in-depth tutorial but description on how I tackled creation of custom API for magento. There are still vast fields of unknown but at least this will let you start building your own API and let you learn, like me, on how to do it. If I will find out anything new about it or I will gain some theoretical knowledge then I will update this post with those revelations.

Even the simplest example involves fairly few files and folders to be created, in this example we will create method which will return the version of the installed magento and list of best selling products, this is the structure of it:

magento/app/
        |- code/community/
	|           |- GrelaDesign/
	|	         |- Tutorial/
	|                      |- etc/
	|                           |- api.xml
	|                           |- config.xml
        |                      |- Helper/
        |                           |- Data.php
	|                      |- Model/
	|                           |- Core/
	|                                 |- Api.php
	|                           |- DashBoard/
	|                                 |- Api.php
        |- etc/modules/
	            |- GrelaDesign_Tutorial.xml

general structure would look like:

magento/app/code/
    |- community/ or local/ or core/
    |           |- COMPANYNAME/
    |             |- MODULENAME/
    |                      |- etc/
    |                           |- api.xml
    |                           |- config.xml
    |                      |- Helper/
    |                           |- Data.php
    |                      |- Model/
    |                           |- OBJECT1/
    |                                 |- Api.php
    |                           |- Object2/
    |                                 |- Api.php
    |- etc/modules/
                |- COMPANYNAME_MODULE.xml

Once you have created tutorial structure let’s start to fill it with content:

This file instructs magento where to find our API

<!-- GrelaDesign_Tutorial.xml -->
<config>
	<modules>
		<GrelaDesign_Tutorial>
			<active>true</active>
			<codePool>community</codePool><!-- this could be also 'local' -->
			<depends>
				<Mage_Api />
			</depends>
		</GrelaDesign_Tutorial>
	</modules>
</config>

config file, point to the model and helper, also to the translation files but I haven’t got this to work so far:), also in this revised version I have added a helper node as it seems to be quite important for Magento, without it I had an error thrown when I tried to access system->web services->roles and add/edit role.

<!-- config.xml -->
<config>
	<modules>
		<GrelaDesign_Tutorial><!-- COMPANYNAME_MODULE -->
			<version>0.0.0.1</version>
		</GrelaDesign_Tutorial>
	</modules>
	<global>
		<models>
			<tutorial><!-- MODULE, just first letter is lowercased, i.e. module CustomAPI would be placed here as customAPI -->
				<class>GrelaDesign_Tutorial_Model</class>
			</tutorial>
		</models>
		<helpers>
			<tutorial>
				<class>GrelaDesign_Tutorial_Helper</class>
			</tutorial>
		</helpers>
	</global>
</config>

In this tutorial we will create two three methods for our API, I have deliberately placed them in different classes to show how it affects look of api.xml file. First will be getVersion in Core object, second will be getBestsellers in DashBoard object and third getTotals also in DashBoard object.

The api.xml defines the resource name, by which programmer will call our exposed methods and will define Access Control Lists (ACL). The ACL is a way of grouping our methods into logical sets from which we will select (mix’n’match) when creating rules and webservices users. In our example we will have 2 sets of access groups: “allaccess” and “owneracess”. First will mark those methods that has no restrictions, and second marks the methods that only the “owner” user can access.

“allaccess”
– getVersion
– getBestsellers
“owneraccess”
– getTotals

<!-- api.xml -->
<config>
	<api>
		<resources>
			<tutorial_core translate="title" module="tutorial">
				<title>GrelaDesign tutorial Core API calls</title>
				<model>tutorial/core_api</model>
				<acl>greladesign/tutorial</acl>
				<methods>
					<getVersion translate="title" module="tutorial">
						<title>Returns version of this API</title>
						<acl>greladesign/tutorial/allaccess</acl>
					</getVersion>
				</methods>
			</tutorial_core>
			<tutorial_dashboard translate="title" module="tutorial">
				<title>GrelaDesign tutorial DashBoard app API calls</title>
				<model>tutorial/dashBoard_api</model>
				<acl>greladesign/tutorial</acl>
				<methods>
					<getSalesTotals translate="title" module="tutorial">
						<title>Get sales totals</title>
						<method>getTotals</method><!-- here we specify the method name if we have a conflict with built in method or we want to change the name i.e. because it is lengthy -->
						<acl>greladesign/tutorial/owneraccess</acl>
					</getSalesTotals>
					<getBestsellers translate="title" module="tutorial">
						<title>Get best selling product list</title>
						<acl>greladesign/tutorial/allaccess</acl>
					</getBestsellers>
				</methods>
			</tutorial_dashboard>
		</resources>
		<resources_alias><!-- here we can put aliases, shortened calls for our api resource, I haven't checked how alias behaves when it collides with different resource... -->
			<dashboard>tutorial_dashboard</dashboard>
			<core>tutorial_core</core>
		</resources_alias>
		<acl><!-- Access Control List  to our resources, this tree structure is displayed in "Resource Roles" panel when you open role to edit -->
			<resources>
				<greladesign translate="title" module="tutorial">
					<title>GrelaDesign</title>
					<sort_order>100</sort_order>
					<tutorial translate="title" module="tutorial">
						<title>Tutorial</title>
						<sort_order>100</sort_order>
						<allaccess translate="title" module="tutorial">
							<title>Core functionality required by all users.</title>
							<sort_order>10</sort_order>
						</allaccess>
						<owneraccess translate="title" module="tutorial">
							<title>Functions accessible only for owner.</title>
							<sort_order>50</sort_order>
						</owneraccess>
					</tutorial>
				</greladesign>
			</resources>
		</acl>
	</api>
</config>

Re-assuming: config->api->resources lists api resources with the exposed methods, config->api->acl->resources defines access groups for our resources

All we need to do now is just the API methods, lets start from the easiest:

<!-- magento/app/code/community/GrelaDesign/Tutorial/Model/Core/Api.php -->
<?php
class GrelaDesign_Tutorial_Model_Core_Api
{
	/**
	 * Returns version of the installed magento
	 * @return String
	 */
	public function getVersion()
	{
		return Mage::getVersion();
	}
}
?>

Simple isn’t it? Next will be more complex, but thanks to the snippi.net I was able to create it with no time, almost:)

<!-- magento/app/code/community/GrelaDesign/Tutorial/Model/DashBoard/Api.php -->
<?php
class GrelaDesign_Tutorial_Model_DashBoard_Api
{
	/**
	 * Returns list of best selling products, returned list is limited to the number items specified in argument.
	 * @param int $limit
	 */
	public function getBestsellers($limit=5)
	{
		//filter
		$visibility = array(
					  Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
					  Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_CATALOG
				  );
		
		$_arrayOfBestsellers = Mage::getResourceModel('reports/product_collection')
									->addAttributeToSelect('*')
									->addOrderedQty()
									->addAttributeToFilter('visibility', $visibility)
									->setOrder('ordered_qty', 'desc')
									->getSelect()->limit($limit)->query();
		$bestProducts = array();
		
		$details = Mage::getModel('catalog/product');
		
		foreach ($_arrayOfBestsellers as $product)
		{
			$details->load($product['entity_id']);
			$bestProduct[] = array(
								  "qty" => $product["ordered_qty"]
								, "price" => $details->getPrice()
								, "name" => $details->getName()
								);
		}
		
		return $bestProduct;
	}
	/**
	 * Get sales totals
	 * Returns totals for : Revenue, Tax, Shipping and Quantity for given time range, default is last 24 hours.
	 * @param String $period - default '24h', possible values are: 24h, ,1y, 2y
	 */
	public function getTotals($period='24h')
	{
		
        $collection = Mage::getResourceModel('reports/order_collection')
            ->addCreateAtPeriodFilter($period)
			//->getSelectCountSql()
            ->calculateTotals(1)
			;
		/*
		$collection->addFieldToFilter('store_id',
			array('eq' => Mage::app()->getStore(Mage_Core_Model_Store::ADMIN_CODE)->getId())
		);
		*/
        $collection->load();
		
        $totals = $collection->getFirstItem();
		
		
		return array(
						  'Revenue' => $totals->getRevenue()
						, 'Tax' => $totals->getTax()
						, 'Shipping' => $totals->getShipping()
						, 'Quantity' => $totals->getQuantity() * 1
						);
	}
}
?>

Last bit is the helper class:

<!-- magento/app/code/community/GrelaDesign/Tutorial/Helper/Data.php -->
<?php
class GrelaDesign_Tutorial_Helper_Data extends Mage_Core_Helper_Abstract
{
	
}
?>

Ok that is all for this, lets test:

log in to the magento admin area, and create 2 rules: “owner” and “client”, for first select all available resources (click on “greladesign”) for second we need to give only access to the essential methods (click on “Core functionality required by all users.”). When you have created those rules, create 2 users and assign those rules to them, i.e.

rule: “owner”
user: admin
pass: 1admin

rule: “client”
user: johndoe
pass: j0hn

once this is done, create test.php file in the root of your magento folder and past the following:
thanks to snippi.net I can share with you this test file:)

<?php
/*
Create a file called, 'test.php' in the root of your Magento development environment.
Copy the code below into your test.php file.
Browse to http://yourstore/test.php.
*/

// Tell this file to use the Mage libraries
$mageFilename = 'app/Mage.php';
require_once $mageFilename;
umask(0);

// Note we are using the 'app' directive not the 'run' directive here
// Also note the store is named 'default' by, well, default.  But as Zeke pointed out if your store is
// 'en' then you  would need to edit the following line to say Mage::app('en')
// Let's go with 'default' for simplicity ...
Mage::app('default');

// Your test code goes here

	echo "<pre>";

$client = new Zend_XmlRpc_Client('http://127.0.0.1/magento/api/xmlrpc/');

// If some stuff requires api authentication,

// we should get session token
//login	
	$session = $client->call('login', array('admin', '1admin'));

	echo var_dump($client->call('call', array($session, 'tutorial_dashboard.getBestsellers'))), "\n";
	echo var_dump($client->call('call', array($session, 'dashboard.getBestsellers'))), "\n";
	echo var_dump($client->call('call', array($session, 'tutorial_dashboard.getSalesTotals'))), "\n";
	echo var_dump($client->call('call', array($session, 'dashboard.getSalesTotals'))), "\n";
	
	echo var_dump($client->call('call', array($session, 'tutorial_core.getVersion'))), "\n";
	echo var_dump($client->call('call', array($session, 'core.getVersion'))), "\n";
//logout
	echo $client->call('endSession', array($session)), "\n";

//login	
	$session = $client->call('login', array('johndoe', 'j0hn'));

	echo var_dump($client->call('call', array($session, 'tutorial_dashboard.getBestsellers'))), "\n";
	echo var_dump($client->call('call', array($session, 'dashboard.getBestsellers'))), "\n";
	try {
		//this will throw an exception ("access denied")
		echo var_dump($client->call('call', array($session, 'tutorial_dashboard.getSalesTotals'))), "\n";
		echo var_dump($client->call('call', array($session, 'dashboard.getSalesTotals'))), "\n";
	} catch (Exception $e) {
		echo $e->getMessage(), "\n";
	}
	
	echo var_dump($client->call('call', array($session, 'tutorial_core.getVersion'))), "\n";
	echo var_dump($client->call('call', array($session, 'core.getVersion'))), "\n";
//logout
	echo $client->call('endSession', array($session)), "\n";

	echo "</pre>";



?>

I hope you will find it useful in any way,
happy coding

17 thoughts on “How to create Magento’s Custom API?

  1. Total magento API newbie here, quick question :

    I still don’t understand how to actually invoke these files, is there a URL command or anything ?. Also, if I am browsing, how do I insert paramaters directly into the methods.

    Many thanks

    • 🙂 Hi Daragh,

      you are using PHP files that contain magento API calls, read through tutorial I wrote, I am mentionging about files that needs to be created, if not I will review it after my holiday,

      regards.

  2. Afternoon,

    (Your example doesn’t work in magento 1.5, do you have one that does?)

    I have been pulling my hair out trying to make a custom webservice api for magento 1.5. I beleive all my configurations are correct but when I try to call a my api method, I get an error in the log, because magento is trying to open a my module as Mage/MyMoudule/Model instead of MyCompanyName/MyModule/Model..

    Why is magento looking at the service like it is a ‘Mage’ api call?

    Do you have a working example I can download an install to magento 1.5?

    Please help I am going crazyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy LMAO!

  3. Hi,

    I am having a big problem here.

    I use magento API to create orders,
    I successfully created order using the checkmo payment method,

    now my problem is how to use Paypal also as a payment method..

    I use this call to set a payment method.

    $resultPaymentMethod = $client->call(‘call’, array($session,’cart_payment.method’,array($cartid,$paymentMethod)));

    • Hi,

      sorry for late response, I was on holiday, the magento’s API is not full and if it doesn’t provide the access to i.e. PayPal payment methods then you have to write your own implementation or find third party one.

      best regards

  4. Hi there, any ideas how to edit the magento dashboard to show orders from the last 7 days by default instead of last 24 hours? I’ve searched Google and your post is the closest i can find.

  5. Thank you very much. I want to write a product attribute options create api and found this good tutorial. One question, is it OK for magento 1.5?

    • I’m not an expert:) but having an experience that version change is likely to broke things I suggest to try:) it (I’m not using 1.5).

  6. Thank you very much for the nice deep explanation of this, all other tutorials I have found online are just a copy/paste from the Magento wiki.

  7. Hi, This error appears:

    “Fatal error: Uncaught SoapFault exception: [4] Resource path is not callable.”

    Why?

    • Hi,
      the error states that can’t find the resource (method of the API) you have to check if there is requested method, if you have (user) permissions, etc. basically there is not enough information in your post:) and anyway I’m using XMLRPC not Soap.