Skip to main content

CLI tool for complex formats

General Information

This tool allows publishers to create master-companion Lineitems with different price-buckets inside a Google Ad Manager (GAM) automatically, to allow websites to display ads via prebid consisting of multiple adslots - Complex Formats.

This script will create one or multiple orders depending on the amount of price-buckets you want to create Lineitems for (see Examples for details).

To create Lineitems inside a Google Ad Manager certain permissions and access keys must be configured (ee Pre-Requisites for details).

Targeting

To correctly target complex formats a new key-value for the format-type will automatically be created, called stroeer-ssp-format. Currently supported formats are "wallpaper" and "fireplace".

If not otherwise specified a new price-bucket key value will be created (if not already used beforehand) called stroeer-ssp-hb-pb. Remember: The price-bucket name can not be longer than 20 letters, otherwise GAM will cut it off.

Pre-Requisites

  1. Clone the repository.
  2. Install Python (minimum version: 3.6 - successfully tested with 3.9.12)
  3. Create Virtual Environment python -m venv /path/to/new/virtual/environment (see https://docs.python.org/3/library/venv.html#creating-virtual-environments)
  4. Activate virtual python environment: source venv/bin/activate.
  5. Install dependencies: pip install -r requirements.txt.
  6. Create a API Key for GAM api here: https://console.cloud.google.com/apis/dashboard (see doc: https://developers.google.com/ad-manager/api/authentication).
  7. Make sure your GAM allows api access (see: https://developers.google.com/ad-manager/api/start#enable_api).
  8. Create a folder secret at the top level of the directory and paste your api-key-file from step 4 into the folder.
  9. create a googleads.yaml on the top level of the directory and paste the following syntax in it:
    ad_manager:
    application_name: stroeerCore-line-item-manager
    network_code: YOUR_GOOGLE_ADMANAGER_NETWORK_CODE
    path_to_private_key_file: PATH_TO_YOUR_SECRET_FILE

Command

WARNING: Only use --write true after testing your configuration!

python line-item-creator.py
--dfp-id <dfp-id>
--format <'wallpaper'|'fireplace'>
--line-item-type <'standard'|'sponsorship'|'network'|'bulk'|'price_priority'|'house'>
--line-item-priority <0-14>
--master-size <master-size>
--companion-sizes <companion-sizes>
--start-price-bucket <start-price-bucket>
--end-price-bucket <end-price-bucket>
--price-bucket-step <price-bucket-step>
--advertiser-id <company-id>
--trafficker-id <your_account_id>
[--price-bucket-key-value-name <price-bucket-key-value-name>]
[--hb-adid-parameter <hb-adid-parameter>]
[--target-ad-units <target-ad-units>]
[--currency <'EUR'|'GDP'|'USD'>]
[--start-time <YYYY-MM-DD HH:MM:SS|'immediately'|'one-hour-from-now'>]
[--end-time <YYYY-MM-DD HH:MM:SS|'unlimited'>]
[--write <true|false>]

P.S.: We want to offer writing this into a config file for easier configuration in the future.

Required arguments

The following list of arguments are required to create the Lineitems inside the Google Ad Manager.

  • --dfp-id <dfp-id>

    GAM Network Code / DFP ID

    The Network Code can be found when you're logged in the GAM on the top left sight under the Network Name.

  • --format <'wallpaper'|'fireplace'>

    Format name (allowed values: 'wallpaper' & 'fireplace').

  • --line-item-type <'standard'|'sponsorship'|'network'|'bulk'|'price-priority'|'house'>

    Line item type (allowed values: 'standard', 'sponsorship', 'network', 'bulk', 'price-priority' & 'house').

    Recommandation: Do not use 'standard' or 'bulk', since they require absolute goals and an specific end-date and don't allow 'unlimited' as run-time.

  • --line-item-priority <number>

    Line item priority, a number between 0 and 14.

    Warning: make sure the selected line-item-priority matches the allowed priorities for your selected line item type.

  • --master-size <master-size>

    Size of the master-part of the master-companion-creative.

    Input is a string of 'widthxheight', required for all formats.

  • --companion-sizes <companion-sizes>

    Size(s) of the companion-part(s) of the master-companion-creative.

    Input is a string of 'widthxheight' or 'widthxheight, widthxheight, ...', for as many companions as are required per format (Wallpaper: 1 companion, Fireplace: 2 companions).

    An error will be thrown when given amount of companions doesn't match required amount of passed format.

  • --start-price-bucket <start-price-bucket>

    Lowest price-bucket value to be set in cents (e.g. 500 for 5.00€).

  • --end-price-bucket <end-price-bucket>

    Highest price-bucket value to be set in cents (e.g. 1000 for 10.00€).

  • --price-bucket-step <price-bucket-step>

    Steps between price-buckets in cents (e.g. 25 for 0.25€).

    Warning: Price-buckets will be calculated from start-price-bucket in steps upwards. If end-price-bucket does not match start-price-bucket + n * price-bucket-step, end-price-bucket will be manually set and calculation will be cut off.

    Example: start-price-bucket = 200, end-price-bucket = 300, price-bucket-step = 30 will result in the following price-buckets 200, 230, 260, 290, 300.

    Keep in mind: passed price-bucket steps should match prebid's price-bucket rounding, else your targeting might never apply.

    See price-bucket-key-value-name for more info.

  • --advertiser-id <advertiser-id>

    ID of the company order(s) will be placed under. Can be found within the GAM in Admin > Companies. Click on a company and in the url you will see the ID, representing the company.

  • --trafficker-id <trafficker-id>

    ID of the GAM User assigned as trafficker for the order(s). Can be found within the GAM in Admin > Access & Authorization. Click on a user and in the url you will see the ID representing the user.

Optional arguments

The following list of arguments is optional and can be left empty. It's advisable to have a look through them before running the code to customize the Lineitems as needed. These parameters will be prompted in your command line if not specified in the initial call.

  • price-bucket-key-value-name <price-bucket-key-value-name> (default 'stroeer_ssp_hb_pb')

    You can pass an existing price-bucket key-value (Note: Will throw an error if key-value does not already exist in your GAM). All calculated price-buckets (see: price-bucket-step) will be mapped to the next closest price-bucket in side the existing key-value if necessary.

    If unused, a key-value 'stroeer_ssp_hb_pb' will be created if necessary and all calculated price-buckets will be created and used. If key already exists, non-existing values will be added to existing keys.

  • --hb-adid-parameter <hb-adid-parameter (default 'hb_adid')

    Name of the hb_adid parameter for master-creative. Per default hb_adid, customize if your GAM uses a different parameter for the adid.

  • --target-ad-units <target-ad-units> (default 'run-of-network')

    Allows you to pass specific adunits lineitems should target. If unused, will target run of network.

  • --currency <currency> (default 'EUR')

    Allows currency definition, defaults to 'EUR'. Other options are 'GDP' and 'USD'.

  • --start-time <start-time> (default 'immediately')

    Allows a specific datetime of 'YYYY-MM-DD HH:MM:SS' or 'immediately' or 'one-hour-from-now', defaults to 'immediately'.

  • --end-time <end-time> (default 'unlimited')

    Allows a specific datetime of 'YYYY-MM-DD HH:MM:SS' or 'unlimited', defaults to unlimited.

    Exception: Line-Item-Types 'standard' and 'bulk' don't allow 'unlimited' as end-time, hence will be mapped to ten years from now (minus leap days). If this behavior is undesired, specify an end-date.

  • --write <true|false> (default false)

    WARNING: Only use after testing your configuration.

    As long as write is set to false, this script will only read data from the GAM and log what the created Lineitems will look like but won't create any orders, keys or Lineitems in the GAM.

    When you are sure everything is configured to your wishes, set write to true, to create the Lineitems in the GAM.

Examples

Sponsorship Lineitems for Wallpaper

with:

  • price-buckets between 5.00€ and 5.50€ in 0.25€ steps
  • using default pb-key-value "stroeer_ssp_hb_pb", adid "hb_adid" and adunit "run-of-network"
  • using default times immediately and unlimited
python line-item-creator.py --format wallpaper --dfp-id <your-dfp-id> --line-item-type sponsorship --line-item-priority 4 --master-size 728x90 --companion-size 160x600 --start-price-bucket 500 --end-price-bucket 550 --price-bucket-step 25 --advertiser-id <your-advertiser-id> --trafficker-id <your-trafficker-id>
Results in 3 Lineitems:

Since this is a test run, orderID & targeting-key- and -value-IDs will be 0. On active request they will be filled accordingly.

Hint: startDateTime will be disregarded when startDateTimeType is "IMMEDIATELY" or "ONE_HOUR_FROM_NOW" & endDateTime will be disregarded if unlimitedEndDateTime is TRUE

[
{
"orderId": 0,
"name": "stroeer_ssp_wallpaper_5.0",
"startDateTime": "immediately",
"startDateTimeType": "IMMEDIATELY",
"endDateTime": "unlimited",
"unlimitedEndDateTime": "TRUE",
"creativeRotationType": "EVEN",
"companionDeliveryOption": "ALL",
"roadblockingType": "CREATIVE_SET",
"lineItemType": "SPONSORSHIP",
"priority": 4,
"costPerUnit": {
"currencyCode": "EUR",
"microAmount": 5000000
},
"costType": "CPM",
"creativePlaceholders": [
{
"size": {
"width": 728,
"height": 90
},
"companions": [
{
"size": {
"width": 160,
"height": 600
}
}
]
}
],
"targeting": {
"inventoryTargeting": {
"targetedAdUnits": [
{
"adUnitId": "run-of-network"
}
]
},
"customTargeting": {
"logicalOperator": "AND",
"children": [
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
},
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
}
]
}
},
"primaryGoal": {
"goalType": "DAILY",
"unitType": "IMPRESSIONS",
"units": 100
}
},
{
"orderId": 0,
"name": "stroeer_ssp_wallpaper_5.25",
"startDateTime": "immediately",
"startDateTimeType": "IMMEDIATELY",
"endDateTime": "unlimited",
"unlimitedEndDateTime": "TRUE",
"creativeRotationType": "EVEN",
"companionDeliveryOption": "ALL",
"roadblockingType": "CREATIVE_SET",
"lineItemType": "SPONSORSHIP",
"priority": 4,
"costPerUnit": {
"currencyCode": "EUR",
"microAmount": 5250000
},
"costType": "CPM",
"creativePlaceholders": [
{
"size": {
"width": 728,
"height": 90
},
"companions": [
{
"size": {
"width": 160,
"height": 600
}
}
]
}
],
"targeting": {
"inventoryTargeting": {
"targetedAdUnits": [
{
"adUnitId": "run-of-network"
}
]
},
"customTargeting": {
"logicalOperator": "AND",
"children": [
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
},
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
}
]
}
},
"primaryGoal": {
"goalType": "DAILY",
"unitType": "IMPRESSIONS",
"units": 100
}
},
{
"orderId": 0,
"name": "stroeer_ssp_wallpaper_5.5",
"startDateTime": "immediately",
"startDateTimeType": "IMMEDIATELY",
"endDateTime": "unlimited",
"unlimitedEndDateTime": "TRUE",
"creativeRotationType": "EVEN",
"companionDeliveryOption": "ALL",
"roadblockingType": "CREATIVE_SET",
"lineItemType": "SPONSORSHIP",
"priority": 4,
"costPerUnit": {
"currencyCode": "EUR",
"microAmount": 5500000
},
"costType": "CPM",
"creativePlaceholders": [
{
"size": {
"width": 728,
"height": 90
},
"companions": [
{
"size": {
"width": 160,
"height": 600
}
}
]
}
],
"targeting": {
"inventoryTargeting": {
"targetedAdUnits": [
{
"adUnitId": "run-of-network"
}
]
},
"customTargeting": {
"logicalOperator": "AND",
"children": [
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
},
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
}
]
}
},
"primaryGoal": {
"goalType": "DAILY",
"unitType": "IMPRESSIONS",
"units": 100
}
}
]

Price-Priority Lineitems for Fireplace

with:

  • price-buckets between 10.00$ and 11.00$ in 0.50$ steps
  • using custom times
  • custom price-bucket key-value-name (not visible in line-item-json due to GAM SOAP API using key- and value-ids instead of names)
  • custom adid-parameter (not visible in line-item-json, only in creative-code)
  • custom ad-unit
python line-item-creator.py --format fireplace --dfp-id <your-dfp-id> --line-item-type price-priority --line-item-priority 12 --master-size 728x90 --companion-size "120x600, 120x600" --start-price-bucket 1000 --end-price-bucket 1100 --price-bucket-step 50 --advertiser-id <your-advertiser-id> --trafficker-id <your-trafficker-id> --start-time "2025-06-01 00:00:00" --end-time "2025-08-30 23:59:59" --hb-adid-parameter "your-custom-adid" --currency "USD" --target-ad-units "custom-adunit"
Results in 3 Lineitems:

Since this is a test run, orderID & targeting-key- and -value-IDs will be 0. On active request they will be filled accordingly.

[
{
"orderId": 0,
"name": "stroeer_ssp_fireplace_10.0",
"startDateTime": "2025-06-01 00:00:00",
"startDateTimeType": "USE_START_DATE_TIME",
"endDateTime": "2025-08-30 23:59:59",
"unlimitedEndDateTime": "FALSE",
"creativeRotationType": "EVEN",
"companionDeliveryOption": "ALL",
"roadblockingType": "CREATIVE_SET",
"lineItemType": "PRICE_PRIORITY",
"priority": 12,
"costPerUnit": {
"currencyCode": "USD",
"microAmount": 10000000
},
"costType": "CPM",
"creativePlaceholders": [
{
"size": {
"width": 728,
"height": 90
},
"companions": [
{
"size": {
"width": 120,
"height": 600
}
},
{
"size": {
"width": 120,
"height": 600
}
}
]
}
],
"targeting": {
"inventoryTargeting": {
"targetedAdUnits": [
{
"adUnitId": "custom-adunit"
}
]
},
"customTargeting": {
"logicalOperator": "AND",
"children": [
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
},
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
}
]
}
},
"primaryGoal": {
"goalType": "NONE"
}
},
{
"orderId": 0,
"name": "stroeer_ssp_fireplace_10.5",
"startDateTime": "2025-06-01 00:00:00",
"startDateTimeType": "USE_START_DATE_TIME",
"endDateTime": "2025-08-30 23:59:59",
"unlimitedEndDateTime": "FALSE",
"creativeRotationType": "EVEN",
"companionDeliveryOption": "ALL",
"roadblockingType": "CREATIVE_SET",
"lineItemType": "PRICE_PRIORITY",
"priority": 12,
"costPerUnit": {
"currencyCode": "USD",
"microAmount": 10500000
},
"costType": "CPM",
"creativePlaceholders": [
{
"size": {
"width": 728,
"height": 90
},
"companions": [
{
"size": {
"width": 120,
"height": 600
}
},
{
"size": {
"width": 120,
"height": 600
}
}
]
}
],
"targeting": {
"inventoryTargeting": {
"targetedAdUnits": [
{
"adUnitId": "custom-adunit"
}
]
},
"customTargeting": {
"logicalOperator": "AND",
"children": [
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
},
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
}
]
}
},
"primaryGoal": {
"goalType": "NONE"
}
},
{
"orderId": 0,
"name": "stroeer_ssp_fireplace_11.0",
"startDateTime": "2025-06-01 00:00:00",
"startDateTimeType": "USE_START_DATE_TIME",
"endDateTime": "2025-08-30 23:59:59",
"unlimitedEndDateTime": "FALSE",
"creativeRotationType": "EVEN",
"companionDeliveryOption": "ALL",
"roadblockingType": "CREATIVE_SET",
"lineItemType": "PRICE_PRIORITY",
"priority": 12,
"costPerUnit": {
"currencyCode": "USD",
"microAmount": 11000000
},
"costType": "CPM",
"creativePlaceholders": [
{
"size": {
"width": 728,
"height": 90
},
"companions": [
{
"size": {
"width": 120,
"height": 600
}
},
{
"size": {
"width": 120,
"height": 600
}
}
]
}
],
"targeting": {
"inventoryTargeting": {
"targetedAdUnits": [
{
"adUnitId": "custom-adunit"
}
]
},
"customTargeting": {
"logicalOperator": "AND",
"children": [
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
},
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
}
]
}
},
"primaryGoal": {
"goalType": "NONE"
}
}
]

Standard Lineitems for Wallpaper with uneven price-buckets

with:

  • price-buckets between 8.00€ and 8.50€ in 0.30€ steps (will result in 8.00, 8.30, 8.50 as brice-buckets)
  • start-date one hour from now, end-date not defined, therefore set to 10 years from now, since standard Lineitems can't have unlimited as end-date
python3 line-item-creator.py --format wallpaper --dfp-id <your-dfp-id> --line-item-type standard --line-item-priority 8 --master-size 728x90 --companion-size "120x600" --start-price-bucket 800 --end-price-bucket 850 --price-bucket-step 30 --advertiser-id <your-advertiser-id> --trafficker-id <your-trafficker-id>>
Results in 3 Lineitems:

Since this is a test run, orderID & targeting-key- and -value-IDs will be 0. On active request they will be filled accordingly. Hint: startDateTime will be disregarded when startDateTimeType is "IMMEDIATELY" or `"ONE_HOUR_FROM_NOW"

[
{
"orderId": 0,
"name": "stroeer_ssp_wallpaper_8.0",
"startDateTime": "one_hour_from_now",
"startDateTimeType": "ONE_HOUR_FROM_NOW",
"endDateTime": "2035-05-18 07:04:04",
"unlimitedEndDateTime": "FALSE",
"creativeRotationType": "EVEN",
"companionDeliveryOption": "ALL",
"roadblockingType": "CREATIVE_SET",
"lineItemType": "STANDARD",
"priority": 8,
"costPerUnit": {
"currencyCode": "EUR",
"microAmount": 8000000
},
"costType": "CPM",
"creativePlaceholders": [
{
"size": {
"width": 728,
"height": 90
},
"companions": [
{
"size": {
"width": 120,
"height": 600
}
}
]
}
],
"targeting": {
"inventoryTargeting": {
"targetedAdUnits": [
{
"adUnitId": "run-of-network"
}
]
},
"customTargeting": {
"logicalOperator": "AND",
"children": [
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
},
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
}
]
}
},
"primaryGoal": {
"goalType": "LIFETIME",
"unitType": "IMPRESSIONS",
"units": 100000
}
},
{
"orderId": 0,
"name": "stroeer_ssp_wallpaper_8.3",
"startDateTime": "one_hour_from_now",
"startDateTimeType": "ONE_HOUR_FROM_NOW",
"endDateTime": "2035-05-18 07:04:04",
"unlimitedEndDateTime": "FALSE",
"creativeRotationType": "EVEN",
"companionDeliveryOption": "ALL",
"roadblockingType": "CREATIVE_SET",
"lineItemType": "STANDARD",
"priority": 8,
"costPerUnit": {
"currencyCode": "EUR",
"microAmount": 8300000
},
"costType": "CPM",
"creativePlaceholders": [
{
"size": {
"width": 728,
"height": 90
},
"companions": [
{
"size": {
"width": 120,
"height": 600
}
}
]
}
],
"targeting": {
"inventoryTargeting": {
"targetedAdUnits": [
{
"adUnitId": "run-of-network"
}
]
},
"customTargeting": {
"logicalOperator": "AND",
"children": [
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
},
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
}
]
}
},
"primaryGoal": {
"goalType": "LIFETIME",
"unitType": "IMPRESSIONS",
"units": 100000
}
},
{
"orderId": 0,
"name": "stroeer_ssp_wallpaper_8.5",
"startDateTime": "one_hour_from_now",
"startDateTimeType": "ONE_HOUR_FROM_NOW",
"endDateTime": "2035-05-18 07:04:04",
"unlimitedEndDateTime": "FALSE",
"creativeRotationType": "EVEN",
"companionDeliveryOption": "ALL",
"roadblockingType": "CREATIVE_SET",
"lineItemType": "STANDARD",
"priority": 8,
"costPerUnit": {
"currencyCode": "EUR",
"microAmount": 8500000
},
"costType": "CPM",
"creativePlaceholders": [
{
"size": {
"width": 728,
"height": 90
},
"companions": [
{
"size": {
"width": 120,
"height": 600
}
}
]
}
],
"targeting": {
"inventoryTargeting": {
"targetedAdUnits": [
{
"adUnitId": "run-of-network"
}
]
},
"customTargeting": {
"logicalOperator": "AND",
"children": [
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
},
{
"xsi_type": "CustomCriteria",
"keyId": 0,
"valueIds": [
0
],
"operator": "IS"
}
]
}
},
"primaryGoal": {
"goalType": "LIFETIME",
"unitType": "IMPRESSIONS",
"units": 100000
}
}
]

Creative Snippets

These are the creatives that will be created automatically once for each format and used for the Lineitem-CreativeAssociation.

The Master-Creative is the Universal-Creative provided by prebid (see: https://github.com/prebid/prebid-universal-creative & https://docs.prebid.org/overview/prebid-universal-creative.html) and can be customized after the creation inside the GAM.

You'll find the creative under a name of the following pattern: stroeer_ssp_<format>__hb_master_creative and stroeer_ssp_<format>__hb_companion_creative.

Show Master-Creative-Snippet
<script src = "https://cdn.jsdelivr.net/npm/prebid-universal-creative@latest/dist/%%PATTERN:hb_format%%.js"></script>
<script>
var ucTagData = {{}};
ucTagData.adServerDomain = "";
ucTagData.pubUrl = "%%PATTERN:url%%";
ucTagData.targetingMap = %%PATTERN:TARGETINGMAP%%;
ucTagData.hbPb = "%%PATTERN:your_pb_key_value_name%%";
ucTagData.hbFormat = "%%PATTERN:hb_format%%";
ucTagData.adId = "%%PATTERN:your_hb_adid_parameter_name%%";
// if you're using GAM and want to track outbound clicks on native ads you can add this line
ucTagData.clickUrlUnesc = "%%CLICK_URL_UNESC%%";
ucTagData.requestAllAssets = true;

try {{
ucTag.renderAd(document, ucTagData);
}} catch (e) {{
console.log(e);
}}
</script>
Show Companion-Creative-Snippet
<script></script>