Custom API for Magento 2

Custom API for Magento 2

We have already went through on how to configure integration and utilize Magento apis. But let’s see how to make our own module with custom API calls.

Module Essentials

For a simpler start you should read the following article: “How to create a basic module in Magento 2” by Hrvoje Ivancic and make basic Magento 2 module. You would need only two things, module.xml and register.php for this example module. I would create module under Inchoo name space and call it Hello.

Module Configuration – etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Inchoo_Hello" setup_version="1.0.0" />

Registration – registration.php


API configuration

There are two more additional configurations we need to add API capability to module, webapi.xml and di.xml. In webapi.xml we are configuring access rights and API Interface that specified method will use.

Web API configuration – etc/webapi.xml

<?xml version="1.0"?>
<routes xmlns:xsi="" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
    <route url="/V1/hello/name/:name" method="GET">
        <service class="Inchoo\Hello\Api\HelloInterface" method="name"/>
            <resource ref="anonymous"/>

Resource tag defines what resources user needs to have to be able to access this api call. Possible options are self, anonymous or Magento resource like Magento_Catalog::products or Magento_Customer::group. We will for now use anonymous so we can access it as a guest.

Define Interface – etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Inchoo\Hello\Api\HelloInterface"
                type="Inchoo\Hello\Model\Hello" />

In di.xml we define what model will interface call to. We also need to add Interface and Model, please note that you need to take care about comments also.

Interface – Api/HelloInterface.php

namespace Inchoo\Hello\Api;
interface HelloInterface
     * Returns greeting message to user
     * @api
     * @param string $name Users name.
     * @return string Greeting message with users name.
    public function name($name);


Model – Model/Hello.php

namespace Inchoo\Hello\Model;
use Inchoo\Hello\Api\HelloInterface;
class Hello implements HelloInterface
     * Returns greeting message to user
     * @api
     * @param string $name Users name.
     * @return string Greeting message with users name.
    public function name($name) {
        return "Hello, " . $name;

Within model we add our functionality that will be executed by call to API method. In this case it will append Hello to name provided by call and return as string.

With all this your module should look like this:



Communicating with new API call

Testing as guest

To test REST you can go to http://{domain_name}/rest/V1/{method}/{attribute}/{value}.

Example: http://magento2.loc/rest/V1/hello/name/Jim

This is how response should look like for this example:

<response>Hello, Jim</response>

Here is small code that will test same API call but with SOAP:

$proxy = new SoapClient('http://magento2.vm/index.php/soap/default?wsdl&services=inchooHelloV1');
$result = $proxy->inchooHelloV1Name(array("name"=>"Jim"));

Response for SOAP

object(stdClass)#2 (1) {
  string(10) "Hello, Jim"

Adding ACL

If we don’t set anonymous in resource of webapi.xml, we need to set existing Magento resource or create our own. We can do that by adding acl.xml to etc.

ACL – etc/acl.xml

<?xml version="1.0"?>
<config xmlns:xsi="" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
            <resource id="Magento_Backend::admin">
                <resource id="Inchoo_Hello::hello" title="Hello" translate="title" sortOrder="110" />


In this case we need to add “Inchoo_Hello::hello” to webapi.xml resource instead anonymous.

In article Magento 2 API usage with examples by Tomas Novoselic is covered how we can connect to Magento with REST or SOAP API and we can use the same example to create new integration and test new implementation of API call.

Since Magento 2 is still fresh this may change in time, but we will try to keep this up to date with latest version. This is tested with Magento v2.1.0.


You made it all the way down here so you must have enjoyed this post! You may also like:

Inchoo & Magento in 2024 and Beyond Lucija Majstrovic
, | 0

Inchoo & Magento in 2024 and Beyond

3 best open-source eCommerce platforms in 2021 Zrinka Antolovic
Zrinka Antolovic, | 8

3 best open-source eCommerce platforms in 2021

Magento 2 API usage with examples Tomas Novoselic
, | 44

Magento 2 API usage with examples


  1. hi
    i’m facing a problem with the set reference = self in resource of webapi.xml getting error

    The consumer isn’t authorized to access %resources.


    #0 /var/www/html/m2.4.6/vendor/magento/module-webapi/Controller/Rest/RequestValidator.php(68): Magento\Webapi\Controller\Rest\RequestValidator->checkPermissions() #1 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Webapi\Controller\Rest\RequestValidator->validate() #2 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Webapi\Controller\Rest\RequestValidator\Interceptor->___callParent() #3 /var/www/html/m2.4.6/vendor/paypal/module-braintree-core/Plugin/RestValidationPlugin.php(102): Magento\Webapi\Controller\Rest\RequestValidator\Interceptor->Magento\Framework\Interception\{closure}() #4 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(135): PayPal\Braintree\Plugin\RestValidationPlugin->aroundValidate() #5 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Webapi\Controller\Rest\RequestValidator\Interceptor->Magento\Framework\Interception\{closure}() #6 /var/www/html/m2.4.6/generated/code/Magento/Webapi/Controller/Rest/RequestValidator/Interceptor.php(23): Magento\Webapi\Controller\Rest\RequestValidator\Interceptor->___callPlugins() #7 /var/www/html/m2.4.6/vendor/magento/module-webapi/Controller/Rest/InputParamsResolver.php(108): Magento\Webapi\Controller\Rest\RequestValidator\Interceptor->validate() #8 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Webapi\Controller\Rest\InputParamsResolver->resolve() #9 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Webapi\Controller\Rest\InputParamsResolver\Interceptor->___callParent() #10 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Webapi\Controller\Rest\InputParamsResolver\Interceptor->Magento\Framework\Interception\{closure}() #11 /var/www/html/m2.4.6/generated/code/Magento/Webapi/Controller/Rest/InputParamsResolver/Interceptor.php(23): Magento\Webapi\Controller\Rest\InputParamsResolver\Interceptor->___callPlugins() #12 /var/www/html/m2.4.6/vendor/magento/module-webapi/Controller/Rest/SynchronousRequestProcessor.php(85): Magento\Webapi\Controller\Rest\InputParamsResolver\Interceptor->resolve() #13 /var/www/html/m2.4.6/vendor/magento/module-webapi/Controller/Rest.php(202): Magento\Webapi\Controller\Rest\SynchronousRequestProcessor->process() #14 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Webapi\Controller\Rest->dispatch() #15 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Webapi\Controller\Rest\Interceptor->___callParent() #16 /var/www/html/m2.4.6/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Webapi\Controller\Rest\Interceptor->Magento\Framework\Interception\{closure}() #17 /var/www/html/m2.4.6/generated/code/Magento/Webapi/Controller/Rest/Interceptor.php(23): Magento\Webapi\Controller\Rest\Interceptor->___callPlugins() #18 /var/www/html/m2.4.6/vendor/magento/framework/App/Http.php(116): Magento\Webapi\Controller\Rest\Interceptor->dispatch() #19 /var/www/html/m2.4.6/vendor/magento/framework/App/Bootstrap.php(264): Magento\Framework\App\Http->launch() #20 /var/www/html/m2.4.6/pub/index.php(30): Magento\Framework\App\Bootstrap->run() #21 {main}

    please provide solution………..

  2. inplace of hello please return a json object like {
    “message” :”somthing”,
    “status”: “200”,


  3. Hi,
    If you have a problem with guess your soap app name you can enter on this url {your_domain}/index.php/soap/all?wsdl_list=1 and press combination ctrl + f and enter your method name and you should found your app name.

    Example xml


  4. Works great, thank you. This was just what I needed to get my project moving.

    I’m wondering where the path component /rest/ comes from? Is that built-in to Magento?

  5. It’s working in magento 2.3.0 but i want to set authentication for below api like as other default api (without admin/customer token no one access this). must pass Authorization header to access this.

  6. I’m getting this error:
    “Request does not match any route.”
    Any idea how to fix this?

  7. Hi,

    Do I have to enable the module from Magento backend before accessing the API url ?
    I have not enabled the same and getting Route not found error , please confirm .


  8. I have a problem, after running the configuration: update and: di: compile my site sends me an error and still delete the add-on and the plugin works but the page I get the error

  9. At this point of my knowledge around M2, I don’t know if this is a limitation with the magic getters and setters, or just something else I missed, but I spent a lot of time trying to make a getCount method available on the API. It looks like the methods shouldn’t have GET or SET words in the beginning, even if you map them in the webapi.xml. 😉

  10. After copy your code to my app/code/Inchoo/Hello and execute ‘setup:upgrade’ and ‘setup:di:compile’ i try ‘curl’.

    It show ‘Class Inchoo\\Hello\\Api\\HelloInterface does not exist’ for me ? Do you have idea on that?

    My Magento version is 2.1.7

    1. The DocBlock in the Interface is required otherwise this error is shown.

      * Returns greeting message to user
      * @api
      * @param string $name Users name.
      * @return string Greeting message with users name.
      public function name($name);

  11. What does exactly V1 do in line in etc/webapi.xml file? is it mandatory to write V1?

  12. Hi,
    Do you know how to return a json response instead of string ? I tried with this , but no luck :
    $result = $this->resultJsonFactory->create();
    $result->setDate([‘success’ => true]);

    Returning a simple string works fine though.

    1. I am having the same problem. The response includes double quote and some slashes while I tested with chrome’s postman extension. I just wish to get the exact same string I was returning from model class.

  13. Great tutorial. Worked perfectly for me. Very clear instructions for a very complex system. Thank you!

  14. I think this is wrong http://{domain_name}/rest/V1/{method}/{attribute}/{value}. The method should probably be replace with class and the rest should also follow suit.

  15. Are you sure di.xml stands for “Define Interface”? Maybe “Dependency Injection”?

    1. Hi Rustem,
      in article, subheading “Define Interface” is not here to explain di in filename of di.xml but instead what is our goal by adding this file. But you make a valid point, subheadings are bit ambiguous I will look into fixing them. Thanks.

  16. Hi – Thanks for the warm up API article! I’m not getting the expected response though – even after enabling the Inchoo_Hello module (and doing the setup:upgrade, etc), I’m seeing a ‘Document is Empty’ response from the REST version.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.

Tell us about your project

Drop us a line. We'd love to know more about your project.