Developers

API endpoint reference

Generated from the API contract itself: every endpoint, every field, every code.

Every endpoint MerchDash’s public API answers, with its parameters, its response fields, and what it refuses. This page is generated from the API’s own contract, so it cannot drift from what the server actually does. For how to get a key and how the review model works, start at Public API.

All endpoints sit under https://app.merchdash.net/api/v1/ and take Authorization: Bearer md_live_…. Collections answer { data, nextCursor }; single resources answer { data }. Reads are served from MerchDash’s synced archive rather than live from Amazon.

Limits

LimitValue
Rows per page, default / maximum100 / 500
Metric window, default / maximum30 days ending 2 days ago / 365 days
Changes in one POST /api/v1/changes500
Campaigns + ad groups in one POST /api/v1/structures2000
ASINs + keywords + targets in one POST /api/v1/structures20000
Request body, POST /api/v1/changes and /structures24 MB — every other endpoint accepts 1 MB
Un-answered proposals held per marketplace25

Rate limits are per key and per account, and are described on the Public API page.

Every endpoint

EndpointScopeWhat it answers
GET /api/v1/profilesreadEvery marketplace this account owns and has synced.
GET /api/v1/campaignsreadCampaigns with their metrics for a window.
GET /api/v1/ad-groupsreadAd groups with their metrics for a window.
GET /api/v1/keywordsreadKeywords with their metrics for a window.
GET /api/v1/targetsreadTargeting clauses with their metrics for a window.
GET /api/v1/asinsreadAdvertising performance aggregated per ASIN.
GET /api/v1/search-termsreadShopper search terms with their metrics for a window.
GET /api/v1/metrics/dailyreadAccount totals for each day in a window.
GET /api/v1/sync/statusreadHow current the synced archive is for a marketplace.
GET /api/v1/runsreadAutomation runs for a marketplace, newest activity first.
GET /api/v1/runs/:runIdreadOne run — the polling target for the runId a proposal returned.
GET /api/v1/runs/:runId/changesreadThe individual changes a run proposed, and what became of each.
POST /api/v1/changeswritePropose a batch of bid, state and negative changes — for a human to approve, or to apply at once.
POST /api/v1/structureswritePropose new campaigns, ad groups, product ads, keywords and targeting clauses for a human to approve.

GET /api/v1/profiles

Every marketplace this account owns and has synced.

Scope: read

Not "the marketplaces currently ticked in the switcher" — the switcher is a UI preference. This is every advertising profile the account owns and has synced at least once, which is exactly the set of profileIds the other endpoints accept.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: profileId ascending

FieldTypeMeaning
profileIdstringThe Amazon advertising profile id.
accountNamestring
nullable
Advertiser account name from the last Amazon discovery. Null if MerchDash has not discovered it.
countryCodestring
nullable
ISO country of the marketplace, when known.
marketplaceIdstring
nullable
Amazon marketplace id, when known.
regionstring
nullable
Amazon Ads API region (NA / EU / FE) this profile is read through.
lastSyncAttimestamp
nullable
When a sync last completed for this marketplace. Null until the first one finishes.

GET /api/v1/campaigns

Campaigns with their metrics for a window.

Scope: read

One row per campaign in the marketplace's snapshot, including campaigns that delivered nothing in the window — their metrics are zero rather than absent. Archived campaigns are included; filter on state.

Parameters

ParameterTypeMeaning
profileIdstring
required
The Amazon advertising profile (marketplace) to read, as returned by GET /api/v1/profiles. When this marketplace is one half of a linked pair of Amazon accounts, the read covers both, exactly as the MerchDash dashboard does; every row carries the profileId it actually came from.
startdate
optional
First day of the metric window, inclusive, as YYYY-MM-DD (UTC, the marketplace's own report days). Defaults to 29 days before end.
enddate
optional
Last day of the metric window, inclusive, as YYYY-MM-DD. Defaults to 2 days ago, because Amazon is still restating attribution for the days after that.
limitinteger
optional
Rows per page, 1–500. Defaults to 100.
cursorstring
optional
An opaque cursor from a previous response's nextCursor. It encodes the endpoint and the filters it was issued for: reusing it with different filters is invalid_cursor rather than a silently wrong page. Paging is not a snapshot — rows synced between two pages may appear.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: campaignId ascending

FieldTypeMeaning
profileIdstringThe marketplace this row belongs to.
adProductenum
SPONSORED_PRODUCTS · SPONSORED_BRANDS · SPONSORED_DISPLAY
Amazon ad product. Open enum: Amazon may add products.
statestring
nullable
ENABLED / PAUSED / ARCHIVED as of the last sync. Null if never observed.
campaignIdstringAmazon campaign id. Always a string, never a number — ids exceed the safe integer range.
namestring
nullable
targetingTypestring
nullable
MANUAL or AUTO for Sponsored Products.
budgetnumber
nullable
Daily budget in the marketplace currency.
budgetTypestring
nullable
biddingStrategystring
nullable
portfolioIdstring
nullable
portfolioNamestring
nullable
startDatestring
nullable
endDatestring
nullable
metricsobjectThe six report metrics summed over the window.
metrics.impressionsintegerImpressions in the window.
metrics.clicksintegerClicks in the window.
metrics.costnumberAd spend in the window, in the marketplace currency.
metrics.ordersintegerAttributed orders (14-day) in the window.
metrics.unitsintegerAttributed units (14-day) in the window.
metrics.salesnumberAttributed sales (14-day) in the window, in the marketplace currency.

GET /api/v1/ad-groups

Ad groups with their metrics for a window.

Scope: read

One row per ad group in the snapshot. campaignId narrows the read to one campaign.

Parameters

ParameterTypeMeaning
profileIdstring
required
The Amazon advertising profile (marketplace) to read, as returned by GET /api/v1/profiles. When this marketplace is one half of a linked pair of Amazon accounts, the read covers both, exactly as the MerchDash dashboard does; every row carries the profileId it actually came from.
campaignIdstring
optional
Only ad groups in this campaign.
startdate
optional
First day of the metric window, inclusive, as YYYY-MM-DD (UTC, the marketplace's own report days). Defaults to 29 days before end.
enddate
optional
Last day of the metric window, inclusive, as YYYY-MM-DD. Defaults to 2 days ago, because Amazon is still restating attribution for the days after that.
limitinteger
optional
Rows per page, 1–500. Defaults to 100.
cursorstring
optional
An opaque cursor from a previous response's nextCursor. It encodes the endpoint and the filters it was issued for: reusing it with different filters is invalid_cursor rather than a silently wrong page. Paging is not a snapshot — rows synced between two pages may appear.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: adGroupId ascending

FieldTypeMeaning
profileIdstringThe marketplace this row belongs to.
adProductenum
SPONSORED_PRODUCTS · SPONSORED_BRANDS · SPONSORED_DISPLAY
Amazon ad product. Open enum: Amazon may add products.
statestring
nullable
ENABLED / PAUSED / ARCHIVED as of the last sync. Null if never observed.
adGroupIdstring
campaignIdstring
nullable
namestring
nullable
defaultBidnumber
nullable
The ad group's default bid — what a keyword or target with no bid of its own bids.
metricsobjectThe six report metrics summed over the window.
metrics.impressionsintegerImpressions in the window.
metrics.clicksintegerClicks in the window.
metrics.costnumberAd spend in the window, in the marketplace currency.
metrics.ordersintegerAttributed orders (14-day) in the window.
metrics.unitsintegerAttributed units (14-day) in the window.
metrics.salesnumberAttributed sales (14-day) in the window, in the marketplace currency.

GET /api/v1/keywords

Keywords with their metrics for a window.

Scope: read

Positive keywords only. Negative keywords are not entities a caller can read in v1 — they are created through POST /api/v1/changes and shown in the review queue.

Parameters

ParameterTypeMeaning
profileIdstring
required
The Amazon advertising profile (marketplace) to read, as returned by GET /api/v1/profiles. When this marketplace is one half of a linked pair of Amazon accounts, the read covers both, exactly as the MerchDash dashboard does; every row carries the profileId it actually came from.
campaignIdstring
optional
Only keywords in this campaign.
adGroupIdstring
optional
Only keywords in this ad group.
startdate
optional
First day of the metric window, inclusive, as YYYY-MM-DD (UTC, the marketplace's own report days). Defaults to 29 days before end.
enddate
optional
Last day of the metric window, inclusive, as YYYY-MM-DD. Defaults to 2 days ago, because Amazon is still restating attribution for the days after that.
limitinteger
optional
Rows per page, 1–500. Defaults to 100.
cursorstring
optional
An opaque cursor from a previous response's nextCursor. It encodes the endpoint and the filters it was issued for: reusing it with different filters is invalid_cursor rather than a silently wrong page. Paging is not a snapshot — rows synced between two pages may appear.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: keywordId ascending

FieldTypeMeaning
profileIdstringThe marketplace this row belongs to.
adProductenum
SPONSORED_PRODUCTS · SPONSORED_BRANDS · SPONSORED_DISPLAY
Amazon ad product. Open enum: Amazon may add products.
statestring
nullable
ENABLED / PAUSED / ARCHIVED as of the last sync. Null if never observed.
keywordIdstring
campaignIdstring
nullable
adGroupIdstring
nullable
keywordTextstring
nullable
matchTypestring
nullable
EXACT / PHRASE / BROAD.
bidnumber
nullable
The keyword's own bid. Null means it has none and bids its ad group's defaultBid.
metricsobjectThe six report metrics summed over the window.
metrics.impressionsintegerImpressions in the window.
metrics.clicksintegerClicks in the window.
metrics.costnumberAd spend in the window, in the marketplace currency.
metrics.ordersintegerAttributed orders (14-day) in the window.
metrics.unitsintegerAttributed units (14-day) in the window.
metrics.salesnumberAttributed sales (14-day) in the window, in the marketplace currency.

GET /api/v1/targets

Targeting clauses with their metrics for a window.

Scope: read

Positive targeting clauses — product targets, categories, and an auto campaign's close-match / loose-match / substitutes / complements clauses. Negative targets are not readable in v1.

Parameters

ParameterTypeMeaning
profileIdstring
required
The Amazon advertising profile (marketplace) to read, as returned by GET /api/v1/profiles. When this marketplace is one half of a linked pair of Amazon accounts, the read covers both, exactly as the MerchDash dashboard does; every row carries the profileId it actually came from.
campaignIdstring
optional
Only targets in this campaign.
adGroupIdstring
optional
Only targets in this ad group.
startdate
optional
First day of the metric window, inclusive, as YYYY-MM-DD (UTC, the marketplace's own report days). Defaults to 29 days before end.
enddate
optional
Last day of the metric window, inclusive, as YYYY-MM-DD. Defaults to 2 days ago, because Amazon is still restating attribution for the days after that.
limitinteger
optional
Rows per page, 1–500. Defaults to 100.
cursorstring
optional
An opaque cursor from a previous response's nextCursor. It encodes the endpoint and the filters it was issued for: reusing it with different filters is invalid_cursor rather than a silently wrong page. Paging is not a snapshot — rows synced between two pages may appear.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: targetId ascending

FieldTypeMeaning
profileIdstringThe marketplace this row belongs to.
adProductenum
SPONSORED_PRODUCTS · SPONSORED_BRANDS · SPONSORED_DISPLAY
Amazon ad product. Open enum: Amazon may add products.
statestring
nullable
ENABLED / PAUSED / ARCHIVED as of the last sync. Null if never observed.
targetIdstring
campaignIdstring
nullable
adGroupIdstring
nullable
expressionstring
nullable
The targeting clause as Amazon stores it.
expressionTypestring
nullable
bidnumber
nullable
Null means the clause bids its ad group's defaultBid.
metricsobjectThe six report metrics summed over the window.
metrics.impressionsintegerImpressions in the window.
metrics.clicksintegerClicks in the window.
metrics.costnumberAd spend in the window, in the marketplace currency.
metrics.ordersintegerAttributed orders (14-day) in the window.
metrics.unitsintegerAttributed units (14-day) in the window.
metrics.salesnumberAttributed sales (14-day) in the window, in the marketplace currency.

GET /api/v1/asins

Advertising performance aggregated per ASIN.

Scope: read

An ASIN-LEVEL AGGREGATION, not a list of product ads. One row per advertised ASIN, summing every ad of it across every campaign and ad group in the marketplace. ads is how many distinct product ads contributed. To act on one ad, read it from the ad group it lives in.

Parameters

ParameterTypeMeaning
profileIdstring
required
The Amazon advertising profile (marketplace) to read, as returned by GET /api/v1/profiles. When this marketplace is one half of a linked pair of Amazon accounts, the read covers both, exactly as the MerchDash dashboard does; every row carries the profileId it actually came from.
startdate
optional
First day of the metric window, inclusive, as YYYY-MM-DD (UTC, the marketplace's own report days). Defaults to 29 days before end.
enddate
optional
Last day of the metric window, inclusive, as YYYY-MM-DD. Defaults to 2 days ago, because Amazon is still restating attribution for the days after that.
limitinteger
optional
Rows per page, 1–500. Defaults to 100.
cursorstring
optional
An opaque cursor from a previous response's nextCursor. It encodes the endpoint and the filters it was issued for: reusing it with different filters is invalid_cursor rather than a silently wrong page. Paging is not a snapshot — rows synced between two pages may appear.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: asin ascending

FieldTypeMeaning
profileIdstringThe marketplace you asked for. An ASIN row can span both halves of a linked pair of Amazon accounts — the ASIN is the identity here, not the profile.
asinstring
adsintegerDistinct product ads of this ASIN that delivered in the window.
campaignsintegerDistinct campaigns this ASIN delivered in during the window.
metricsobjectThe six report metrics summed over the window.
metrics.impressionsintegerImpressions in the window.
metrics.clicksintegerClicks in the window.
metrics.costnumberAd spend in the window, in the marketplace currency.
metrics.ordersintegerAttributed orders (14-day) in the window.
metrics.unitsintegerAttributed units (14-day) in the window.
metrics.salesnumberAttributed sales (14-day) in the window, in the marketplace currency.

GET /api/v1/search-terms

Shopper search terms with their metrics for a window.

Scope: read

One row per (campaign, ad group, keyword, search term) as Amazon reported it. For a product-targeting campaign the "search term" may be an ASIN rather than a shopper query — that is Amazon's own shape.

Parameters

ParameterTypeMeaning
profileIdstring
required
The Amazon advertising profile (marketplace) to read, as returned by GET /api/v1/profiles. When this marketplace is one half of a linked pair of Amazon accounts, the read covers both, exactly as the MerchDash dashboard does; every row carries the profileId it actually came from.
startdate
optional
First day of the metric window, inclusive, as YYYY-MM-DD (UTC, the marketplace's own report days). Defaults to 29 days before end.
enddate
optional
Last day of the metric window, inclusive, as YYYY-MM-DD. Defaults to 2 days ago, because Amazon is still restating attribution for the days after that.
limitinteger
optional
Rows per page, 1–500. Defaults to 100.
cursorstring
optional
An opaque cursor from a previous response's nextCursor. It encodes the endpoint and the filters it was issued for: reusing it with different filters is invalid_cursor rather than a silently wrong page. Paging is not a snapshot — rows synced between two pages may appear.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: campaignId, adGroupId, keywordId, searchTerm — all ascending

FieldTypeMeaning
profileIdstring
adProductstring
campaignIdstring
adGroupIdstring
keywordIdstringThe keyword or target the term matched through.
searchTermstring
keywordstring
nullable
The keyword text or targeting clause it matched.
matchTypestring
nullable
metricsobjectThe six report metrics summed over the window.
metrics.impressionsintegerImpressions in the window.
metrics.clicksintegerClicks in the window.
metrics.costnumberAd spend in the window, in the marketplace currency.
metrics.ordersintegerAttributed orders (14-day) in the window.
metrics.unitsintegerAttributed units (14-day) in the window.
metrics.salesnumberAttributed sales (14-day) in the window, in the marketplace currency.

GET /api/v1/metrics/daily

Account totals for each day in a window.

Scope: read

One row per report day, summing every campaign in the marketplace — the same daily totals the MerchDash dashboard charts. This is the endpoint to ask "what did we spend on this day"; the entity reads above answer "what did this campaign do over this window", and summing a page of them is neither cheaper nor more correct. A day the archive holds nothing for is absent rather than zero — before the first sync covered it, no spend and no data look identical, and only GET /api/v1/sync/status can tell them apart. When the marketplace is one half of a linked pair the row is the sum of both, exactly as the dashboard shows it, so a row carries no profileId of its own. There is no paging: the whole window comes back in one response, which the 365-day window cap bounds at 365 rows.

Parameters

ParameterTypeMeaning
profileIdstring
required
The Amazon advertising profile (marketplace) to read, as returned by GET /api/v1/profiles. When this marketplace is one half of a linked pair of Amazon accounts, the read covers both, exactly as the MerchDash dashboard does; every row carries the profileId it actually came from.
campaignIdstring
optional
Sum only this campaign. It narrows what each day totals; it never splits a day into several rows.
adProductenum
SPONSORED_PRODUCTS · SPONSORED_BRANDS · SPONSORED_DISPLAY
optional
Sum only this Amazon ad product.
startdate
optional
First day of the metric window, inclusive, as YYYY-MM-DD (UTC, the marketplace's own report days). Defaults to 29 days before end.
enddate
optional
Last day of the metric window, inclusive, as YYYY-MM-DD. Defaults to 2 days ago, because Amazon is still restating attribution for the days after that.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: date ascending

FieldTypeMeaning
datestringThe report day, YYYY-MM-DD, in the marketplace's own report calendar.
metricsobjectThe six report metrics summed over that one day.
metrics.impressionsintegerImpressions in the window.
metrics.clicksintegerClicks in the window.
metrics.costnumberAd spend in the window, in the marketplace currency.
metrics.ordersintegerAttributed orders (14-day) in the window.
metrics.unitsintegerAttributed units (14-day) in the window.
metrics.salesnumberAttributed sales (14-day) in the window, in the marketplace currency.

GET /api/v1/sync/status

How current the synced archive is for a marketplace.

Scope: read

The freshness of the data every read above answers from, and whether the first backfill has finished — proposals are refused with sync_incomplete until it has.

Parameters

ParameterTypeMeaning
profileIdstring
required
The Amazon advertising profile (marketplace) to read, as returned by GET /api/v1/profiles. When this marketplace is one half of a linked pair of Amazon accounts, the read covers both, exactly as the MerchDash dashboard does; every row carries the profileId it actually came from.

Response

Single resource — { data: { … } }.

FieldTypeMeaning
profileIdstring
lastSyncAttimestamp
nullable
When a sync last completed.
initialSyncCompletebooleanFalse while the first backfill is still running. Proposals are refused until it is true.
syncingbooleanTrue while a sync is running or queued right now.
firstSyncedDatestring
nullable
Oldest report day held, YYYY-MM-DD.
lastSyncedDatestring
nullable
Newest report day held, YYYY-MM-DD.
syncedDaysintegerHow many report days are held.
campaignsintegerCampaigns in the snapshot.

GET /api/v1/runs

Automation runs for a marketplace, newest activity first.

Scope: read

Every run MerchDash recorded for the marketplace — proposals made through this API and runs made by the account's own automation rules alike. trigger says which.

Parameters

ParameterTypeMeaning
profileIdstring
required
The Amazon advertising profile (marketplace) to read, as returned by GET /api/v1/profiles. When this marketplace is one half of a linked pair of Amazon accounts, the read covers both, exactly as the MerchDash dashboard does; every row carries the profileId it actually came from.
statusenum
preview · running · applied · partial · failed · reverted · discarded
optional
Only runs in this public status. Omit for all. Internal bookkeeping states are never accepted or returned.
limitinteger
optional
Rows per page, 1–500. Defaults to 100.
cursorstring
optional
An opaque cursor from a previous response's nextCursor. It encodes the endpoint and the filters it was issued for: reusing it with different filters is invalid_cursor rather than a silently wrong page. Paging is not a snapshot — rows synced between two pages may appear.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: most recent activity first, then runId — a review enters the list when it is approved, not when it was proposed

FieldTypeMeaning
runIdstring
profileIdstring
statusenum
preview · running · applied · partial · failed · reverted · discarded
Closed enum.
triggerstringapi, manual, schedule, … — what started the run.
modestring
nullable
review waits for a human; auto applies by itself.
labelstring
nullable
The batch label for an API proposal, or the rule name for a rule run.
ruleIdstring
nullable
Null for an API proposal — it has no rule.
apiKeyIdstring
nullable
Which key proposed it. Survives that key being revoked.
startedAttimestamp
nullable
finishedAttimestamp
nullable
countsobjectHow the run's own changes stand. Revert records are not counted.
counts.proposedintegerStill awaiting approval.
counts.appliedinteger
counts.failedinteger
counts.revertedinteger
counts.discardedinteger

GET /api/v1/runs/:runId

One run — the polling target for the runId a proposal returned.

Scope: read

The same object the list returns. An unknown id and a run belonging to another account answer identically with 404 not_found: telling them apart would describe somebody else's account.

Parameters

ParameterTypeMeaning
runId (path)string
required
The run id, as returned by POST /api/v1/changes.

Response

Single resource — { data: { … } }.

Fields: same as runs.list

GET /api/v1/runs/:runId/changes

The individual changes a run proposed, and what became of each.

Scope: read

One row per change the run itself proposed. The revert records a later revert writes are excluded: they belong to the revert, not to this run, and status: reverted on the original already says it happened.

Parameters

ParameterTypeMeaning
runId (path)string
required
limitinteger
optional
Rows per page, 1–500. Defaults to 100.
cursorstring
optional
An opaque cursor from a previous response's nextCursor. It encodes the endpoint and the filters it was issued for: reusing it with different filters is invalid_cursor rather than a silently wrong page. Paging is not a snapshot — rows synced between two pages may appear.

Response

Collection — { data: [ … ], nextCursor }, and nextCursor is null on the last page.

Order: changeId ascending

FieldTypeMeaning
changeIdstring
runIdstring
statusenum
proposed · applied · failed · reverted · discarded
Closed enum.
entityTypestring
nullable
campaign / adGroup / keyword / target / productAd / negativeKeyword / negativeTarget.
entityIdstring
nullable
Null for a change that creates something — there is no id until it is applied.
entityNamestring
nullable
campaignIdstring
nullable
adGroupIdstring
nullable
fieldstring
nullable
What the change alters: bid, state, negative, …
beforeValstring
nullable
The value MerchDash read before proposing. Null for a create.
afterValstring
nullable
The value the change writes.
reasonstring
nullable
The caller's own note, shown to the human reviewing it.
errorstring
nullable
Why this change failed, when it did.
appliedAttimestamp
nullable
createdAttimestamp
nullable

POST /api/v1/changes

Propose a batch of bid, state and negative changes — for a human to approve, or to apply at once.

Scope: write

Writes a run and returns immediately; the writing to Amazon, when there is any, happens in the background. On a review key — the default, and what every key is until its owner says otherwise — nothing reaches Amazon here at all: a signed-in human applies or turns down the proposal in MerchDash's review queue. On an auto-apply key, or with "applyMode": "auto", MerchDash applies the batch itself as soon as it is accepted. Either way an API key can watch the outcome through GET /api/v1/runs/:runId and can never approve, discard or revert a run itself.

Headers

HeaderRequiredMeaning
Content-TyperequiredMust be application/json.
Idempotency-Keyrequired1–128 visible ASCII characters, unique per logical request. A retry with the same key and the same body returns the first call's response and creates nothing; the same key with a different body is 409 idempotency_conflict. Keys are remembered for 24 hours.

Request body

FieldTypeMeaning
profileIdstring
required
Every change in the batch must belong to this marketplace. A mixed batch is refused whole.
labelstring
required
1–80 characters. What the review card is titled — the human deciding sees this and nothing else about who sent it.
changesarray
required
1–500 change objects, discriminated on operation. set_auto_clause_bids expands into several changes each, and a request whose expansion exceeds 500 is refused whole rather than truncated.
applyModeenum
review · auto
optional
What happens to the proposal. review queues it for a human, which is what a key does by default. auto writes it to Amazon as soon as it is accepted, with nobody in the loop, and is only allowed on a key whose owner has turned auto-apply on for it — on any other key it is 403 scope. Omit it to use whatever the key is set to. A key that CAN auto-apply may still send review for a batch that wants a second pair of eyes; the reverse is never possible.

Operation: set_bid

Set a keyword's or target's bid.

FieldTypeMeaning
entityTypeenum
keyword · target
required
entityIdstring
required
Must exist in this marketplace's snapshot.
bidnumber
required
In the marketplace currency. $0.02–$1,000 ($0.25 minimum on Sponsored Brands). A bid above the account's own maximum-bid setting is written at that maximum and flagged as adjusted, exactly as an automation rule's would be.
reasonstring
optional
Up to 200 characters, shown to the reviewer. Display only.

Operation: set_state

Enable or pause an entity.

FieldTypeMeaning
entityTypeenum
campaign · adGroup · keyword · target · productAd
required
entityIdstring
required
stateenum
ENABLED · PAUSED
required
ARCHIVED is not offered: archiving is irreversible on Amazon and cannot be undone by a revert.
reasonstring
optional

Operation: set_auto_clause_bids

Set the bids of an auto campaign's four predefined targeting clauses — the same action as Actions → Adjust Auto Targeting Bid in the dashboard. This is the only operation that reads from Amazon while it compiles: an auto campaign's clauses appear in MerchDash's own snapshot only once they have delivered, so the clauses are enumerated live and the request expands into one target-bid change per clause it actually alters. Expect it to be slower than the other operations, and to answer 503 temporarily_unavailable when Amazon cannot be reached — no proposal is created in that case.

FieldTypeMeaning
campaignIdstring
required
An auto-targeting Sponsored Products campaign. Any other campaign is refused: nothing else has these clauses.
bidsobject
required
One bid per clause, keyed close-match, loose-match, substitutes, complements. A clause you omit — or set to null — is left alone. Same range and the same maximum-bid clamp as set_bid.
adGroupIdsarray
optional
Narrows the fill to these ad groups of the campaign, at most 125 of them. Omit it to fill every ad group in the campaign. An empty array is refused rather than read as "all".
emptyOnlyboolean
optional
Default true: only clauses with no bid of their own — the ones inheriting the ad group default — are filled. false overwrites existing clause bids too.
reasonstring
optional
Shown to the reviewer on every change this expands into. Omit it and each change explains itself (what the clause was bidding, or which default it was inheriting).

Operation: add_negative_keyword

Add a negative keyword at campaign or ad-group scope.

FieldTypeMeaning
campaignIdstring
required
adGroupIdstring
optional
Omit for a campaign-wide negative. Present means ad-group scope.
keywordTextstring
required
1–80 characters.
matchTypeenum
NEGATIVE_EXACT · NEGATIVE_PHRASE
required
reasonstring
optional

Operation: add_negative_target

Add a negative product target (an ASIN) to an ad group.

FieldTypeMeaning
campaignIdstring
required
adGroupIdstring
required
Required — Amazon has no campaign-scoped negative product target.
asinstring
required
A 10-character ASIN.
reasonstring
optional
Unknown fields are refused. Any field not named above is refused rather than ignored — including the internal fields of a stored change (apiCall, beforeVal, afterVal, status). The server derives all of those from its own snapshot, so a caller can never dictate what MerchDash will send to Amazon.

Response

Single resource — { data: { … } }.

FieldTypeMeaning
runIdstringPoll GET /api/v1/runs/:runId for what a human decides.
countintegerHow many changes the run holds.
applyModeenum
review · auto
What this run will actually do — what the key allows, narrowed by whatever the request asked for. review means a human decides; auto means MerchDash is already writing it.
statusenum
preview · running · applied · partial · failed · reverted · discarded
The run's status at the instant it was created, which is preview either way — an auto-applied run leaves it within seconds, on its own. Branch on applyMode, not on this: with applyMode: "auto" never tell a user their changes are waiting for approval. Poll GET /api/v1/runs/:runId for what happened.

POST /api/v1/structures

Propose new campaigns, ad groups, product ads, keywords and targeting clauses for a human to approve.

Scope: write

Takes a nested graph and turns it into durable creation intents plus one run. On a review key — the default — nothing reaches Amazon here: a signed-in human applies the proposal in MerchDash, and only then is the structure built, parents before children. On an auto-apply key, or with "applyMode": "auto", MerchDash starts building it immediately, in that same dependency order. Because a create cannot be made idempotent by value — the id does not exist yet — each container is RESERVED under a stable identity at proposal time. A retry, a second proposal, or a nightly automation rule asking for the same campaign in the same marketplace lands on that same reservation instead of building a second one.

Headers

HeaderRequiredMeaning
Content-TyperequiredMust be application/json.
Idempotency-Keyrequired1–128 visible ASCII characters, unique per logical request. A retry with the same key and the same body returns the first call's response and creates nothing; the same key with a different body is 409 idempotency_conflict. Keys are remembered for 24 hours.

What this endpoint will not do

  • Sponsored Products only. An SB ad group needs a creative and a landing page and an SD one a tactic, neither of which this API can supply, so a node naming a Sponsored Brands or Sponsored Display campaign is refused.
  • Auto-targeting campaigns take no keywords and no positive targeting clauses. Amazon creates their close match, loose match, substitutes and complements clauses with the ad group and lets nothing be added to them; only negatives are accepted there, through POST /api/v1/changes.
  • Created entities start ENABLED unless a node says "state": "PAUSED", and campaigns are created with no start date, so a proposal begins spending when the human applies it.
  • A campaign or ad group that already exists — named by id, or matched by the name you gave — is added to, never modified: its budget, bid, targeting and state are not changed here. alreadyPresent reports the matched container and its id; it does not echo settings that were ignored.
  • At most 2000 campaigns and ad groups, and 20000 ASINs, keywords and targeting clauses, per request.

Request body

FieldTypeMeaning
profileIdstring
required
Every node in the graph belongs to this marketplace.
labelstring
required
1–80 characters. What the review card is titled.
campaignsarray
optional
Campaigns to create, or existing campaigns to add ad groups to. Each entry names EITHER name OR campaignId, never both. A name the marketplace already has is ADDED TO, not created again — the same get-or-create an automation rule performs — so sending the same request twice builds one campaign.
adGroupsarray
optional
The single-shot form: ad groups that ALREADY exist, named by adGroupId, to add ASINs, keywords or targeting clauses to. This is how a script adds product ads — there is no stage-then-commit step, unlike the dashboard's paste box, and unlike that path nothing is written until a human approves it.
applyModeenum
review · auto
optional
What happens to the proposal. review queues it for a human, which is what a key does by default. auto builds the structure on Amazon as soon as the request is accepted, and is only allowed on a key whose owner has turned auto-apply on for it — on any other key it is 403 scope. Omit it to use whatever the key is set to. Worth a thought here in particular: a campaign or an ad group Amazon has created cannot be un-created by a revert.

Node: campaign (new)

FieldTypeMeaning
namestring
required
1–128 characters. Compared case-insensitively and whitespace-collapsed, exactly as Amazon collides names. A campaign of this name that already exists is used as-is; its targeting type must match the one asked for, since Amazon cannot change that after creation.
budgetnumber
required
Daily budget in the marketplace currency. Below Amazon's floor of 1 it is raised to it; above the account's creation ceiling it is lowered to that and reported in adjustments.
targetingenum
manual · auto
required
Amazon cannot change a campaign's targeting type after it is created.
stateenum
ENABLED · PAUSED
optional
Defaults to ENABLED.
biddingStrategystring
optional
Amazon's value or the Campaign Manager label — "DYNAMIC BIDS - UP AND DOWN", "DYNAMIC BIDS - DOWN ONLY" (the default), or "FIXED BIDS".
portfoliostring
optional
A portfolio name. Created if the account does not have one, and the campaign waits for it.
adGroupsarray
optional
Ad groups to get-or-create inside it by name. An existing adGroupId may also be used when the campaign name resolves to an existing campaign; if the campaign itself has to be created, its children must use name.

Node: campaign (existing)

FieldTypeMeaning
campaignIdstring
required
Must exist in this marketplace and be Sponsored Products.
adGroupsarray
required
At least one ad group to create in it, or to add leaves to.

Node: adGroup (new)

FieldTypeMeaning
namestring
required
1–128 characters. Unique within its campaign — "Core terms" in two campaigns is two ad groups. One the campaign already holds is added to rather than created again, and then defaultBid is not needed.
defaultBidnumber
optional
The bid a keyword or clause with no bid of its own uses. $0.02–$1,000, and the account's own maximum-bid setting applies. Required only when the ad group has to be created — an ad group of this name that the campaign already holds keeps its own default bid.
stateenum
ENABLED · PAUSED
optional
Defaults to ENABLED.
asinsarray
optional
ASINs to advertise, as 10-character strings.
keywordsarray
optional
targetsarray
optional

Node: adGroup (existing)

FieldTypeMeaning
adGroupIdstring
required
Must exist in this marketplace. At least one of asins/keywords/targets is required — an existing ad group's own settings are not changed here.
asinsarray
optional
keywordsarray
optional
targetsarray
optional

Node: keyword

FieldTypeMeaning
textstring
required
1–80 characters. An ASIN is refused: bought as keyword text it is a literal string no shopper types, so it costs a write and never serves. Send it under targets.
matchTypeenum
EXACT · PHRASE · BROAD
required
bidnumber
optional
Omit to follow the ad group's default bid.
stateenum
ENABLED · PAUSED
optional
reasonstring
optional
Up to 200 characters, shown to the reviewer. Display only.

Node: target

FieldTypeMeaning
asinstring
optional
A product target. Exactly one of asin or category.
categorystring
optional
An Amazon category id. Exactly one of asin or category.
bidnumber
optional
stateenum
ENABLED · PAUSED
optional
reasonstring
optional
Unknown fields are refused. Any field not named above is refused rather than ignored, including every internal field of a stored change and of a creation intent (apiCall, desiredKey, specHash, intentId). The server derives all of those, so a caller can never dictate what MerchDash will send to Amazon or what identity it will reserve under.

Response

Single resource — { data: { … } }.

FieldTypeMeaning
runIdstringPoll GET /api/v1/runs/:runId for what a human decides.
countintegerHow many change rows the run holds. Not the number of entities: ASINs and targeting clauses are batched one row per ad group.
intentsintegerHow many containers were reserved — portfolios, campaigns and ad groups.
applyModeenum
review · auto
What this run will actually do — what the key allows, narrowed by whatever the request asked for. review means a human decides; auto means MerchDash is already building it.
statusenum
preview · running · applied · partial · failed · reverted · discarded
The run's status at the instant it was created, which is preview either way — an auto-applied run leaves it within seconds, on its own. Branch on applyMode, not on this. Poll GET /api/v1/runs/:runId for what happened.
alreadyPresentarray
nullable
Up to the first 50 things the marketplace snapshot already held, so they were added to rather than proposed again. Absent when there were none. Each entry is { kind, value } plus the id it resolved to — adGroupId for asin, keyword, target and adGroup, campaignId for campaign, or portfolioId for portfolio.
alreadyPresentCountinteger
nullable
Total number of already-present containers and leaves, including entries omitted from the bounded alreadyPresent sample. Absent when there were none.
alreadyPresentTruncatedboolean
nullable
True when alreadyPresentCount is larger than the returned alreadyPresent sample. Absent when there were no already-present items.
adjustmentsarray
nullable
Values an account or Amazon ceiling moved, with the number that will actually be used. Absent when there were none. Each entry is { path, field, note }.

Run statuses

The lifecycle a proposal moves through, as reported by GET /api/v1/runs/{runId}. This is a closed enum: a client may switch on it exhaustively, and MerchDash’s internal bookkeeping states are mapped into these rather than added to them.

StatusMeaningCan become
previewProposed and waiting for a human to approve it in MerchDash. Nothing has been written to Amazon.running, discarded, partial
runningA human approved it and the writes are in flight.applied, partial, failed
appliedEvery change was written to Amazon.reverted
partialSome changes were written and others were not — because they failed, or because they are still awaiting approval.reverted, running
failedThe run ended without writing anything it meant to write.nothing — terminal
revertedAt least one applied change has since been rolled back.nothing — terminal
discardedA human turned the proposal down. Nothing was written to Amazon, and the run stays readable at this id.nothing — terminal

Error codes

Every error response carries one of these as code, alongside a human-readable error. Switch on the code; the message wording may be improved at any time.

CodeHTTPRetry?What happened
invalid_key401stopThe bearer token is not a usable MerchDash API key.
key_revoked401stopThe key was revoked by its owner.
key_expired401stopThe key is past its expiry date.
plan403stopThe account's plan does not include API access. The key is not revoked — an upgrade re-enables it.
scope403fixThe credential does not carry the capability this request requires — a read-only key on a write route, a browser session on an endpoint that needs a key, or "applyMode": "auto" on a key whose owner has not turned auto-apply on for it.
not_found404fixNo such route, or no such resource in this account. An id belonging to somebody else answers exactly this.
validation400fixThe request could not be read or accepted. May carry a bounded details array naming each problem.
invalid_cursor400fixThe cursor is not one of ours, or was issued for a different endpoint or different filters.
idempotency_conflict409fixThis Idempotency-Key was already used for a different request body.
creation_conflict409laterA campaign or ad group this request wants to create is already reserved by another proposal that nobody has answered yet. Apply or turn that one down first — the reservation is what stops the same container being built twice.
rate_limited429waitA rate limit was exhausted. Retry-After says how long to wait.
review_queue_full429laterThis marketplace already holds 25 un-answered proposals. Retry once a human has worked through some.
sync_incomplete409laterThe marketplace's first data sync has not finished, so proposals cannot be validated against its snapshot yet.
restarting503waitMerchDash is mid-deploy. The request was refused before it did anything and is safe to re-send.
temporarily_unavailable503waitA dependency the request needs — shared rate enforcement, for one — could not be reached.