See introduction to XMLAPI for details on Authentication:

https://support.sippysoft.com/a/solutions/articles/106909


Starting from Sippy 4.4 it's possible to use MULTICALL in XMLAPI.

This feature allows to "pack" N XMLAPI call into a single request to send to Server, instead of sending them one by one.


Measured benchmark for the addDID() xmlapi call showed 3 times faster execution in multicall compared with regular one-by-one addition of 1000 DIDs:

- 5m 20s in regular mode 

- 1m 39s in multicall.


PYTHON example (updated):

Note: This updated Python example replaces httplib2 with the requests library to handle HTTP requests. By using requests.post() along with the appropriate URL, request body, and headers, the process is simplified, ensuring compatibility with newer Python versions (e.g., 3.11). Also requests by default writes more information about its internal work for example, errors or warnings about SSL settings.

#!/usr/bin/env python

import requests
from requests.auth import HTTPDigestAuth
try:
    from urllib.parse import urlparse
    from xmlrpc.client import ServerProxy, getparser, ProtocolError, MultiCall
except ImportError:
    from urlparse import urlparse
    from xmlrpclib import ServerProxy, getparser, ProtocolError, MultiCall


class HTTPSDigestAuthTransport:
    def request(self, host, handler, request_body, verbose=0):
        parsed = urlparse("https://" + host)
        username = parsed.username
        password = parsed.password
        hostname = parsed.hostname
        port = parsed.port or 443

        url = f"https://{hostname}:{port}{handler}"

        if verbose:
            print(f"Making request to: {url}")

        response = requests.post(url, data=request_body, headers={'content-type': 'text/xml'}, auth=HTTPDigestAuth(username, password), verify=True)

        if response.status_code != 200:
            raise ProtocolError(url, response.status_code, response.reason, None)

        p, u = getparser(0)
        p.feed(response.content)
        return u.close()

if __name__ == "__main__":
    transport = HTTPSDigestAuthTransport()
    client = ServerProxy("https://username:[email protected]/xmlapi/xmlapi", transport)
    multicall = MultiCall(client)
    multicall.getAccountCallStats({"i_account": 1})
    multicall.getAccountCallStats({"i_account": 1})
    multicall.getAccountInfo({"i_account": -1}) # expect error here
    multicall.getAccountCallStats({"i_account": 1})

    for res in multicall().results:
        if isinstance(res, dict):
            print("Fault", res)
        else:
            print(res[0])

PYTHON example (using httplib2):

Below is an example using httplib2. Although the scripts above are now based on requests, this code is still functional in Python 3 and might be useful for backward compatibility or for users who prefer httplib2.

#!/usr/bin/env python

import warnings
warnings.simplefilter("ignore", DeprecationWarning)
import httplib2
try:
    from urllib.parse import splituser, splitpasswd
    from xmlrpc.client import ServerProxy, getparser, ProtocolError, MultiCall
except ImportError:
    from urllib import splituser, splitpasswd
    from xmlrpclib import ServerProxy, getparser, ProtocolError, MultiCall

class HTTPSDigestAuthTransport:
    def request(self, host, handler, request_body, verbose=0):
        auth, host = splituser(host)
        username, password = splitpasswd(auth)

        h = httplib2.Http(disable_ssl_certificate_validation=True)
        if verbose:
            h.debuglevel = 1
        h.add_credentials(username, password)

        resp, content = h.request("https://" + host + handler, "POST", body=request_body,
                                  headers={'content-type':'text/xml'})

        if resp.status != 200:
            raise ProtocolError("https://" + host + handler, resp.status, resp.reason, None)

        p, u = getparser(0)
        p.feed(content)
        return u.close()

transport = HTTPSDigestAuthTransport()
client = ServerProxy("https://username:[email protected]/xmlapi/xmlapi", transport)

multicall = MultiCall(client)
multicall.getAccountCallStats({ "i_account" : 1})
multicall.getAccountCallStats({ "i_account" : 1})
multicall.getAccountInfo({ "i_account" : -1}) # expect error here
multicall.getAccountCallStats({ "i_account" : 1})

for res in multicall().results:
    if isinstance(res, dict):
        print ("Fault", res)
    else:
        print (res[0])

More python examples:

https://support.sippysoft.com/a/solutions/articles/107511


PHP example:

<?
set_include_path(get_include_path() . PATH_SEPARATOR . '/home/ssp/sippy_web/lib');

include 'xmlrpc/xmlrpc.inc';

$customer_web_login    = "username";
$customer_api_password = "password";

$params = array(new xmlrpcval(array(
        "i_environment" => new xmlrpcval('1', "int"),
    ), 'struct'));
$msg = new xmlrpcmsg('getAccountCallStats', $params);

$params2 = array(new xmlrpcval(array(
        "i_account" => new xmlrpcval('-1', "int"), // expect error
    ), 'struct'));
$msg2 = new xmlrpcmsg('getAccountInfo', $params2);

$cli = new xmlrpc_client('https://1.2.3.4/xmlapi/xmlapi');
$cli->request_charset_encoding = 'UTF-8';
$cli->setSSLVerifyPeer(false);
$cli->return_type = 'phpvals';
$cli->setDebug(2);

//$cli->setCredentials($customer_web_login, $customer_api_password, CURLAUTH_DIGEST);

$r = $cli->multicall(Array($msg, $msg, $msg2, $msg), 10, '', FALSE);

print_r($r);
?>