Python API¶
Generated from the source docstrings.
Client¶
amazon_ads.AmazonAds(credentials, *, sandbox=False, retry=None, timeout=60.0, http=None, user_agent=None, sleep=time.sleep)
¶
Account-level client. Lists profiles and hands out a :class:ProfileClient per
marketplace.
ads = AmazonAds.from_env() us = ads.profile("US") us.sp.keywords.list(campaign_ids=["123"])
from_env(*, env_file=None, **kwargs)
classmethod
¶
Build a client from AMAZON_ADS_* environment variables (and optionally a
dotenv file). See :meth:Credentials.from_env.
profiles(*, refresh=False)
¶
Every profile the token can reach, across all three regions.
Amazon only returns a region's profiles from that region's host, so this asks all three and merges the answers. Cached after the first call.
profile(market_or_id, *, account_type=None, account_name=None)
¶
A client scoped to one profile.
market_or_id is a country code ("US", "UK" or "GB") or a profile id.
When one marketplace holds several profiles (a seller and a vendor account, say),
narrow it with account_type ("seller", "vendor", "agency") or
account_name.
profile_from(profile_id, country_code)
¶
A client for a known profile without listing profiles first (saves a call).
amazon_ads.ProfileClient(account, profile)
¶
Everything scoped to one advertising profile (one account in one marketplace).
Attributes:
| Name | Type | Description |
|---|---|---|
sp |
SponsoredProducts
|
Sponsored Products campaigns, ad groups, keywords, targets, negatives, ads. |
sb |
SponsoredBrands
|
Sponsored Brands campaigns, keywords and negative keywords. |
portfolios |
SpResource[Any]
|
Portfolios. |
bids |
BidRecommendations
|
Suggested bids. |
history |
History
|
Change history. |
reports |
Reports
|
Async reports (reporting v3). |
request(method, path, *, json=None, params=None, content_type=None, accept=None, headers=None, idempotent=None)
¶
A raw call on this profile's regional host with the profile scope header.
call(operation, body=None, *, path_params=None, query=None, headers=None, content_type=None, accept=None)
¶
Send any operation from Amazon's published specs, by id.
us.call("sponsored-products:ListSponsoredProductsKeywords", ... {"campaignIdFilter": {"include": ["123"]}})
The method, path and vendor media types come from the operation catalog (see
:mod:amazon_ads.catalog), including its corrections where the live API refuses
what the spec documents. Returns the parsed JSON body, or None for an empty
response. 207 bodies are returned as-is; pass them to
:func:amazon_ads.batch.parse_multi_status for per-item results.
Raises :class:~amazon_ads.errors.UnsupportedOperationError, before sending
anything, for catalog entries this client cannot send, such as the mcp-* tools
of Amazon's hosted MCP server.
resource(kind)
¶
The resource behind a change plan's kind ("sp.keywords",
"sb.keywords", ...).
amazon_ads.Credentials(client_id, client_secret, refresh_token, token_url='https://api.amazon.com/auth/o2/token', consent_date=None)
dataclass
¶
What the client needs to authenticate.
consent_date is optional. When set, :attr:refresh_token_expires tells you when
the consent has to be repeated, and the CLI warns 30 days ahead.
from_env(prefix=ENV_PREFIX, *, env_file=None)
classmethod
¶
Read AMAZON_ADS_CLIENT_ID, _CLIENT_SECRET, _REFRESH_TOKEN and the
optional _TOKEN_URL and _CONSENT_DATE (YYYY-MM-DD).
env_file points at a dotenv file to read first. Variables already set in the
process environment win over the file.
Sponsored Products¶
amazon_ads.sp.resource.SpResource(client, spec)
¶
Bases: Generic[M]
iter(*, ids=None, campaign_ids=None, ad_group_ids=None, states=UNSET, name=None, extended=False, page_size=None, filters=None)
¶
Yield every matching entity, fetching further pages as needed.
states defaults to enabled and paused (every state for portfolios); pass
None for every state including archived. name is an exact-name match for
campaigns and ad groups. filters is merged into the request body for anything
not covered here.
list(**kwargs)
¶
Every matching entity as a list. Takes the same arguments as :meth:iter.
get(entity_id)
¶
One entity by id, in any state, or None if Amazon has no such entity.
get_many(ids)
¶
Entities by id, in any state, keyed by id. Missing ids are simply absent.
create(items)
¶
Create entities. Returns per-item results; never raises on item failures.
Call :meth:BatchResult.raise_for_errors to turn any rejected item into an
exception.
update(items)
¶
Update entities. Each item needs the id field plus the fields to change.
Fields Amazon does not allow changing (for example a keyword's text or match type) are dropped from the payload rather than sent and rejected.
delete(ids)
¶
Archive entities. Amazon has no hard delete; archived entities stay listable
with states=["ARCHIVED"] and cannot be re-enabled.
set_state(ids, state)
¶
Enable or pause entities. Use :meth:delete to archive.
plan_update(items, *, note=None)
¶
Build a reviewable :class:ChangePlan for an update without sending it.
Reads each entity's current values so the plan shows before and after for every
field and can be reversed. Items whose values already match are left out. Ids
Amazon does not return are reported in plan.missing.
Because the current values are known here, a field Amazon takes as an object
keeps the siblings the entity already has: raising a lifetime budget to 20 plans
{"budget": 20.0, "budgetType": "LIFETIME"}, not a silent switch to daily.
plan_create(items, *, note=None)
¶
A plan that creates entities. Its rollback, built after applying, archives the ids Amazon assigned.
Fields Amazon takes only as an object are put into that shape here as well, so the plan that is reviewed is the request that will be sent, and a bad value is refused while the plan is built rather than part way through applying it.
plan_archive(ids, *, note=None)
¶
A plan that archives entities. Amazon cannot re-enable an archived entity, so
this plan has no rollback; pausing (:meth:plan_update with state) does.
Batch results¶
amazon_ads.batch.BatchResult(successes=list(), errors=list())
dataclass
¶
Bases: Generic[T]
raise_for_errors()
¶
Raise :class:PartialFailureError if any item failed; return self otherwise.
amazon_ads.batch.parse_multi_status(body, items=None, *, entity_key=None, id_field=None, offset=0)
¶
Read a 207 body into a :class:BatchResult.
items is the list that was sent, so each result can be paired with its input.
offset is added to Amazon's indexes when this body answers one chunk of a larger
list. entity_key names the wrapper key ("keywords") and id_field the id
("keywordId"); both are inferred when omitted.
Change plans¶
amazon_ads.plans.ChangePlan(id, created_at, profile_id, country_code, changes, missing=list(), warnings=list(), note=None, reverses=None)
dataclass
¶
fingerprint
property
¶
SHA-256 over the profile and the changes, independent of id and timestamps.
table()
¶
One row per changed field: kind, id, label, field, before, after.
inverse()
¶
A plan that restores every before value.
A field that had no value before (for example a keyword bid left to the ad group
default) cannot be restored by an update, because Amazon's handling of an explicit
null is undocumented. Such fields are left out and listed in warnings.
apply(client, *, check_drift=False)
¶
Send the plan's updates through client.
check_drift=True first re-reads every entity and raises
:class:~amazon_ads.errors.PlanDriftError if any field no longer holds its before
value, so a plan reviewed an hour ago cannot silently overwrite a newer change.
grouped()
¶
Changes grouped by "<op> <kind>" in first-seen order, as they are sent.
save(path)
¶
Write the plan as JSON. A directory (existing, or a path ending in a slash or
without a file extension) gets plan-<id>.json inside it.
amazon_ads.plans.PlanResult(plan, results)
dataclass
¶
Per-operation batch results of :meth:ChangePlan.apply, keyed "<op> <kind>".
rollback_plan()
¶
A plan that undoes exactly what Amazon accepted.
Updates that succeeded are reversed to their before values; creates that succeeded
are archived by the ids Amazon assigned; failed items are left out because they
changed nothing. Archives cannot be reversed and are listed in warnings.
Suggested bids¶
amazon_ads.bids.BidRecommendations(client)
¶
for_ad_group(campaign_id, ad_group_id, expressions, *, theme=DEFAULT_THEME)
¶
Suggested bids for expressions in an existing ad group, in request order.
for_ad_group_all_themes(campaign_id, ad_group_id, expressions)
¶
Suggestions for every theme Amazon returned, keyed by theme.
for_new_ad_group(asins, expressions, *, strategy='MANUAL', placement_adjustments=None, theme=DEFAULT_THEME)
¶
Suggested bids before an ad group exists, for the ASINs it would advertise.
placement_adjustments maps PLACEMENT_TOP / PLACEMENT_PRODUCT_PAGE /
PLACEMENT_REST_OF_SEARCH to a percentage.
for_keywords(keywords, *, theme=DEFAULT_THEME)
¶
Suggestions for existing keywords, keyed by keyword id. Keywords are grouped by ad group, since recommendations are asked per ad group.
for_targets(targets, *, theme=DEFAULT_THEME)
¶
Suggestions for existing product and auto targets, in input order.
Targets Amazon offers no recommendation for (ASIN_EXPANDED_FROM and the like)
are returned in unsupported instead of failing the request.
Change history¶
amazon_ads.history.History(client)
¶
events(*, since=None, until=None, entity_types=ENTITY_TYPES, entity_ids=None, campaign_ids=None, changes=None, newest_first=True)
¶
Yield change events, paging through Amazon's results.
since defaults to 90 days ago (Amazon's limit) and until to now.
entity_ids narrows to specific keyword, target, ad group or campaign ids;
campaign_ids narrows to children of those campaigns. changes filters by
change type, e.g. BID_AMOUNT, BUDGET_AMOUNT, STATUS, CREATED,
PLACEMENT_GROUP, PORTFOLIO_ID, NAME, START_DATE, END_DATE.
list(**kwargs)
¶
:meth:events as a list.
value_at(entity_type, entity_id, change, at, *, events=None)
¶
What change (e.g. "BID_AMOUNT") held on an entity at moment at.
Returns Amazon's string value, or None when no change to that field falls
in the history window, which means the value then was the value now.
rollback_plan(since, *, entity_types=('KEYWORD', 'PRODUCT_TARGETING', 'AD_GROUP', 'CAMPAIGN'), changes=('BID_AMOUNT', 'STATUS'), campaign_ids=None, note=None)
¶
A plan restoring every bid and state changed since since to its value at
that moment, from Amazon's change history.
Entities changed several times are restored to the value before the first
change. The plan's before values are the values after the latest change; use
apply(check_drift=True) to catch anything changed after the plan was built.
Change types without a known update field (budget amounts, names, dates) are
listed in warnings.
Reports¶
amazon_ads.reports.Reports(client, *, sleep=time.sleep, monotonic=time.monotonic)
¶
run(report, start, end, *, columns=None, filters=None, time_unit='DAILY', timeout=3600.0, name=None)
¶
Request, wait for and download a report over any date range.
report is a preset name (see :data:PRESETS) or a full Amazon
configuration dict. With time_unit="SUMMARY" rows are totals per chunk,
so ranges over 31 days yield one total per chunk rather than one overall.
create(configuration, start, end, *, name=None)
¶
Queue a report and return its id. A duplicate of a report still being built returns that report's id instead of an error.
wait(report_id, *, timeout=3600.0, poll=5.0, max_poll=60.0)
¶
Poll until the report completes. Raises :class:ReportFailedError on
FAILED or timeout. Polling backs off from poll to max_poll seconds.
download(status)
¶
Download a completed report's rows. Accepts a status or a report id. The pre-signed URL expires (an hour by default), so a stale status is re-fetched.
delete(report_id)
¶
Cancel a pending report.
Warehouse¶
amazon_ads.warehouse.Warehouse(path)
¶
sync(client, reports=('sp_placement', 'sp_targeting', 'sp_search_terms'), *, restate_days=DEFAULT_RESTATE_DAYS, start=None, end=None)
¶
Bring the store up to date for one profile.
Without start each report resumes restate_days before its newest stored
day, or reaches back as far as Amazon retains on the first run. end defaults
to yesterday, the last complete day.
replace(profile_id, country_code, report, start, end, rows, *, report_ids=())
¶
Replace every stored row of report dated start..end with rows,
in one transaction.
rows(report, *, country_code=None, profile_id=None, start=None, end=None)
¶
Stored rows as dicts, oldest first, each with country_code added.
query(sql, params=())
¶
Run a read-only SQL query and return rows as dicts.
coverage()
¶
Per market and report: first and last stored day, row count, last pull.
Catalog¶
amazon_ads.catalog
¶
The operation catalog: every operation in every Ads API spec Amazon publishes.
Generated by scripts/generate_catalog.py from Amazon's OpenAPI specs. Each entry
records the HTTP method, path template, parameters and the vendor media types Amazon
expects, which is what :meth:ProfileClient.call needs to send any operation correctly.
Two things are applied on top of the generated file when it loads, so regenerating it from Amazon's specs cannot undo them:
- :data:
CORRECTIONS: media types the live API needs where the spec says otherwise. The corrected :attr:Operation.acceptis what gets sent, :attr:Operation.notesays why, and :attr:Operation.acceptsstill lists what the spec documents. - Operations this client cannot send carry :attr:
Operation.unsupported_reason. Today that is every tool of Amazon's hosted Ads MCP server, which Amazon publishes as OpenAPI files beside the Ads API specs. :func:searchleaves them out and :meth:ProfileClient.callrefuses them.
Look operations up by full id ("sponsored-products:ListSponsoredProductsKeywords"), by
bare operation id when it is unique across APIs, or search with :func:search.
Operation(id, api, operation_id, method, path, summary, tags, deprecated, path_params, query, headers, content_type, accept, content_types, accepts, has_body, note=None, unsupported_reason=None)
dataclass
¶
note = None
class-attribute
instance-attribute
¶
Why this entry differs from Amazon's spec, when a :data:CORRECTIONS entry applies.
unsupported_reason = None
class-attribute
instance-attribute
¶
Why this client cannot send the operation, or None when it can.
read_only
property
¶
True for operations that change nothing: GETs, and POSTs whose path or id
says they list, query or recommend (see READ_WORDS) and nothing says they
create, update, delete or archive (see WRITE_WORDS).
The MCP server's call_operation refuses every operation for which this is
False, adsctl ops call asks before sending one, and
:meth:ProfileClient.call retries an operation after a 5xx or timeout only
when this is True.
ensure_sendable()
¶
Raise :class:UnsupportedOperationError if this client cannot send it.
format_path(path_params=None)
¶
The path with every {name} placeholder replaced by its value.
Each value fills exactly one path segment and reaches Amazon as given; see
:func:path_segment for the values refused with ValueError. Placeholders are
read from the path template itself rather than from :attr:path_params, so one
that a spec forgot to declare is still required and checked. Names that match no
placeholder are ignored.
Correction(accept, note)
dataclass
¶
The Accept type to send where the one Amazon's spec documents is refused live.
path_segment(operation_id, name, value)
¶
value percent-encoded as one path segment, or ValueError naming name.
:meth:Operation.format_path fills every path parameter through this, and code that
builds a path by hand, such as a raw :meth:ProfileClient.request call, can use it
the same way. operation_id and name only label the error.
The value must be a string or an integer. It is refused, rather than encoded into
something harmless, when it is percent-encoded, contains "/", "\", "?", "#" or a
control character, or is empty or only dots and spaces (before its first ";" too):
a value like that is a mistake or an attack, never an id. Unchecked,
{"brandEntityId": "../sp/targets?x="} would turn
POST /stores/{brandEntityId}/insights into POST /sp/targets, a write sent as
though it were the read that the caller, or the MCP server's read-only guard,
approved.
is_hosted_mcp_tool(source)
¶
True when an API's spec (by its source URL) lists tools of Amazon's hosted Ads MCP
server rather than Ads API endpoints. Those specs name no server and document no
responses, and every operation in them is POST /mcp/<tool_name>, a path the Ads
API hosts do not serve.
get(operation)
¶
Find an operation by "api:operationId", or by bare operation id if unique.
Operations this client cannot send are returned too, so their
:attr:~Operation.unsupported_reason can explain why.
search(text, *, api=None, limit=25, include_unsupported=False)
¶
Operations whose id, path, summary or tags contain every word of text.
Operations this client cannot send are left out unless include_unsupported is
true or api names their API, so a search scoped to one of them returns them, each
with its :attr:~Operation.unsupported_reason, instead of nothing.
Errors¶
amazon_ads.errors
¶
Exceptions raised by the client.
Every HTTP failure becomes an :class:ApiError subclass carrying the status code, Amazon's
error code and message, and the x-amz-request-id Amazon support asks for when a call
misbehaves.
AmazonAdsError
¶
Bases: Exception
Base class for everything this library raises.
ConfigurationError
¶
Bases: AmazonAdsError
Credentials or settings are missing or inconsistent.
AuthError(message, *, error=None)
¶
Bases: AmazonAdsError
Login with Amazon refused to issue an access token.
invalid_grant usually means the refresh token expired (365 days after consent for
tokens issued since mid 2026) or the client secret was reset, which revokes every
refresh token issued under it.
ApiError(message, *, status_code, code=None, request_id=None, body=None, method=None, url=None)
¶
BadRequestError(message, *, status_code, code=None, request_id=None, body=None, method=None, url=None)
¶
UnauthorizedError(message, *, status_code, code=None, request_id=None, body=None, method=None, url=None)
¶
ForbiddenError(message, *, status_code, code=None, request_id=None, body=None, method=None, url=None)
¶
NotFoundError(message, *, status_code, code=None, request_id=None, body=None, method=None, url=None)
¶
UnprocessableError(message, *, status_code, code=None, request_id=None, body=None, method=None, url=None)
¶
Bases: ApiError
422: the request was well formed but Amazon will not act on it.
Amazon also uses 422 for unsupported combinations, for example a bid recommendation request that mixes keyword and product (ASIN) targeting expressions.
ThrottledError(*args, retry_after=None, **kwargs)
¶
ServerError(message, *, status_code, code=None, request_id=None, body=None, method=None, url=None)
¶
PartialFailureError(result)
¶
Bases: AmazonAdsError
A batch write where Amazon rejected some or all items.
Amazon answers batch writes with 207 Multi-Status: items succeed or fail one by one,
and the call as a whole still returns 207. result holds both lists, so the items
that did go through are never lost.
ReportFailedError(message, *, report_id, status=None)
¶
PlanDriftError(message, *, drifted)
¶
Bases: AmazonAdsError
The account no longer holds the values a change plan was built against.
Raised by :meth:ChangePlan.apply with check_drift=True so a stale plan cannot
overwrite a change someone made after the plan was reviewed.
UnsupportedOperationError
¶
Bases: AmazonAdsError
The operation catalog lists this operation, but this client cannot send it.
Raised before any request is made. Today this covers the tools of Amazon's hosted Ads
MCP server: Amazon publishes them as OpenAPI files beside the Ads API specs, but their
POST /mcp/<tool> paths are not routes on the Ads API hosts. The message says why
and, where one is known, which Ads API operation to call instead.