The initial goal of the CL-SOAP project was to access the Google AdWords API ( http://www.google.com/apis/adwords/ )
This milestone has now been reached. Please see the file test/test-google-adwords.lisp for even more code.
First XML namespaces have to be set up correctly.
(defconstant +google-adwords-ns-uri+ "https://adwords.google.com/api/adwords/v2") (defpackage :google (:nicknames "google") (:documentation "Package for symbols in the Google Adwords API XML Namespace")) (defparameter *google-adwords-ns* (s-xml:register-namespace +google-adwords-ns-uri+ "google" :google)) ;;; apparently there are different XML Schema Defintion namespace URIs, Google is using this one: (s-xml:register-namespace "http://www.w3.org/2001/XMLSchema" "xsd" :xsd)We are now ready to look at some services.
Parse the WSDL of the service that you want to access and have a look at it.
CL-SOAP 11 > (describe-wsdl-soap (wsdl-cache-get "https://adwords.google.com:443/api/adwords/v2/InfoService?wsdl")) WSDL Document Definitions Service: InfoService Port: InfoService SOAP Address Location "https://adwords.google.com:443/api/adwords/v2/InfoService" Binding: api:InfoServiceSoapBinding SOAP style [document] Operation: getUnitCountForMethod Input: getUnitCountForMethodRequest (getUnitCountForMethod ("service" :STRING) 1 ("method" :STRING) 1 ("startDate" :DATE) 1 ("endDate" :DATE) 1 ) 1 Output: getUnitCountForMethodResponse (getUnitCountForMethodResponse ("getUnitCountForMethodReturn" :LONG) 1 ) 1 Operation: getUnitCount Input: getUnitCountRequest (getUnitCount ("startDate" :DATE) 1 ("endDate" :DATE) 1 ) 1 Output: getUnitCountResponse (getUnitCountResponse ("getUnitCountReturn" :LONG) 1 ) 1 Operation: getMethodCost Input: getMethodCostRequest (getMethodCost ("service" :STRING) 1 ("method" :STRING) 1 ("date" :DATE) 1 ) 1 Output: getMethodCostResponse (getMethodCostResponse ("getMethodCostReturn" :INT) 1 ) 1 Operation: getUsageQuotaThisMonth Input: getUsageQuotaThisMonthRequest ("getUsageQuotaThisMonth") 1 Output: getUsageQuotaThisMonthResponse (getUsageQuotaThisMonthResponse ("getUsageQuotaThisMonthReturn" :LONG) 1 ) 1 Operation: getOperationsQuotaThisMonth Input: getOperationsQuotaThisMonthRequest ("getOperationsQuotaThisMonth") 1 Output: getOperationsQuotaThisMonthResponse (getOperationsQuotaThisMonthResponse ("getOperationsQuotaThisMonthReturn" :LONG) 1 ) 1 Operation: getOperationCount Input: getOperationCountRequest (getOperationCount ("startDate" :DATE) 1 ("endDate" :DATE) 1 ) 1 Output: getOperationCountResponse (getOperationCountResponse ("getOperationCountReturn" :LONG) 1 ) 1The input and output characteristics of each operation are described in some detail. Example lisp template code of actual input and output are also given (along with multiplicity indication, whether elements are required, optional, can occur zero ore more times, or one or more times).
Given that you understand a particular operation, you can implement it quite easily.
(defun get-method-cost (service method &optional (date (ut))) (wsdl-soap-call (wsdl-cache-get "https://adwords.google.com:443/api/adwords/v2/InfoService?wsdl") "getMethodCost" :input `("getMethodCost" ("service" ,service "method" ,method "date" ,date)) :headers (make-google-headers)))For the Google AdWords API, some headers are required. The method implemented above requires some parameters, specified to the framework as a structured alist. With debugging enabled (using (setf *debug-stream* *output-stream*)), executing the call looks as follows:
CL-SOAP 85 > (get-method-cost "InfoService" "getMethodCost") ;; SOAP CALL sending: <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:google="https://adwords.google.com/api/adwords/v2" xmlns="https://adwords.google.com/api/adwords/v2"> <soapenv:Header> <token>XXX</token> <useragent>cl-soap-testing</useragent> <password>XXX</password> <clientEmail>sven@beta9.be</clientEmail> <email>svc@mac.com</email> </soapenv:Header> <soapenv:Body> <getMethodCost> <service>InfoService</service> <method>getMethodCost</method> <date>2005-09-27</date> </getMethodCost> </soapenv:Body> </soapenv:Envelope> ;; SOAP CALL receiving: <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"> <soapenv:Header> <responseTime soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v2"> 393 </responseTime> <operations soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v2"> 1 </operations> <units soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v2"> 1 </units> </soapenv:Header> <soapenv:Body> <getMethodCostResponse xmlns="https://adwords.google.com/api/adwords/v2"> <getMethodCostReturn>1</getMethodCostReturn> </getMethodCostResponse> </soapenv:Body> </soapenv:Envelope> ("getMethodCostResponse" ("getMethodCostReturn" 1)) (("responseTime" 393) ("operations" 1) ("units" 1))Some sensitive data fields have been marked with X's. XML was beautified a little bit ;-) Note that two values are returned: the actual value and the headers.
This more complicated example is also described in the introduction to the Google AdWords API: estimating the traffic for keywords. Again, we first look at the interpreted WSDL:
CL-SOAP 43 > (describe-wsdl-soap (wsdl-cache-get "https://adwords.google.com:443/api/adwords/v2/TrafficEstimatorService?wsdl")) WSDL Document Definitions Service: TrafficEstimatorService Port: TrafficEstimatorService SOAP Address Location "https://adwords.google.com:443/api/adwords/v2/TrafficEstimatorService" Binding: api:TrafficEstimatorServiceSoapBinding SOAP style [document] Operation: estimateKeywordList Input: estimateKeywordListRequest (estimateKeywordList (keywordRequests ( ("id" :LONG) ? ("type" :STRING) ? ("text" :STRING) ? ("maxCpc" :LONG) ? ("negative" :BOOLEAN) ? ) + )) 1 Output: estimateKeywordListResponse (estimateKeywordListResponse (estimateKeywordListReturn ( ("id" :LONG) ? ("impressions" :INT) 1 ("ctr" :FLOAT) 1 ("cpc" :LONG) 1 ("avgPosition" :FLOAT) 1 ("notShownPerDay" :INT) 1 ) + )) 1 Operation: estimateAdGroupList Input: estimateAdGroupListRequest (estimateAdGroupList (adGroupRequests ( ("id" :INT) ? ("maxCpc" :LONG) ? (keywordRequests ( ("id" :LONG) ? ("type" :STRING) ? ("text" :STRING) ? ("maxCpc" :LONG) ? ("negative" :BOOLEAN) ? ) + )) + )) 1 Output: estimateAdGroupListResponse (estimateAdGroupListResponse (estimateAdGroupListReturn ( ("id" :INT) ? (keywordEstimates ( ("id" :LONG) ? ("impressions" :INT) 1 ("ctr" :FLOAT) 1 ("cpc" :LONG) 1 ("avgPosition" :FLOAT) 1 ("notShownPerDay" :INT) 1 ) + )) + )) 1 Operation: estimateCampaignList Input: estimateCampaignListRequest (estimateCampaignList (campaignRequests ( ("id" :INT) ? ("optInSearchNetwork" :BOOLEAN) ? ("optInContentNetwork" :BOOLEAN) ? (geoTargeting ("countries" (:STRING) * ) ("regions" (:STRING) * ) ("metros" (:STRING) * ) ("cities" (:STRING) * )) ? (languageTargeting ("languages" (:STRING) * )) ? (adGroupRequests ( ("id" :INT) ? ("maxCpc" :LONG) ? (keywordRequests ( ("id" :LONG) ? ("type" :STRING) ? ("text" :STRING) ? ("maxCpc" :LONG) ? ("negative" :BOOLEAN) ? ) + )) + )) + )) 1 Output: estimateCampaignListResponse (estimateCampaignListResponse (estimateCampaignListReturn ( ("id" :INT) ? (adGroupEstimates ( ("id" :INT) ? (keywordEstimates ( ("id" :LONG) ? ("impressions" :INT) 1 ("ctr" :FLOAT) 1 ("cpc" :LONG) 1 ("avgPosition" :FLOAT) 1 ("notShownPerDay" :INT) 1 ) + )) + )) + )) 1Next we define our little lisp function:
(defun estimate-keyword-list (keywords) "((text type max-cpc)*) where type is Broad|Phrase|Exact" (wsdl-soap-call (wsdl-cache-get "https://adwords.google.com:443/api/adwords/v2/TrafficEstimatorService?wsdl") "estimateKeywordList" :input `("estimateKeywordList" ("keywordRequests" ,(mapcar #'(lambda (keyword) (destructuring-bind (text type max-cpc) keyword `("text" ,text "type" ,type "maxCpc" ,max-cpc))) keywords))) :headers (make-google-headers)))Now we can test it, passing multiple requests and receiving multiple responses:
CL-SOAP 192 > (estimate-keyword-list '(("flowers" "Broad" 50000) ("tree" "Broad" 50000))) ;; SOAP CALL sending: <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:google="https://adwords.google.com/api/adwords/v2" xmlns="https://adwords.google.com/api/adwords/v2"> <soapenv:Header> <token>XXX</token> <useragent>cl-soap-testing</useragent> <password>XXX</password> <clientEmail>sven@beta9.be</clientEmail> <email>svc@mac.com</email> </soapenv:Header> <soapenv:Body> <estimateKeywordList> <keywordRequests> <type>Broad</type> <text>flowers</text> <maxCpc>50000</maxCpc> </keywordRequests> <keywordRequests> <type>Broad</type> <text>tree</text> <maxCpc>50000</maxCpc> </keywordRequests> </estimateKeywordList> </soapenv:Body> </soapenv:Envelope> ;; SOAP CALL receiving: <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"> <soapenv:Header> <responseTime soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v2"> 412 </responseTime> <operations soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v2"> 2 </operations> <units soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" soapenv:mustUnderstand="0" xmlns="https://adwords.google.com/api/adwords/v2"> 50 </units> </soapenv:Header> <soapenv:Body> <estimateKeywordListResponse xmlns="https://adwords.google.com/api/adwords/v2"> <estimateKeywordListReturn> <avgPosition>4.537519</avgPosition> <cpc>50000</cpc> <ctr>0.01547993</ctr> <id>-1</id> <impressions>7263</impressions> <notShownPerDay>288532</notShownPerDay> </estimateKeywordListReturn> <estimateKeywordListReturn> <avgPosition>2.8274193</avgPosition> <cpc>50000</cpc> <ctr>0.012525387</ctr> <id>-1</id> <impressions>18177</impressions> <notShownPerDay>397941</notShownPerDay> </estimateKeywordListReturn> </estimateKeywordListResponse> </soapenv:Body> </soapenv:Envelope> ("estimateKeywordListResponse" ("estimateKeywordListReturn" (("id" -1 "impressions" 7263 "ctr" 0.01547993 "cpc" 50000 "avgPosition" 4.537519 "notShownPerDay" 288532) ("id" -1 "impressions" 18177 "ctr" 0.012525387 "cpc" 50000 "avgPosition" 2.8274193 "notShownPerDay" 397941)))) (("responseTime" 412) ("operations" 2) ("units" 50))That is all for now! More example and testing code can be found in the project's source tree.
$Id: google-adwords-api.html,v 1.6 2005/10/03 12:28:26 scaekenberghe Exp $