Python SDK: Cannot set bid_type auto on LineItem; missing bid_amount param

python

#1

Hi,

Experiencing an issue with the Python Ads SDK when trying to set `bid_type = ‘AUTO’:

Here’s the code:

remote_object = LineItem.load(self.ad_account, object_id)
remote_object.bid_amount_local_micro = None         # Must set to NULL when bid_type is 'AUTO'
remote_object.automatically_select_bid = None       # Cannot send both bid_type and automatically_select_bid
remote_object.bid_type = 'AUTO'                     # Enable automatic bidding
remote_object.save()

Here’s the exception I get back in return though:

<BadRequest object at 0x867c240L code=400 details=[{u\'message\': u\'"bid_amount_local_micro" is a required parameter\', u\'code\':
u\'MISSING_PARAMETER\', u\'parameter\': u\'bid_amount_local_micro\'}]>'}

If I set it to a string, ‘NULL’, I get as you would expect and that I’ve seen in my searches around these community boards:

<BadRequest object at 0x863d240L code=400 details=[{u\'message\': u\'Expected Long, got "NULL" for bid_amount_local_micro\', u\'code\': u\'INVALID_PARAMETER\', u\'parameter\': u\'bid_amount_local_micro\'}]>

I found this bit of code in the Python Twitter SDK in Resource.to_params:

It excludes params that have their value of None - but then how do I send a “NULL”? Well, I tried working around this…

params = remote_object.to_params()
if remote_object.bid_type == 'AUTO':
    params['bid_amount_local_micro'] = None
resource_uri = LineItem.RESOURCE.format(account_id=self.ad_account.id, id=remote_object.id)
print params
twitter_request = Request(client, 'put', resource_uri, params=params)
response = twitter_request.perform()

… however, no luck, apparently bid_amount_local_micro isn’t being sent at all still:

<BadRequest object at 0x8642240L code=400 details=[{u\'message\': u\'"bid_amount_local_micro" is a required parameter\', u\'code\':
u\'MISSING_PARAMETER\', u\'parameter\': u\'bid_amount_local_micro\'}]>

But I printed out params before sending, here they all are:

{u'bid_amount_local_micro': None,
 'bid_type': u'AUTO',
 'bid_unit': u'LINK_CLICK',
 'campaign_id': u'snipped'',
 'charge_by': u'LINK_CLICK',
 'created_at': u'snipped',
 'id': u'by request!',
 'include_sentiment': u'POSITIVE_ONLY',
 'name': u'Test',
 'objective': u'WEBSITE_CLICKS',
 'optimization': u'WEBSITE_CONVERSIONS',
 'paused': 'true',
 'placements': 'TWITTER_TIMELINE',
 'product_type': u'PROMOTED_TWEETS',
 'total_budget_amount_local_micro': 100000000,
 'updated_at': u'snipped'}
}

My bid_amout_local_micro is there in the params, but still not being sent. Thankfully the OAuth layer can be logged, so let’s log what OAuth sees beyond the Twitter SDK: (OAuth secrets removed)

Collected params:
[(u'bid_type', u'AUTO'),
 (u'name', u'Test'),
 (u'created_at', u'snip),
 (u'total_budget_amount_local_micro', u'100000000'),
 (u'campaign_id', u'snip'),
 (u'bid_unit', u'LINK_CLICK'),
 (u'updated_at', u'snip'),
 (u'paused', u'true'),
 (u'charge_by', u'LINK_CLICK'),
 (u'optimization', u'WEBSITE_CONVERSIONS'),
 (u'objective', u'WEBSITE_CLICKS'),
 (u'product_type', u'PROMOTED_TWEETS'),
 (u'include_sentiment', u'POSITIVE_ONLY'),
 (u'id', u'by request'),
 (u'placements', u'TWITTER_TIMELINE'),
 (u'account_id', u'by request')]

… my bid_amount_local_micro is gone again!

I tried in one last desperate attempt to set the bid_amount_local_micro to False to no avail - the API still expects a Long.

What’s going on? How can I send a null value through the Python Twitter SDK?


#2

Thanks for the details, @REMcAuley. We’ll try to take a look in the next few days and post an update when we know more.


#3

Hey, I believe the problem might be from passing the automatically_select_bid parameter - that one is basically deprecated and removing it might make it work. Here is the sequence I just tried in IRB:

2.0.0-p247 :010 > campaign_1 = TwitterAds::Campaign.new(account)
 => #<TwitterAds::Campaign:0x70153299104300>
2.0.0-p247 :011 > campaign_1.funding_instrument_id = account.funding_instruments.first.id
 => "ixj7f"
2.0.0-p247 :012 > campaign_1.daily_budget_amount_local_micro = 1_000_000
 => 1000000
2.0.0-p247 :013 > campaign_1.name       = 'test0111111'
 => "test0111111"
2.0.0-p247 :014 > campaign_1.paused     = true
 => true
2.0.0-p247 :015 > campaign_1.start_time = Time.now.utc
 => 2017-04-06 06:09:38 UTC
2.0.0-p247 :016 > line_item_1 = TwitterAds::LineItem.new(account)
 => #<TwitterAds::LineItem:0x70153299330020>
2.0.0-p247 :017 >
2.0.0-p247 :018 >   line_item_1.campaign_id = campaign_1.id
 => nil
2.0.0-p247 :019 > line_item_1.campaign_id = campaign_1.id
 => nil
2.0.0-p247 :020 > line_item_1.name = 'my first ad'
 => "my first ad"
2.0.0-p247 :021 > line_item_1.product_type = TwitterAds::Product::PROMOTED_TWEETS
 => "PROMOTED_TWEETS"
2.0.0-p247 :022 >
2.0.0-p247 :023 >   line_item_1.placements = [TwitterAds::Placement::ALL_ON_TWITTER]
 => ["ALL_ON_TWITTER"]
2.0.0-p247 :024 > line_item_1.objective = TwitterAds::Objective::TWEET_ENGAGEMENTS
 => "TWEET_ENGAGEMENTS"
2.0.0-p247 :025 > line_item_1.bid_type = 'AUTO'
 => "AUTO"
2.0.0-p247 :026 > line_item_1.paused = true
 => true
2.0.0-p247 :027 > campaign_1.save
 => #<TwitterAds::Campaign:0x70153299104300 id="80ofx">
2.0.0-p247 :029 > line_item_1.campaign_id = campaign_1.id
 => "80ofx"
2.0.0-p247 :030 > line_item_1.save
 => #<TwitterAds::LineItem:0x70153299330020 id="8dcd9">

Let us know if that doesn’t fix the issue!


#4

Hi, thanks for replying and apologies for the long delay.

I’m setting automatically_select_bid to None so that it is excluded in the API calls. In the Python SDK, automatically_select_bid is populated when you get an object as well as bid_type, which causes problems when sending updates. (the point where I have an abstract function called “clean_up_line_item” that checks for this condition to prevent errors)

You’ll note in the API call debugging I have above that automatically_select_bid is not being sent as part of the params to the server.

But this same trick I use to remove automatically_select_bid when the Python SDK is populating it, is also removing bid_amount_local_micro from my requests when I need it to be there.