Skip to content
<- Guides
Amazon Ads · Reporting

Amazon Marketing Stream API: Set Up Version 2

How to set up the Amazon Marketing Stream API for Version 2: Amazon's manual path (destination, role policies, subscription call, record-level processing) or Kuudo's, where your AI does it through the Amazon Ads MCP in a few minutes.

Set up the Amazon Marketing Stream API for Version 2 two ways: Amazon's manual AWS and Ads API steps, or a few minutes through the Amazon Ads MCP.

Kuudo
Reviewed by Kuudo Engineering
ChatGPT setting up Amazon Marketing Stream Version 2 through the Amazon Ads MCP: a flow from the destination Kuudo provisioned (S3 bucket, SQS queue and Amazon's role policies), to the create call for ads-performance-v1, to checking the subscription is ACTIVE and data is arriving (a missing write permission means no data and no error), to keeping the highest streamBatch.version per record, ending in the data queryable in the lake.
Amazon's manual path is AWS policy work plus code. Through the Amazon Ads MCP, your AI runs the same steps in a few minutes.
TL;DR
  • Stream Version 2 needs a destination: an S3 bucket and a standard SQS queue in the same allowed Region, with policies granting two Amazon roles in account 926844853897.
  • A subscription is one POST to /adsApi/v1/create/streamSubscriptions per dataset, advertiser account and destination, and the same combination can't be subscribed twice.
  • Stream checks only that the destination exists. A missing write permission leaves the subscription ACTIVE with no data and no error.
  • Deliveries are at least once and unordered: keep the highest streamBatch.version per full record key, read it as a 64-bit integer, and treat all-null metrics as deletes.
  • Through Kuudo, the destination is provisioned in your cloud and your AI subscribes through the Amazon Ads MCP, in a few minutes.

The Amazon Marketing Stream API sets up Version 2 in four parts: an S3 bucket and SQS queue with Amazon's roles granted, a subscription call, a consumer that keeps the newest version of each record, and subscription management. By hand, that is AWS policy work plus code. Through Kuudo's Amazon Ads MCP, it takes a few minutes.

A brand's data lead asked me: "we want Marketing Stream v2 flowing for our accounts. What do we set up in AWS, how do we create the subscription, and how do we read the files without double counting?" Amazon documents each step across six pages. Every step has a way to fail quietly.

The setup is the same work either way; the question is who does it. Kuudo provisioned the destination in our own cloud, our agent created and checked the subscription through the Amazon Ads MCP, and Kuudo landed the data in our data lake. Amazon Agent Atlas held Amazon's Version 2 rules, which is where each trap below comes from.

StepAmazon's manual pathKuudo's path
DestinationS3, SQS, two role policies, KMSProvisioned in your cloud
SubscriptionOne create call per dataset and accountYour AI asks the Ads MCP
CheckRetrieve or query with the same accountYour AI reads the status back
ProcessingUpsert code keyed on every dimensionKuudo lands it in your lake
QueryingYour own warehouse or AthenaYour AI, through Agent Flow

The manual Stream v2 destination is an S3 bucket, a standard SQS queue and two Amazon roles

Set up your destination treats an S3 bucket and an SQS queue as one destination. Stream writes Parquet files to the bucket and sends a notification to the queue for each delivery. Amazon recommends its CloudFormation template, which creates the bucket, the notification queue and a dead-letter queue, customer-managed KMS keys, access logging, and every policy Stream needs.

By hand, the steps are:

  1. Create the S3 bucket in a Region allowed for your geography: us-east-1 for North America; eu-west-1, eu-west-2 or eu-south-2 for Europe; us-west-2 or ap-southeast-1 for the Far East.
  2. Create a standard SQS queue, not a FIFO queue, in the same Region.
  3. Grant two roles in Amazon's account 926844853897. PublisherRole gets s3:PutObject on the bucket and sqs:SendMessage on the queue; ReviewerRole gets s3:ListBucket and sqs:GetQueueAttributes.
  4. If the bucket or queue uses a customer-managed KMS key, give PublisherRole kms:GenerateDataKey and kms:Decrypt in the key policy.

One destination can serve many subscriptions, because each dataset lands under its own streamDatasetId= prefix. An application can register five destinations.

On Kuudo's path this whole section is provisioned in your cloud, with the bucket, queue and Amazon's role policies in place, so the first thing you do is ask your AI for the data.

A missing write permission leaves a Stream v2 subscription ACTIVE with no data

Create a subscription is one call, POST /adsApi/v1/create/streamSubscriptions, carrying the advertiser account in accessRequestedAccounts, the dataset ID, the bucket ARN and the queue ARN. Each call creates one subscription and returns a 207 Multi-Status response, with the result in success or errors at index 0. The subscription is ACTIVE immediately, and history for the dataset's restatement period arrives within about a day.

Two rules from Limits and Data delivery decide whether it works:

  • The combination is unique. A second subscription for the same dataset, destination and account fails with "A subscription already exists for this dataset, destination, and account combination."
  • Creation doesn't test writes. Stream verifies that the destination exists and is reachable, not that it can write. If a policy lacks s3:PutObject, the subscription stays ACTIVE and nothing arrives, with no error.

The Amazon Ads MCP carries the Version 2 subscription operations. Our agent created the ads-performance-v1 subscription from a single request in the AI client, read the status back, and confirmed files were arriving rather than trusting ACTIVE. The change went through our workspace policy before it reached Amazon.

Reading Stream v2 correctly means keeping the highest streamBatch.version per record

Data delivery sends a notification naming the dataset, the batch and the exact S3 files to read, so a consumer reads the notification instead of listing the bucket. The delivery guarantees are loose on purpose:

  • At least once. The same records can arrive again under different S3 keys, so files can't be deduplicated.
  • No ordering. Notifications arrive out of order; only streamBatch.version says which record is newer.
  • Shared files. Several subscriptions to one destination can share a Parquet file.
  • Split batches. One batch can span several notifications.

Process your data turns that into four rules. Upsert every record on all of its dimension fields and keep the highest streamBatch.version. Read that version as a 64-bit integer or string, because it can exceed the safe integer range of a 64-bit float. Treat a record whose metric columns are all null as a delete. A changed dimension, such as a renamed placement, arrives as a delete of the old row plus a new row.

On Kuudo's path, Kuudo lands each delivery in your lake and your AI queries it through Amazon Agent Flow. Atlas holds these processing rules, so the agent applies the highest-version rule when it reads instead of summing every file.

Managing a Stream v2 subscription takes the same advertiser account on every call

Manage subscriptions adds three calls: retrieve one subscription, query all of an account's subscriptions (archived ones included), and delete. Each must carry the advertiser account used at creation, and each returns only subscriptions your own application created. A mismatch returns FORBIDDEN.

StatusMeansWhat to do
ACTIVEDeliveringConfirm files arrive
SUSPENDEDDestination unreachableFix it, then create a new one
ARCHIVEDDeleted by your applicationCreate a new one to resume

Stream keeps re-validating the destination. Deleting the bucket or queue, or removing the permission Stream uses to check it, moves the subscription to SUSPENDED for good. A subscription's destination is fixed at creation, so moving it means deleting and recreating the subscription.

The Amazon Ads MCP carries retrieve, query and delete, so the agent can list every subscription on an account, spot a suspended one, and recreate it after approval.

What happens next: ask your AI for the dataset, then check that files arrive

On Kuudo's path the setup is a request in your AI client:

  • Subscribe the account to ads-performance-v1 for hourly performance, or ads-converted-product-v1 for daily ASIN-level conversions.
  • Confirm Parquet files are landing, not only that the status reads ACTIVE.
  • On Version 1 today, run both versions until the totals reconcile, before Version 1 shuts off on June 30, 2027.
  • Keep scheduled, stakeholder-ready reports on the reporting API with the Build the Right Amazon Ads Report Skill.

The Selling Partner MCP covers the orders and inventory the ads drive, and the weekly optimization routine is where hourly data earns its place.

Amazon's manual path is a destination, a call, a consumer and a lifecycle to babysit. On Kuudo's, it is one request and a few minutes.

Next: the hourly data put to work, in Amazon PPC dayparting.

Private beta

Turn on Marketing Stream v2 in minutes

We'll provision the Stream destination in your cloud, connect the Amazon Ads MCP to the AI client your team uses, and land the data in your own lake so your AI can query hourly ads data.

Book a working session

What you need to run Marketing Stream v2 setup

MCP
Amazon Ads MCP for the Marketing Stream Version 2 subscription operations: create, retrieve, query and delete. Kuudo provisions the S3 and SQS destination; Amazon Agent Flow lands the data in your lake and queries it.
Atlas collection
amazon_ads, the Amazon rules behind this guide (playbooks: Set up your destination; Create a subscription; Data delivery; Process your data; Manage subscriptions; Limits)
Required subscriptions
An Amazon Ads account. On the manual path, an AWS account in an allowed Region; on Kuudo's, the destination is provisioned for you.
Related reading

Keep exploring Marketing Stream v2 setup

Use these companion guides to understand the inputs, follow-on analysis, and adjacent workflows behind this playbook.

FAQ

How do I set up the Amazon Marketing Stream API?

For Version 2, create an S3 bucket and a standard SQS queue in an allowed Region, grant Amazon's two Stream roles access, then call POST /adsApi/v1/create/streamSubscriptions with the advertiser account, the dataset ID and both ARNs. Through Kuudo, the destination is provisioned for you and your AI creates the subscription through the Amazon Ads MCP.

What AWS permissions does Marketing Stream v2 need?

Amazon's PublisherRole needs s3:PutObject on the bucket and sqs:SendMessage on the queue; its ReviewerRole needs s3:ListBucket and sqs:GetQueueAttributes. Both roles are in Amazon's account 926844853897. A customer-managed KMS key also needs kms:GenerateDataKey and kms:Decrypt for PublisherRole.

Why does my Stream v2 subscription show ACTIVE but deliver no data?

At creation, Stream checks only that the bucket and queue exist and are reachable, not that it can write to them. A missing permission such as s3:PutObject leaves the subscription ACTIVE with no data and no error. Fix the policy; if the destination was deleted, the subscription moves to SUSPENDED and needs recreating.

Can I use a FIFO queue for Amazon Marketing Stream?

No. Amazon asks for a standard SQS queue, in the same Region as the S3 bucket.

Which AWS Regions does Stream v2 support?

North America uses us-east-1. Europe uses eu-west-1, eu-west-2 or eu-south-2. Far East uses us-west-2 or ap-southeast-1. A destination outside the allowed Regions is rejected when you create the subscription.

How do I deduplicate Amazon Marketing Stream v2 data?

Upsert every record on all of its dimension fields and keep the row with the highest streamBatch.version. Deliveries can repeat and arrive out of order, so arrival order means nothing, and a record whose metrics are all null is a delete.

How many Stream v2 destinations can I create?

Five distinct S3 bucket and SQS queue pairs per client application. One destination can serve many subscriptions, because each dataset is written under its own streamDatasetId= prefix.

Sources