---
metadata:
  - name: generator
    content: Diplodoc Platform v5.52.0
alternate:
  - https://yandex.com/dev/direct/doc/dg-v4/en/examples/python-suds.md
  - https://yandex.com/dev/direct/doc/dg-v4/ru/examples/python-suds.md
  - href: en/examples/python-suds.md
    type: text/markdown
    title: Markdown version
  - href: ../llms.txt
    type: text/markdown
    title: llms.txt
---
> **Documentation Index:** Fetch the complete configuration index at https://yandex.com/dev/direct/doc/dg-v4/en/llms.txt

# SOAP (Suds library)
Creating and modifying campaigns, ads and keywords in Python 2.7 using the Suds 0.4 GA library.
The [Suds](https://pypi.python.org/pypi/suds) library analyzes the WSDL description and provides an interface based on the SOAP protocol. The library creates input data structures that the application then fills in and passes to the server.


{% note warning %}

The Python socket subsystem should support SSL (the Python distribution includes the `ssl.py` module).

{% endnote %}


Sample application: [app-python-oauth.py](http://download.cdn.yandex.net/tech/com/direct/doc/download/app-python-oauth.py). A step-by-step tour of the application is provided below.

## 1. Connect Suds and configure logging {#import}

```python
# -*- coding: utf_8 -*-
from suds.client import Client
from suds.cache import DocumentCache
from suds.sax.element import Element
from suds import WebFault
```

The `Client` class (`suds.client` module) analyzes the WSDL description, creates data structures, sends SOAP packets and parses responses. This is the main class for interacting with the API. The `DocumentCache` class (`suds.cache` module) provides caching for the WSDL description.

The `Element` class (`suds.sax.element` module) is for adding arbitrary headers to SOAP packets. SOAP headers are used for passing metadata and data to the server for OAuth authorization.

The `WebFault` class is an exception that is generated when errors occur on the side of the API server. Catching this exception allows you to distinguish between server errors and errors on the application side.

Logging parameters are shown below.

```python
import logging
logging.basicConfig(level=logging.INFO)
if __debug__:
    logging.getLogger('suds.client').setLevel(logging.DEBUG)
else:
    logging.getLogger('suds.client').setLevel(logging.CRITICAL)
```

When running the Python interpreter with the -O flag (optimized mode), only messages about critical errors are output. When running it without this flag, debugging messages about sent and received SOAP packets are output, including the packet texts.

## 2. Plugin for correcting responses {#ns-correction}

The technique shown below fixes a known API error that causes the data types in a response to be associated with the namespace `http://namespaces.soaplite.com/perl` instead of the `API` namespace. Because of this error, Suds creates incorrect output data structures that cannot be reused in API requests. In the next version of the API, the error will be fixed and this technique will no longer be necessary.

```python
from suds.plugin import *
class NamespaceCorrectionPlugin(MessagePlugin):
    def received(self, context):
        context.reply = context.reply.replace('"http://namespaces.soaplite.com/perl"','"API"')
```

The `received` user function corrects the namespace in API responses before Suds analyzes the responses. The response is passed in the `context` parameter as a Unicode string. The response consists of a SOAP message in the same format as it arrived via HTTP.

## 3. Instance of the suds.Client class {#create-client}

An instance of the `suds.Client` class is used for interacting with the API. When creating it, specify the URL of the WSDL description.

```python
api = Client('https://api.direct.yandex.ru/v4/wsdl/', plugins = [NamespaceCorrectionPlugin()])
api.set_options(cache=DocumentCache())
```

The code shown also connects the plugin that was created in the previous step, and in the last line it includes caching for the WSDL description.

## 4. Metadata in SOAP packet headers {#meta}

SOAP packets contain the `<Header>` element, which is used for passing metadata to the server. The metadata relates to the request in general, not to the particular method invoked. The following example shows how to pass the `locale` parameter to specify the language for response messages.

```python
locale = Element('locale').setText('en')
api.set_options(soapheaders=(locale))
```

SOAP headers are automatically included in all SOAP packets.

## 5. OAuth authorization {#oauth}

For OAuth authorization, you must know the access token (`token`). It is specified in the SOAP packet headers as metadata.

```python
token = Element('token').setText('e4d3b3d2a74e4fa387a18dda5cd1c8d9')
locale = Element('locale').setText('en')
api.set_options(soapheaders=(token, locale))
```

## 6. Function for invoking API methods {#call-method}

The following function calls the specified API method with input parameters.

```python
def directRequest(methodName, params):
    '''
    Calling a Yandex Direct API method:
       api - instance of the suds.Client class
       methodName - method name
       params - input parameters
    If an error occurs the program ends,
    otherwise it returns the result of calling the method
    '''
    try:
        result = api.service['APIPort'][methodName](params)
        return result
    except WebFault, err:
        print unicode(err)
    except:
        err = sys.exc_info()[1]
        print 'Other error: ' + str(err)
    exit(-1)
```

If the method is executed successfully, the function returns the data received from the server. The data is presented as a hierarchy of objects, which is created by Suds based on an analysis of the SOAP response. Now we will examine techniques for working with output data.

If an error occurs, the function analyzes the exception object. If it belongs to the `WebFault` class, an error message from the server is output. In all other cases, the error is on the application side, and the error message is output with the “Other error:” prefix. For any error, the application exits with the return code -1.

## 7. Create a campaign {#create-camp}

The following code demonstrates how to use a dictionary to formulate input parameters. The `params` dictionary contains the minimum required parameters for creating a campaign.

```python
params = {
   'CampaignID': 0,
   'Login': 'agrom',
   'Name': u'Campaign created via the API',
   'FIO': 'Alex Gromov',
   'Strategy':{
      'StrategyName': 'WeeklyBudget',
      'WeeklySumLimit': 400,
      'MaxPrice': 8,
   },
   'EmailNotification':{
      'MoneyWarningValue':20,
      'SendAccNews':'Yes',
      'WarnPlaceInterval':60,
      'SendWarn':'Yes',
      'Email':'agrom@yandex.ru'
   },
}
```

Shown below is a [CreateOrUpdateCampaign](https://yandex.ru/dev/direct/doc/dg-v4/reference/CreateOrUpdateCampaign.html) method call using the `directRequest` function that was created earlier. The ID of the created campaign is put in the `campaignId` output parameter.

```python
campaignId = directRequest('CreateOrUpdateCampaign', params)
print u'Created campaign ID=' + str(campaignId)
```

## 8. Edit the campaign {#edit-camp}

To edit a campaign, it is a good idea to get its parameters, make the necessary changes, then save the parameters in the API. The [GetCampaignsParams](https://yandex.com/dev/direct/doc/dg-v4/en/reference/GetCampaignsParams.md) method accepts an array of campaign IDs and returns their parameters.

```python
params = {'CampaignIDS': [campaignId]}
campaignsParams = directRequest('GetCampaignsParams', params)
```

Ways to access output data are shown below.

```python
params = campaignsParams[0]
params['EmailNotification']['Email'] = 'new@email.ru'
params.MinusKeywords = [u'car cooler', u'refrigerator truck'] 
params.TimeTarget.DaysHours[0].Hours = [6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
```

The `campaignsParams` array contains [CampaignInfo](https://yandex.com/dev/direct/doc/dg-v4/en/reference/GetCampaignsParams.md#CampaignInfo) objects. Since we have requested parameters for a single campaign, we will be working with the first element of the array, setting its variable with `params`.

The second line of code shows that parameters can be accessed in dictionary notation. To do this, specify parameter names in quotation marks inside square brackets. The third and fourth lines demonstrate how to access parameters in object notation, where parameter names are separated by dots.

After making the necessary changes, we save the parameters using the[CreateOrUpdateCampaign](https://yandex.ru/dev/direct/doc/dg-v4/reference/CreateOrUpdateCampaign.html) method.

```python
result = directRequest('CreateOrUpdateCampaign', params)
print u'Modified parameters of the campaign ID=' + str(result)
```

## 9. Create an ad and a keyword {#create-banner}

To create an ad and a keyword, we will use the `Factory` class from the `suds` module. It will help us create an input data structure, then we will populate it with values. This method is an alternative to creating dictionaries.

```python
banner = api.factory.create('BannerInfo')
banner.CampaignID = campaignId
banner.BannerID = 0
banner.Title = Refrigerators'
banner.Text = u'Refrigerator sales and repair'
banner.Href = 'http://www.api.ru/banner{param1}?page={param2}'
banner.Geo = '1,10174'
```

In the first line, the [BannerInfo](https://yandex.com/dev/direct/doc/dg-v4/en/reference/CreateOrUpdateBanners.md#BannerInfo) input data structure is created, which contains all the supported parameters. In the following lines, mandatory parameters are set (the keyword can't be created without them).

The input structure created using `Factory` should not contain any uninitialized arrays. For this reason, parameters that contain arrays should either be explicitly initialized (for example, by assigning an empty array), or they should be deleted, as shown below.

```python
banner.Sitelinks = []
del(banner.ContactInfo) 
del(banner.MinusKeywords)
```

For the [BannerInfo](https://yandex.com/dev/direct/doc/dg-v4/en/reference/CreateOrUpdateBanners.md#BannerInfo) structure, there is a mandatory `Phrases` array, which must contain at least one keyword. We will create one and place it in the `Phrases` array.

```python
phrase = api.factory.create('BannerPhraseInfo')
phrase.PhraseID = 0
phrase.Phrase = u'refrigerator'
phrase.Price = 1.99
banner.Phrases = [phrase]
```

In the first line, the [BannerPhraseInfo](https://yandex.com/dev/direct/doc/dg-v4/en/reference/CreateOrUpdateBanners.md#BannerPhraseInfo) structure is created. In the following lines, mandatory parameters are set for the keyword.

The API lets you set variables for the keyword, which can be automatically substituted in the link to the site if the ad was found using this keyword. How to set these variables is shown below.

```python
userParams = api.factory.create('PhraseUserParams')
userParams.Param1 = ''
userParams.Param2 = 'freezer'
banner.Phrases[0].UserParams = userParams
```

Now, if the ad is found using the keyword [refrigerator], the link to the site `http://www.api.ru/banner{param1}?page={param2}` will appear as `http://www.api.ru/banner?page=freezer`.

Save the ad parameters using the [CreateOrUpdateBanners](https://yandex.com/dev/direct/doc/dg-v4/en/reference/CreateOrUpdateBanners.md) method.

```python
params = [banner]
bannerID = directRequest('CreateOrUpdateBanners', params)[0]
print u'Created ad ID=' + str(bannerID)
```

If successful, the output parameter `bannerId` contains the ID of the created ad.

## 10. Add a keyword to the ad {#edit-banner}

To add keywords, it is a good practice to get the current parameters for the ad, including parameters for keywords, then make the necessary changes and save the parameters in the API.

How to get ad parameters is shown below.

```python
params = {'BannerIDS': [bannerID]}
bannerParams = directRequest('GetBanners', params)[0]
```

The [GetBanners](https://yandex.com/dev/direct/doc/dg-v4/en/reference/GetBanners.md) method returns an array of [BannerInfo](https://yandex.com/dev/direct/doc/dg-v4/en/reference/CreateOrUpdateBanners.md#BannerInfo) objects, each of which contains information about an ad. In our example, we requested information that is located in the `bannerParams` variable for a single ad.

Now we create the BannerPhraseInfo object and set the keyword parameters.

```python
phrase = api.factory.create('BannerPhraseInfo')
phrase.PhraseID = 0
phrase.Phrase = u'freezer'
phrase.Price = 1.44
```

We create user variables for substituting in the link to the site.

```python
phrase.UserParams = api.factory.create('PhraseUserParams')
phrase.UserParams.Param1 = '_2'
phrase.UserParams.Param2 = 'deepfreeze'
```

Now, if the ad is found using the keyword [freezer], the link to the site `http://www.api.ru/banner{param1}?page={param2}` will appear as `http://www.api.ru/banner_2?page=deepfreeze`.

Let's add the keyword to the `Phrases` array and save the ad parameters using the [CreateOrUpdateBanners](https://yandex.com/dev/direct/doc/dg-v4/en/reference/CreateOrUpdateBanners.md) method.

```python
bannerParams.Phrases.append(phrase)
params = [bannerParams]
bannerIDS = directRequest('CreateOrUpdateBanners', params)
print u'Modified ad ID=' + str(bannerIDS[0])
```

## 11. Call finance methods {#create-invoice}

When calling the finance method, you must additionally specify the finance token and the transaction number (see [Accessing finance methods](https://yandex.com/dev/direct/doc/dg-v4/en/concepts/finance-token.md)).

As an example, we will call the [CreateInvoice](https://yandex.com/dev/direct/doc/dg-v4/en/reference/CreateInvoice.md) method, which creates an invoice. Below you can see how the finance token is formed. The input data are the master token (obtained from the Yandex Direct interface), the transaction number, the method name, and the username.

```python
import hashlib

masterToken  = 'AEgchkX2M3FBL8lU'
operationNum = 119
usedMethod   = 'CreateInvoice'
login        = 'agrom'

financeToken = hashlib.sha256(masterToken + str(operationNum) + usedMethod + login).hexdigest()
```

This creates a single-use finance token for calling the `CreateInvoice` method for the user `agrom`. An example of the token is shown below.

```
7215f95e84a766971d8ec4eb5a39ae96505b3a5529a91e5a03e5943565a6e6c7
```

The finance token and transaction number are passed in the header of the SOAP packet. Below you can see how the parameters are set in the header.

```python
finance_token = Element('finance_token').setText(financeToken)
operation_num = Element('operation_num').setText(operationNum)
api.set_options(soapheaders=(finance_token, operation_num))
```


{% note alert %}

When using OAuth authorization, you also must specify the access token (`token`) in the header of the SOAP package.

{% endnote %}


Let's create an input data structure and call the [CreateInvoice](https://yandex.com/dev/direct/doc/dg-v4/en/reference/CreateInvoice.md) method.

```python
invoice = api.factory.create('PayCampElement')
invoice.CampaignID = campaignId
invoice.Sum = 150
params = api.factory.create('CreateInvoiceInfo')
params.Payments = []
params.Payments.append(invoice)
url = directRequest('CreateInvoice', params)
```

If successful, the method returns the URL to use to get the printable format of the invoice. The only person who can access the invoice is the authorized Yandex user that the request was made on behalf of (in this example, the user "agrom").


