Run everything you never had time to implement. Define your process once, and keep it running with Qore.

All reports
Amazon Ads Console
Search Terms & Keywords

Amazon Sponsored Products Search Term Report (Manual Keywords)

The Sponsored Products search term report for manual targeting gives you one row per manual keyword per shopper search term per day — the query a shopper actually typed, the broad, phrase or exact keyword that caught it, and the impressions, clicks, cost and attributed sales that followed. It is the file keyword harvesting and negative-keyword lists are built from, and the column most people misread is search_term, which equals the keyword on about a quarter of rows and not on the rest.

Known upstream as
spSearchTerm
One row is
One row per search term
Refreshed
Daily
Columns
53
Marketplaces
All marketplaces
History
Amazon retains 65 days of Sponsored Products search term data and caps one request at a 31-day date range, so a full window takes three requests and anything older has to be accumulated by pulling on a schedule. The console's own search term report states the same 65-day lookback. It is shorter than the 95 days the Sponsored Products campaign and targeting reports get — retention is set per report type, not per ad product.
Latency
Impression and click data is available within 12 hours and conversion data within 24 hours, on top of up to three hours for the report itself to generate. Clicks and impressions can still change for up to three days while invalid traffic is filtered, and Amazon recommends waiting at least five days for accurate click data. Rows are dated by advertising day, and conversions are restated 1, 7 and 28 days after the conversion event, so with attribution windows of up to 30 days here a recent day keeps moving for weeks after it is first published.
Requires
Amazon Ads API access

What this report contains

One row per manual keyword per shopper search term per day, for Sponsored Products. The unique key is (keyword_id, search_term, date) — in 32,267 production rows there were 32,267 distinct keys — so the same search term appears on several rows for the same day when several of your keywords caught it, and the same keyword appears on as many rows as there were distinct queries.

"Manual" means the request is the standard spSearchTerm report filtered to keyword types BROAD, PHRASE and EXACT: the keywords an advertiser typed in, not the targeting expressions of an auto campaign. Auto campaigns' search terms are the same 53 columns under a different filter, and a separate report.

The left of the row is who matched what: search_term is the query the shopper typed, and keyword, keyword_type and keyword_bid are the keyword that won the impression, with campaign, ad group and portfolio context. Two pairs here are duplicates by construction. keyword equals targeting on every row, and keyword_type equals match_type on every row — Amazon carries both names, so both are here, but they are not two dimensions. That is the opposite of the auto report, where keyword and targeting diverge because a category target carries an id in one and a name in the other.

search_term is not one of those duplicates. It equals keyword on 27.1% of rows — an exact-match keyword the shopper typed verbatim — and differs on the rest, where a broad or phrase keyword caught a longer or reordered query. Both cases are real data.

The middle is delivery: impressions, clicks, click_through_rate, cost, cost_per_click. The right is attribution, repeated per window. Purchases, units and sales each come at 1, 7, 14 and 30 days, with same-SKU subsets at every window; the other-SKU columns come at 7 days only; ACOS and ROAS come at 7 and 14 days only. The windows nest — sales_30d contains sales_7d — so they are never summed.

How to get it

Through the Amazon Ads API v3, you request a report with reportTypeId: spSearchTerm, adProduct: SPONSORED_PRODUCTS, groupBy: ["searchTerm"], timeUnit: DAILY and format: GZIP_JSON, a keywordType filter restricted to BROAD, PHRASE and EXACT, and the full list of 53 columns. The request goes to the /reporting/reports endpoint; Amazon returns a report id you poll until it is complete, then download.

The trap is the column list. v3 returns exactly the columns you name and nothing else, so a column that is missing from your file was missing from your request, not from Amazon's schema. Requesting all 53 columns on this page returns all 53: every one arrived on every one of 14 production files checked, and the sparse ones are present-and-null, never absent.

The response is a JSON array of flat objects with Amazon's camelCase keys. Two things about it are worth knowing before you load it. Ids (keywordId, campaignId, adGroupId, portfolioId) arrive as bare numbers of 11 to 15 digits and want to be strings. And if you let a loader infer types, it will infer differently for every file: money and ratio columns flip between integer and double according to whether that advertiser happened to have a fractional value that week, the ACOS columns come out as an all-null type for an advertiser with no sales, and the column order follows whatever order the JSON happened to carry. Pin a schema.

One request covers one profile, so an advertiser in five marketplaces makes five requests and gets five files, each with its own campaign_budget_currency_code. One request also covers the whole window you asked for as a single file; date is the column you split it on.

In the Amazon Ads console, the equivalent is Measurement & Reporting → Sponsored ads reports, category Sponsored Products, report type Search term, daily time unit. The console report is not filtered to manual match types, so it carries the auto-targeting rows as well. Treat that path as dated: Amazon has said it is sunsetting Sponsored Ads reports by 31 December 2026 in favour of unified reporting at Measurement and Reporting → Reporting. The API request above is the route that outlasts the move.

Sample rows

Illustrative values, not data from a real account. Shown to give the shape of the file.

datekeywordmatch_typesearch_termimpressionsclickscostpurchases_7dsales_7dacos_clicks_7d
2026-09-14stainless steel water bottleEXACTstainless steel water bottle840012694.509269.9135.01
2026-09-14water bottleBROADinsulated water bottle 32 oz52006139.654119.9633.05
2026-09-14water bottleBROADkids water bottle with straw21501711.9000.00
2026-09-14insulated bottlePHRASEbest insulated bottle for hiking13002219.80259.9833.01
2026-09-14insulated bottlePHRASEinsulated bottle 40 oz96098.10129.9927.01

Field reference

ColumnTypeDescription
datedateThe advertising day the row's metrics are attributed to, as a plain ISO date with no time component. It is the column a multi-day file is split and filtered on.
campaign_idstringAmazon's numeric campaign identifier. It arrives as an 11 to 15 digit JSON number and is carried as a string because it is a join key and a numeric type would silently drop a leading zero.
campaign_namestringThe campaign name as it stood when the report ran. Join on campaign_id, not on this.
campaign_statusstringWhether the campaign is enabled, paused or archived, as of report time.
campaign_budget_amountdecimalThe campaign's configured budget, in campaign_budget_currency_code.
campaign_budget_typestringWhether campaign_budget_amount is a daily figure or a lifetime total.
campaign_budget_currency_codestringThe currency of campaign_budget_amount. Populated on every row, and in every file observed it agreed with the marketplace of the credential that pulled it (BRL, CAD, EUR, GBP, MXN, USD). Kept under its own name because it is narrower than a row-level currency code.
ad_group_idstringThe ad group the keyword belongs to. A numeric identifier carried as a string, like every other id in the row.
ad_group_namestringThe ad group's name as of report time.
portfolio_idstringThe portfolio the campaign sits in. Null on 43.6% of rows observed — campaigns in no portfolio — which is a fact about the account, not a gap in the file.
keyword_idstringThe manual keyword's identifier and one third of the row's unique key, with search_term and date. A numeric id carried as a string.
keywordstringThe manual keyword text the advertiser chose. Equal to targeting on every row observed, and equal to search_term on about 27% of them — an exact-match keyword the shopper typed verbatim.
targetingstringAmazon's generic targeting expression column. For manual keywords it is the same string as keyword on every row; both are carried because both are in Amazon's vocabulary.
keyword_typestringBROAD, PHRASE or EXACT — the manual vocabulary this report is filtered to. Equal to match_type on every row observed.
match_typestringThe keyword's match type, BROAD, PHRASE or EXACT. Identical to keyword_type on every row.
keyword_biddecimalThe bid set on the keyword, in the campaign's currency. Null on 1.4% of rows, where the campaign's bidding strategy leaves no per-keyword bid.
ad_keyword_statusstringENABLED, PAUSED or ARCHIVED. Archived keywords still appear when they had traffic on the day.
search_termstringThe query the shopper actually typed. Free text, never null in the files observed, and equal to keyword on 27.1% of rows and different on the rest. Neither column is a proxy for the other. Not every value is necessarily typed text — Amazon states that where a placement has no search keyword associated with it on a product detail page, the search term is reported as an asterisk *. No asterisk appeared in the files measured for this page, so check for it before treating the column as pure query text.
impressionsint64Times an ad was shown for this keyword against this search term on the day.
clicksint64Clicks on those impressions. The billable event.
click_through_ratefloat64Clicks divided by impressions, expressed as a percentage rather than a 0 to 1 fraction. Null on 0.1% of rows. Recompute from the counts when aggregating.
costdecimalWhat the clicks cost. The only spend column in this report — unlike the campaign report, there is no spend twin to reconcile.
cost_per_clickdecimalAverage cost of a click on the row. Money, not a rate — it multiplies back into cost — so recompute from cost and clicks rather than averaging it.
purchases_1dint64Orders attributed to a click within 1 day.
purchases_7dint64Orders attributed within 7 days. Includes everything in purchases_1d.
purchases_14dint64Orders attributed within 14 days.
purchases_30dint64Orders attributed within 30 days — the widest window and the slowest to settle.
purchases_same_sku_1dint64The subset of purchases_1d that were the advertised SKU itself.
purchases_same_sku_7dint64Same-SKU subset of purchases_7d.
purchases_same_sku_14dint64Same-SKU subset of purchases_14d.
purchases_same_sku_30dint64Same-SKU subset of purchases_30d.
units_sold_clicks_1dint64Units in click-attributed orders within 1 day. Counts units where purchases_1d counts orders.
units_sold_clicks_7dint64Units in click-attributed orders within 7 days.
units_sold_clicks_14dint64Units in click-attributed orders within 14 days.
units_sold_clicks_30dint64Units in click-attributed orders within 30 days.
units_sold_same_sku_1dint64Units of the advertised SKU specifically, within 1 day.
units_sold_same_sku_7dint64Units of the advertised SKU specifically, within 7 days.
units_sold_same_sku_14dint64Units of the advertised SKU specifically, within 14 days.
units_sold_same_sku_30dint64Units of the advertised SKU specifically, within 30 days.
units_sold_other_sku_7dint64Units of products other than the advertised SKU, within 7 days — the halo. Only the 7-day window is requested for this column.
sales_1ddecimalAttributed sales value within 1 day.
sales_7ddecimalAttributed sales value within 7 days. The denominator of acos_clicks_7d.
sales_14ddecimalAttributed sales value within 14 days. The denominator of acos_clicks_14d.
sales_30ddecimalAttributed sales value within 30 days. There is no 30-day ACOS column, so divide cost by this yourself.
attributed_sales_same_sku_1ddecimalThe portion of sales_1d from the advertised SKU itself.
attributed_sales_same_sku_7ddecimalSame-SKU portion of sales_7d.
attributed_sales_same_sku_14ddecimalSame-SKU portion of sales_14d.
attributed_sales_same_sku_30ddecimalSame-SKU portion of sales_30d.
sales_other_sku_7ddecimalSales of products other than the advertised SKU within 7 days — the halo in money. Only the 7-day window is requested.
acos_clicks_7dfloat64Cost divided by sales_7d, as a percentage (values above 300 occur). Null, not zero, on 86.1% of rows observed — every row with spend and no attributed sales. Divide the two money columns yourself when you need exactness or a zero-sales row to count.
acos_clicks_14dfloat64Cost divided by sales_14d, as a percentage. Null wherever there were no 14-day sales.
roas_clicks_7dfloat64Sales at 7 days divided by cost, as a bare multiple rather than a percentage. Null on 0.1% of rows.
roas_clicks_14dfloat64Sales at 14 days divided by cost, as a bare multiple.

Use cases

Harvesting search terms into exact-match keywords. Filter to rows where search_term differs from keyword, keyword_type is BROAD or PHRASE, and purchases_7d is above your threshold. Each one is a query a shopper converted on that you are only reaching by approximation; adding it as an EXACT keyword lets you bid on it directly. sales_7d against cost tells you which are worth it.

Building a negative-keyword list. Rows with clicks and cost but zero purchases_14d are the queries you are paying for and not converting. Because ACOS is null rather than infinite on these rows, you find them by filtering the counts, not by sorting acos_clicks_14d — sorting on ACOS puts the worst rows at the bottom, where nulls go.

Comparing match types on the same query. The grain puts the same search_term on one row per keyword that caught it, so a query reached by both a BROAD and an EXACT keyword on the same date shows up twice. Grouping by search_term and comparing cost_per_click and sales_7d across match_type is a direct read on whether the broad keyword is doing anything the exact one is not.

Auditing bids against what clicks actually cost. keyword_bid and cost_per_click sit on the same row. A keyword whose CPC runs close to its bid every day is being priced by that bid; one whose CPC is far below it has headroom that the bid is not using.

Measuring the halo on a keyword. sales_other_sku_7d against attributed_sales_same_sku_7d is how much a search term sells of things you did not advertise on it. A keyword that looks poor on same-SKU ACOS and fine on total ACOS is a keyword that introduces shoppers to the catalogue.

Reporting spend by portfolio. portfolio_id is on the row, so spend and sales roll up to portfolio without a join — but it is null for 43.6% of rows, because campaigns in no portfolio have no portfolio, and those rows must be kept as their own bucket rather than dropped.

Limitations and gotchas

The row is a keyword crossed with a search term, not a search term. The unique key is (keyword_id, search_term, date), so the same query appears once for every manual keyword that caught it that day. Totals across the whole file are still right — each row's spend is its own — but anything keyed on search_term alone is not. Counting rows to count queries, or joining this file to another table on search_term, multiplies by however many keywords matched. Group by search_term first, then join.

Amazon says every row had a click. The search term report reference states that search term reports only include impressions that resulted in at least one ad click, so a query that was shown and never clicked is not in this file and cannot be found by filtering it. Every production row observed did carry spend, which is consistent, but that is not a test of the claim — treat it as Amazon's statement, not as something measured here.

Attribution windows nest — never add them. purchases_30d contains purchases_14d contains purchases_7d contains purchases_1d, and the same for units and sales. Summing across windows multiplies revenue roughly fourfold.

ACOS is a percentage, ROAS is a multiple, CTR is a percentage. None of them is a 0 to 1 fraction. Values observed run up to 342.8 for acos_clicks_7d, 4,952 for ROAS and 200.0 for click_through_rate. A pipeline that divides by 100 or multiplies by 100 on assumption gets it wrong by two orders of magnitude.

ACOS is null on most rows. acos_clicks_7d and acos_clicks_14d were null on 86.1% of production rows — every row with spend and no attributed sales. An average of the column ignores precisely the rows that spent money for nothing. Compute cost ÷ sales_7d from the two money columns yourself; they are carried at full precision and the ratio is not.

ACOS and ROAS exist only at 7 and 14 days; the other-SKU columns only at 7. There is no acos_clicks_30d or sales_other_sku_30d. Anything at another window is a division you do.

keyword and targeting are the same column; so are keyword_type and match_type. Both pairs were equal on all 32,267 rows. Pick one of each and do not treat them as separate facets.

search_term equal to keyword is not a defect. It happens on about a quarter of rows and means an exact-match keyword was typed verbatim. Filtering those rows out as "no search term data" removes your best-performing queries.

Ids are numbers in the JSON and must be strings in your table. keyword_id, campaign_id, ad_group_id and portfolio_id are 11 to 15 digit integers on the wire. Loaded as numbers they lose leading zeros silently and, in some tools, precision.

Nulls in portfolio_id and keyword_bid are structural. A campaign in no portfolio has no portfolio_id (43.6% of rows); a keyword under a bidding strategy with no per-keyword bid has no keyword_bid (1.4%). Neither is a missing-data problem.

There is no spend column. Unlike the campaign and advertised-product reports, this one carries cost alone. A join that expects spend finds nothing.

Don't average the rate columns. click_through_rate, cost_per_click, ACOS and ROAS are computed per row. Recompute from impressions, clicks, cost and sales_* when aggregating.

Names are as-of-now. campaign_name and ad_group_name reflect the current names, not the names on the row's date. Join on ids.

Auto campaigns are a separate report, and so are Sponsored Brands and Sponsored Display. This file is Sponsored Products, manual keywords only. A complete search term picture needs at least the auto report alongside it.

FAQ

What is the difference between the manual and auto search term reports?

They are the same spSearchTerm report with the same 53 columns, and differ only in the keyword type filter on the request. The manual report is filtered to BROAD, PHRASE and EXACT keywords; the auto report carries the targeting expressions of auto campaigns instead.

Why is search_term sometimes identical to keyword?

Because an exact-match keyword the shopper typed verbatim produces the same string in both columns. It happened on about 27 percent of production rows, and those rows are real, converting traffic, not a placeholder.

Why is ACOS blank on most rows?

ACOS is null wherever there were no attributed sales in the window, which was 86 percent of rows in production. It is not zero and it is not infinite. To find rows that spent and did not sell, filter on cost and purchases rather than on ACOS.

Is ACOS a percentage or a fraction?

A percentage. A row with ACOS 35.0 spent 35 cents for every dollar of attributed sales. ROAS is a bare multiple and click-through rate is also a percentage, so none of the three is on a 0 to 1 scale.

What exactly is one row?

One manual keyword, one shopper search term, one advertising day. The combination of keyword_id, search_term and date is unique, so a search term caught by three of your keywords on the same day appears three times.

Does this report include Sponsored Brands or auto-targeting search terms?

No. It is Sponsored Products only, and only the BROAD, PHRASE and EXACT keywords an advertiser chose. Auto-targeting search terms, Sponsored Brands and Sponsored Display each come from their own report.

Sources

Every researched claim on this page, and the Amazon or Walmart page it came from.

  • marketplaces — advertising.amazon.com/faq — under 'In what regions will the updated API be available?': 'The reporting updates are available worldwide (NA, EU, FE) wherever you can run sponsored ads.'
  • marketplaces — advertising.amazon.com/features — the Campaign Management section names 22 marketplaces (US, CA, MX, BR, DE, ES, FR, IT, UK, NL, SE, TR, PL, BE, UAE, EG, SA, JP, IN, AU, SG, ZA) and does not name Ireland. Deliberately not used as the basis for an enumeration: the sibling Sponsored Products advertised-product page records that report being pulled from an IE profile, so this list is demonstrably incomplete, and enumerating from it would tell a reader the report is unavailable in a marketplace production files exist for. The 14 production files for this report came from US, CA, MX, BR, UK, FR and IT, which is where a pull was run rather than the limit of the report.
  • marketplaces — advertising.amazon.com/G3HEFZYWZF84NPS9 — Amazon's console help scopes the search term report by account type — 'They are available for sellers, vendors, authors, and book vendors running Sponsored Brands and Sponsored Products campaigns' — and names no marketplace restriction. Nothing on Amazon's own domains narrows this report type to a marketplace list, so `all` follows the v3 reporting FAQ's worldwide statement and the enumeration on the features page was deliberately not used. Confirmed on 2026-09-28; re-check before a person marks the page ready.
  • history_window — advertising.amazon.com/search-term — the search term report configuration table gives `spSearchTerm` a data retention of 65 days and a maximum date range of 31 days. The 60-day figure in that table belongs to `sbSearchTerm`, not to Sponsored Products.
  • history_window — advertising.amazon.com/G3HEFZYWZF84NPS9 — Amazon's console help for the Sponsored Products search term report: 'The search term report has 2 time units available: summary or daily. The lookback window for the search term report is 65 days.'
  • history_window — advertising.amazon.com/campaign and .../report-types/targeting — checked to make sure 65 was not the Sponsored Brands or Display column bleeding across: both of those tables do give Sponsored Products 95 days with 60 for Sponsored Brands and 65 for Sponsored Display, so the 65 days on the search term table is genuinely this report type's.
  • latency — advertising.amazon.com/faq — 'Initial impression and click data is available using the API within 12 hours', changes 'may happen up to three days after the initial click date due to the traffic validation process', 'Initial conversion data is available within 24 hours', and 'Restatements of conversion data occur 1, 7, and 28 days after the conversion event.' Also: 'Report generation can take as long as three hours.'
  • latency — advertising.amazon.com/overview — invalid-traffic detection 'monitors the traffic data for up to 30 days after the fact', Amazon recommends 'waiting at least 5 days for the most accurate click data', and conversion data 'is subject to change for up to 60 days back from the current date'.
  • grain — advertising.amazon.com/search-term — the `spSearchTerm` configuration table offers `searchTerm` as its only `groupBy`, so the single taxonomy term `search-term` is Amazon's own vocabulary for this report and is kept. The unique key measured in production files is (keyword_id, search_term, date), so a search term appears once per manual keyword that caught it on a day; per the directory decision to keep the closest single term, that second axis is carried in the summary, the overview and the limitations rather than in a compound grain term.
  • row population — advertising.amazon.com/search-term — 'Note that search term reports only include impressions that resulted in at least one ad click', and 'If a placement does not have a search keyword associated with it on a product detail page, the search term in the report will be an asterisk `*`.' Neither is contradicted by the production files — every row observed carried spend and `search_term` was never null — but neither was tested for, so both are stated on the page as Amazon's claim rather than as a measurement. The v3 column reference (advertising.amazon.com/columns) defines `searchTerm` only as 'The search term used by the customer. Same as query.'
  • console vs API population — two Amazon pages disagree, and the page follows neither. — advertising.amazon.com/faq says 'The search term report in the advertising console contains all search terms that had an impression, regardless of if that term resulted in a click. The API search term report only contains impressions that also had at least one click.' advertising.amazon.com/G3HEFZYWZF84NPS9, Amazon's help article for that same console report, says 'The search term report includes only the search terms that resulted in at least 1 ad click, so the impressions in the report may not match the impressions in the campaign manager.' They agree only about the API report, which is click-only and is what this page describes; the page makes no claim about how many rows the console download carries.
  • how-to-get-it (console sunset) — advertising.amazon.com/streamline-campaign-analysis-with-unified-reporting — Amazon's 8 June 2026 unified reporting launch announcement: 'we're sunsetting Sponsored Ads reports (Measurement and reporting > Sponsored Ads reports) and Amazon DSP reports (Measurement and reporting > Amazon DSP reports) by December 31, 2026', with the replacement at 'Measurement and Reporting > Reporting'. The console path on this page is therefore dated, and the page says so.
  • how-to-get-it — advertising.amazon.com/get-started — 'All reports requests use a single endpoint: POST /reporting/reports'; you then poll GET /reporting/reports/{reportId} with the returned `reportId` until `status` is COMPLETED and a `url` appears, then download.
  • how-to-get-it — advertising.amazon.com/search-term — Amazon's own 'Sponsored Products: Keywords only' sample is reportTypeId `spSearchTerm`, adProduct SPONSORED_PRODUCTS, groupBy ['searchTerm'], format GZIP_JSON with a `keywordType` filter of BROAD, PHRASE and EXACT; the sample above it is the same request filtered to TARGETING_EXPRESSION and TARGETING_EXPRESSION_PREDEFINED instead. `timeUnit` may be SUMMARY or DAILY.
  • how-to-get-it — advertising.amazon.com/advertising-console — 'To access your reports, click Measurement & Reporting in the sidebar, then click through to Sponsored ads reports. From here, click on Create report to set up your new custom report.' The same guide names the Search term report among the types you can choose.
  • how-to-get-it — advertising.amazon.com/GBYSPTSLR337JMLH — the downloadable-reports table ticks Search term for both Sponsored Products and Sponsored Brands, so Search term is the console's own name for this report under Sponsored Products. In the same table the Keyword report is ticked for Sponsored Brands only, and Targeting for Sponsored Products and Display.
  • how-to-get-it — advertising.amazon.com/G3HEFZYWZF84NPS9 — the console report is not restricted to manual keywords: it notes that ASIN-style entries appear in the customer search term column for Sponsored Products automatic-targeted campaigns and product-attribute-targeted ad groups.