Insert ads with ad pod timing metadata

  • Google Ad Manager's Pod Serving API enables dynamic ad insertion into live HLS and DASH streams for advanced publishers.

  • Before using the API, publishers need to configure livestream events in Google Ad Manager and set up a manifest manipulator to handle stream requests and ad stitching.

  • HLS stitching involves proxying playlists, identifying ad breaks, and inserting ad segment URLs, while DASH stitching uses a period template populated with dynamic macros.

  • Successful ad delivery requires proper authentication, URL encoding, and handling of stream manifests and ad break cues.

  • Publishers can leverage provided documentation and SDKs for various platforms to implement DAI with the IMA SDK and Pod Serving API.

This guide describes how to insert ad breaks using the Ad pod Timing Metadata (ATM) method to fetch the precise timing and duration for ad breaks, including pre-roll ads.

To insert pre-roll ads and manage the return to content after mid-roll ad breaks, we recommend calling the following APIs:

Optionally, for high concurrency livestream events, we recommend calling Early Ad Break Notification (EABN) API to schedule ad decisions before the ad break begins.

Prerequisites

To get started, you need a livestream event set up for the Pod serving redirect Dynamic Ad Insertion (DAI) type. Choose one of the following methods:

Retrieve the content stream

When a user selects a livestream event, the client app makes a stream request to Google Ad Manager. In the stream response, the app extracts the Google DAI session ID and metadata to include in the stream manifest request.

The following example passes a Google DAI session ID to a manifest manipulator:

https://MANIFEST_MANIPULATOR_URL/manifest.m3u8?DAI_stream_ID=SESSION_ID&network_code=NETWORK_CODE&DAI_custom_asset_key=CUSTOM_ASSET_KEY

When processing the video content playback request, store the Google DAI session ID and CUSTOM_ASSET_KEY from the request to prepare for ad stitching.

Retrieve ad pod timing metadata

To retrieve the ad pod timing, follow these steps:

  1. Generate an HMAC token.
  2. Call the ATM API with the HMAC token.

Generate a signed HMAC token

To authenticate your requests, follow these steps:

  1. Create a token string by concatenating the following parameters in alphabetical order, separated by a tilde ~:

    Parameter Required or optional Description
    ad_break_id Required A string that you specify to identify the ad break, for example ad-break-1. For pre-roll ads, use preroll.
    custom_asset_key Required The livestream event's custom asset key.
    cust_params Optional Custom targeting parameters. For more details, see Supply targeting parameters to your stream.
    exp Required The time when this token expires, measured as the total number of seconds that have passed since Unix epoch.
    network_code Required Your Google Ad Manager network code. For more details, see Find Ad Manager account information.
    pd Required The ad break duration in milliseconds. For pre-roll ads, Google DAI overrides this parameter with your livestream event settings.
    scte35 Optional Base64-encoded SCTE-35 signal. Verify that the signal is correct. If incorrect, a message is sent to the X-Ad-Manager-Dai-Warning HTTP header in the response and the signal is still propagated to create an ad break. For more details about supported ad break markers, see HLS integration.
  2. Download your Google DAI authentication key of type HMAC. For more details, see Authenticate DAI video stream requests.

  3. Use your downloaded HMAC key to generate the SHA256 signature of the token string.

  4. Concatenate the token string and the generated signature.

  5. Apply URL encoding to the concatenated string. The encoded string is the signed HMAC token for authenticating the Ad pod Timing Metadata (ATM) API requests.

The following example generates a signed HMAC token for pre-roll ads:

custom_asset_key="CUSTOM_ASSET_KEY"
exp="1750700000" # Expired on Mon Jun 23 2025 13:33:20 GMT-0400 (Eastern Daylight Time)
network_code="NETWORK_CODE"
ad_break_id="preroll"
pd="0" # Pod duration value is overridden by the livestream event settings.

# The HMAC authentication key associated with your livestream event in Google Ad Manager.
secret_key="24E96382584C328087546B0E8454F26158564E8466FD2BE3D8A996B38445876C"

# Concatenate the parameters, keep the parameters alphabetically ordered by name.
token_string="ad_break_id=${ad_break_id}~custom_asset_key=${custom_asset_key}~exp=${exp}~network_code=${network_code}~pd=${pd}"

# Calculate the SHA256 signature of the token_string.
hmac_signature=$(echo -n "$token_string" | openssl dgst -sha256 -hmac "$secret_key" | awk '{print $2}')

# Concatenate the token string and the signature.
signed_token="${token_string}~hmac=${hmac_signature}"

url_encode() {
    local string="${1}"
    local strlen=${#string}
    local encoded=""
    local pos c
    for (( pos=0 ; pos<strlen ; pos++ )); do
        c=${string:$pos:1}
        case "$c" in
            [-_.~a-zA-Z0-9] ) o="${c}" ;;
            *   ) printf -v o '%%%02x' "'$c"
        esac
        encoded+="${o}"
    done
    echo "${encoded}"
}

# Apply URL encoding to the concatenated string.
url_encoded_signed_token=$(url_encode "$signed_token")

echo "Signed HMAC token:"
echo "${url_encoded_signed_token}"

# Example output:
# ad_break_id%3Dpreroll~custom_asset_key%3DiYdOkYZdQ1KFULXSN0Gi7g~exp%3D1489680000~network_code%3D21775744923~pd%3D180000~pod_id%3D5~hmac%3D24E96382584C328087546B0E8454F26158564E8466FD2BE3D8A996B38445876C

Request timing metadata for pre-roll ads

Verify pre-roll settings for your livestream event using the following options:

To retrieve the pre-roll ad decision results, make a request to the ATM API.

The following example makes an ATM request for pre-roll ads:

curl "https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/pod.json?stream_id=SESSION_ID&ad_break_id=preroll&auth-token=your_signed_HMAC_token"

Request timing metadata for mid-roll ads

To retrieve ad pod metadata for mid-rolls, follow these steps:

  1. Parse the livestream manifest to find the ad markers that contain the timing and duration for each mid-roll ad break.
  2. Call the ATM API endpoint to request the precise ad pod and slate duration. The API returns a JSON object with the ad pod's decision results.

The following example makes an ATM request for mid-roll ads:

curl "https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/pod.json?stream_id=SESSION_ID&ad_break_id=AD_BREAK_ID&pd=AD_BREAK_DURATION&auth-token=your_signed_HMAC_token"

If successful, you see output similar to the following JSON object:

{
  "status": "final",
  "ads": [
    {
      "duration_ms": 5046,
      "variants": {
        "devrel1428000": {
          "segment_extension": "ts",
          "segment_durations": {
            "timescale": 1000,
            "values": [
              5045
            ]
          }
        },
        "devrel1928000": {
          "segment_extension": "ts",
          "segment_durations": {
            "timescale": 1000,
            "values": [
              5045
            ]
          }
        }
      }
    }
  ],
  "slate": {
    "duration_ms": 0,
    "variants": {
      "devrel1428000": {
        "segment_extension": "ts",
        "segment_durations": {
          "timescale": 1000,
          "values": [
            5005,
            ...
            5046
          ]
        }
      },
      "devrel1928000": {
        "segment_extension": "ts",
        "segment_durations": {
          "timescale": 1000,
          "values": [
            5005,
            ...
            5046
          ]
        }
      }
    }
  }
}

Stitch ads into content manifest

The following sections walk you through how to modify the livestream manifest and add the ad segments.

Identify ad break segments and insert discontinuities

As you process each variant manifest, identify the EXT-X-CUE-IN and EXT-X-CUE-OUT tags in your stream, indicating the start and end of an ad break.

Replace the EXT-X-CUE-IN and EXT-X-CUE-OUT tags with the EXT-X-DISCONTINUITY elements for the client video player to switch between content and ads.

The following example manifest replaces the EXT-X-CUE-IN and EXT-X-CUE-OUT tags:

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:5.000,
contentorigin.com/1.ts
#EXTINF:5.000,
contentorigin.com/2.ts
#EXT-X-CUE-OUT:15.000
#EXTINF:5.000,
contentorigin.com/3.ts
#EXTINF:5.000,
contentorigin.com/4.ts
#EXTINF:5.000,
contentorigin.com/5.ts
#EXT-X-CUE-IN
#EXTINF:5.000,
contentorigin.com/6.ts
#EXTINF:5.000,
contentorigin.com/7.mp4
#EXTINF:5.000,
contentorigin.com/8.mp4

The following example shows a replaced manifest:

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:5.000,
contentorigin.com/1.ts
#EXTINF:5.000,
contentorigin.com/2.ts
#EXTINF:5.000,
#EXT-X-DISCONTINUITY
{... Insert ad segments here ...}
#EXT-X-DISCONTINUITY
#EXTINF:5.000,
contentorigin.com/6.mp4
#EXTINF:5.000,
contentorigin.com/7.mp4
#EXTINF:5.000,
contentorigin.com/8.mp4

Google DAI ad segments are not encrypted. If your content is encrypted, remove encryption by inserting EXT-X-KEY:METHOD=NONE element prior to the first ad segment of each ad break. At the end of the ad break, add encryption back by inserting an appropriate EXT-X-KEY.

Keep track of the start time, duration, and index of the upcoming ad break.

Build ad segment URLs

Replace the content segments between the EXT-X-DISCONTINUITY tags with URLs for each ad segment. To determine how many ad segments to insert, use the ads.segment_durations.values provided in the JSON response from the ATM API.

To end an ad pod early when detecting an EXT-X-CUE-IN tag, add the d= parameter to the URL of the final ad segment. This parameter shortens the segment to avoid affecting the client video player's timeline.

The following example assembles a pre-roll ad segment URL to the manifest. Note that ad segments use a zero-based index:

https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/preroll/ad/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID

The following example assembles a mid-roll ad segment URL to the manifest:

https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID

The following example inserts ad segments to the manifest:

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:5.00,
contentorigin.com/1.ts
#EXTINF:5.00,
contentorigin.com/2.ts
#EXT-X-DISCONTINUITY
#EXTINF:5.00,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID
#EXTINF:5.00,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/1.ts?stream_id=SESSION_ID
#EXTINF:5.00,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/2.ts?stream_id=SESSION_ID
#EXT-X-DISCONTINUITY
#EXTINF:5.00,
contentorigin.com/6.mp4
#EXTINF:5.00,
contentorigin.com/7.mp4
#EXTINF:5.00,
contentorigin.com/8.mp4

Build slate segments

To fill the gap between ad and content, insert slate segments. Use the slates.segment_durations.values array from the ATM API's JSON response to determine the duration of each slate segment. Loop the sequence of segment durations as needed to fill the entire ad break.

The following example assembles a slate segment:

https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/slate/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID

The slate/0 in the example represents the slate iteration number. Refer to your client video player's compatibility and caching capability to determine whether to start at 0 and increment this number for each loop of the slate, or keep it at 0 for all iterations.

Manage the return to content

After you insert all segments from the ad pod, choose one of the following methods to transition back to your content stream:

Method Description Effects on client video player
Fill with slate segments Inserts the slate segments and loops the slate. Fills the duration and inserts EXT-X-DISCONTINUITY elements between each slate iteration. No effects.
The video player transitions back to the content without altered timeline.
Re-align with a single slate segment Inserts a single slate segment. Uses the d= parameter to fill the duration until the content start. No effects.
The video player transitions back to the content without altered timeline.
Immediate Return Insert content segments. The video player's timeline is altered.
Your client video player must handle the altered timeline.

Optional: Schedule an ad break

To enhance your fill rate, send an Early Ad Break Notification (EABN) with the ad pod duration, custom targeting parameters, and SCTE-35 signal data. For more details, see Send early ad break notifications.