Documentation

MCP, CLI & API

New account-wide reads, insights, editing, copying and history tools are in beta and are turned on per account; existing commands and parent-scoped campaign/ad set/ad reads remain available.

CLI Configuration

The Ads Uploader CLI (command-line interface) lets you upload media and create Meta ads from a terminal. CLI access is for paid plans and is not available during a trial.

The CLI follows the same structure as the web app. If you have uploaded ads through the web app, the CLI will make sense straight away: you pick a source ad or preset, add your media, preview, then create.

Presets you build in the web app show up in the CLI, and you can save new API presets from the CLI with ads presets:save. The CLI also uses the same saved build references as the web uploader and MCP, so you can carry on with an in-progress build without starting again.

Why use the CLI? It gives you the same Ads Uploader launch pipeline through an interface built for AI agents. Loading several batches is faster, and you can let an agent write ad copy and assemble builds for you. Choose the CLI when you want to run your whole operation through an agent. For small, targeted help inside a web-first workflow, see CLI or MCP? on the MCP page.

Most people drive the CLI through an AI agent such as Claude Code or Cursor. See Using with AI below.

Install and Sign In

The CLI needs Node.js 18 or later. Install it from npm:

npm install -g @adsuploader/cli

Then sign in:

ads login

ads login opens your browser so you can approve the sign-in with your Ads Uploader account. On a server or over SSH it uses device login, which you approve on another device. The CLI then uses your existing Meta connection.

How long a login lasts. A login token lasts 30 days. After that, run ads login again. You can see and revoke your CLI sessions under Account > MCP, CLI & API in the MCP and CLI-Connected AI Apps card.

Updating. The CLI tells you when a new version is out. Update with:

npm update -g @adsuploader/cli

Run ads --version to see which version you have.

Logging in on a server (no browser)

On a VPS, in SSH, or on a machine without a browser, run:

ads login --device

Open the printed URL on your phone or laptop, enter the code, and approve the device after checking its country and network. The code expires after 10 minutes. The CLI saves a 30-day login on the server. ads login automatically uses this flow over SSH or on Linux without a display, and falls back to it if opening the browser fails. Use ads login --browser to force the localhost browser flow.

API Keys for Your Own Agents and Scripts

For long-running connections from your own agents and scripts, use an API key through ADS_API_KEY or ads login --api-key. See API Keys for creation, setup, one-person use and revocation.

Set Your Ad Account

Run ads accounts to list the ad accounts connected to your Meta account, then set a default:

ads account act_123456789

This is the same as the account selector in the web app. If you have just been given access to a new ad account in Meta, run ads accounts:refresh to fetch the list again straight away.

Browse Your Account

Before you create ads, you can click through your account from the terminal, just as you would in the web app:

CommandWhat it does
ads campaignsList your active campaigns
ads campaigns --status allInclude inactive campaigns as well
ads campaign 123Show the ad sets inside a campaign
ads adsets --search "Summer"Find ad sets by name anywhere in the account, with the campaign each one is in
ads adset 456Show the ads inside an ad set
ads ads --search "Summer"Find ads by name anywhere in the account, with their ad set and campaign
ads ad 789Show an ad's full details and creative settings. If Meta rejected the ad, this also shows why
ads presetsList your saved API presets
ads presets:save --from-ad 789 --name "Summer Sale"Save an existing ad as an API preset. Add --share to share it with your team.
ads text-presetsList your saved text presets
ads buildsList your saved builds

This is how you find the ad to copy settings from, or the preset or build to use.

Campaign tables show status, objective and budget. Ad set tables show status, optimization goal and budget. Ad tables show status and the creative ID. Add --limit and --after to page through long lists. With --json, the output keeps its usual shape and the next-page hint goes to stderr.

Read Performance and Account Data

The CLI can also read how your ads are doing and what is in your ad account. Nothing here changes your ads.

CommandWhat it reads
ads insights --level ad --fields ad_name,spend,purchases,roas --preset last_7dPerformance for any level, date range and breakdown, with friendly metrics such as purchases, leads, cost per lead and ROAS
ads insights --fields spend,leads,cost_per_lead --preset last_7d --compare previous_periodThe same numbers next to the previous period, with the change and percentage change
ads insights:health --campaign 123How a campaign is doing on its goal, plus delivery issues and learning status
ads insights:fields --search purchaseWhich fields, friendly metrics and breakdowns you can ask for
ads account:info act_123Account status, currency, timezone, spend cap, amount spent and balance
ads audiencesCustom, lookalike and saved audiences with their size
ads conversionsPixels, custom conversions and standard events
ads estimate --targeting targeting.json --goal REACHHow big an audience is and what a budget could reach
ads meta-activity --since 2026-09-01 --until 2026-09-07Meta's change history for the account (separate from the Ads Uploader Activity Log)
ads library:images / ads library:videosImages and videos in the Meta ad account library
ads posts --page 123Facebook Page posts (or --instagram <id> for Instagram posts) that can be used as ads

Every command supports --json, and --fresh to skip recently cached results. Large reports run in the background automatically and the CLI waits for them. See Ads Insights for every metric, breakdown, filter and limit.

History

ads history shows the same launches and changes as the Ads Uploader History page, newest first: Uploader, Duplicator, AI Edits, CLI, MCP and API activity. Use ads history launch <launchId> for campaigns, ad sets, ads, results and failure messages from one launch.

ads history --account act_123 --days 30
ads history --type changes --source mcp --search budget
ads history launch <launchId> --json

Both commands use --account or your default account. List filters are --days 7|30|90 (default 30; History covers the last 90 days), --type all|launches|changes (default all), --source web|duplicator|mcp|cli|api, --search <text> (up to 200 characters), and --member <id> (a team member ID). Use --limit 1..100 (default 50) and the printed --before <cursor> hint for more. Keep the same filters when paging. Tables show Time, Item, Type, Change, Before, After, Source and Status, plus By for teams. --json returns the full response unchanged, including paging and pending suggestions. The History page is open to everyone; reading History through the API, CLI and MCP is in beta.

ads meta-activity reads Meta's ad account activity log, including edits in Ads Manager and other tools. It retains --since, --until, --object, --after, --limit, --fresh, --account and --json. The default range is 7 days, with a maximum of 90 days.

ads history --meta and ads history with --since, --until or --object still read Meta activity for one release and print a deprecation note to stderr. Switch to ads meta-activity. The ads edits list is also deprecated; use ads history --type changes. ads edit:show, undo and ads edits cancel keep working.

Agent Inbox history

ads inbox:history (also ads inbox history) reads your retained suggestions and direct edit outcomes from the last 90 days, newest first, without calling Meta. It includes agent notes, edited-note markers, run titles, client names, before/after values and outcomes. Read permission is required; hidden and Read-off accounts are excluded.

ads inbox:history --account act_123 --campaign 12345 --status applied --json
ads inbox history --account act_123 --since 2026-10-01 --until 2026-10-09
ads inbox:history --run <reviewId> --kind duplicate --limit 20

Use --account or your selected account; with neither, history covers all permitted accounts. Filters are --campaign, --since/--until (inclusive ISO dates or timestamps with a timezone), --run <reviewId>, --status waiting|applied|failed|cancelled|skipped|stale, --kind edit|duplicate, and --action <operation> such as set_bid, rename or duplicate. A date-only --until includes the whole UTC day. Waiting includes applying, and failed includes partial outcomes; outcome preserves the specific result. Time is resolution time when recorded, otherwise creation time. Direct edits can be filtered by campaign only when that campaign ID was recorded. Use --limit 1..100 (default 50) and --cursor from the previous response, keeping the same filters. --json returns the full response including paging.cursor. The table's revised column shows the latest change made to a suggestion after the agent made it, such as user restored agent value or user edited note; --json includes every entry in revisions with at, by (user, or agent with source and clientName), kind (value or note), action (edited or restored_to_agent), field, before and after.

Upload Media

Upload images and videos to your ad account with ads upload:

ads upload hero.jpg banner.mp4 promo.mp4
ads upload ./my-creatives/
ads upload hero.jpg "https://cdn.example.com/banner.mp4"
ads upload "https://drive.google.com/file/d/.../view"
ads upload:drive "https://drive.google.com/drive/folders/..."

Each upload returns a batch ID. You use it when you create ads. Run ads uploads to see your recent batches.

ads upload spots HTTPS links by itself. You can mix local files and links in one command: local files go first, then Ads Uploader downloads each link on its server into the same batch. That way, ratio grouping and thumbnail matching still work across everything. Public Google Drive file links work like any other link. For a whole Drive folder, use ads upload:drive; the folder must be shared as Anyone with the link (Viewer).

Name your files with ratio suffixes and the CLI groups the versions for you, just like the web app. See Aspect Ratio Variations.

If some files fail (for example, because of a network drop), run ads upload --retry-failed to retry the failed files from your last failed batch. Add a batch ID to retry a specific batch.

Exit codes for ads upload changed in CLI 0.4.0: 0 means every file uploaded, 1 means at least one file failed (earlier versions returned 1 only when every file failed), and 2 means files are still processing when the CLI stops waiting. Scripts that check the result should retry failed files with ads upload --retry-failed, and wait and check again on 2.

Create Ads

Creating ads has two steps: preview and create.

The Spec File

A JSON spec file tells the CLI what to build. The simplest preset spec names a new campaign and your upload batch:

{ "adPresetId": "your_preset_id", "campaign": { "name": "New Campaign" }, "uploadId": "batch_abc123" }

Presets reuse their saved campaign and ad set when no destination is supplied, including preset-only creates and older saved builds. Preview and create return a non-fatal PRESET_DESTINATION_USED warning identifying the saved destination. To choose a different destination, pass campaign.name for a new campaign, named campaign.campaigns entries in duplicate/split mode, or campaign.id / adSet.id for an existing destination. With only an explicit campaign.id, new ad sets are created. With copyFromAd and no campaign or adSet, a new paused campaign and ad set are created from the template ad's settings; the template's own campaign and ad set are never used unless you pass their IDs.

Cross-campaign create/preview is available only for direct agent requests. Saved builds and Agent Inbox launches must use the template ad's campaign or a new campaign; to save a build for another existing campaign, copy from an ad in that campaign (campaign.id + adSet.id picks one). Existing stored cross-campaign builds can still launch, but cannot be re-saved. The same goes for ad sets: a saved build that names an existing ad set must name the template ad's own ad set, or use a new ad set; to post into another existing ad set, copy from an ad in that ad set or launch directly without saving a build. A stored build that names another ad set can still launch from the CLI and MCP, but must be changed in the uploader before it launches there.

You can copy settings from an existing ad instead of a preset:

{ "copyFromAd": "120233848667930472", "uploadId": "batch_abc123" }

To find the ad ID, browse with ads campaigns, ads campaign <id>, ads adset <id> and ads ad <id>. An ad built from an existing Page post works as a template too, and the new ads use the post's link. If neither the ad nor its post has a website link, set cta.link in the spec.

You can also skip the spec file and launch a saved build with --build <buildId>.

Preview First

Always preview before you create. ads create:preview spec.json shows exactly what would be created, without creating anything in Meta. It catches setup mistakes before anything goes live.

Create

When the preview looks right, run ads create spec.json. Ads go live by default, just like in the web app. To create them paused, add --status PAUSED or set "options": { "status": "PAUSED" } in the spec.

Duplicate Existing Ads by Post ID

Agent duplications through MCP, CLI and the public API follow the account’s Agent Launch setting on the Account Permissions page, the same as launches. Agent Edit does not apply to them. Allowed (the default) starts the copy directly. Approval returns pending_approval and reviewUrl; nothing is duplicated until the user approves in your Agent Inbox. Share the link and do not retry or poll: there is no job until approval. Off refuses the request with AGENT_LAUNCH_DISABLED. Regular ACTIVE copies may require explicit confirmSpend: true (--confirm-spend in the CLI); post-ID copies never do. Uploads from new media and the human-operated web Duplicator are unaffected.

Launches follow the same setting: with Approval, ads create prints a build link instead of launching. --review-id, --run-title and --reason hold the launch in an existing Agent Inbox run, as they do for edits and copies, so the user reviews the launch beside those changes (the changes and the launch run separately). A review ID from another user or ad account is refused; with Allowed these flags are ignored.

The CLI can also run the Duplicator, which copies an existing ad into another campaign or ad set while keeping its post engagement:

ads duplicator:post-id --account act_123 --post 1234567890_9876543210 --campaign 120200000000000000 --new-adset "Winners {AdName}" --paused

Preview the source-to-destination mapping first by running the same arguments with ads duplicator:post-id:preview. See Post-ID Duplication Flags for every flag and the spec format.

Full Reference

For every command and flag, the full spec format, creative enhancements, carousel, flexible and Multi Media ads, naming placeholders and spec limits, see the CLI Full Reference.

Track Jobs

Ad creation runs in the background. The CLI streams progress while it runs, and you can check on a job later:

CommandWhat it does
ads jobs JOB_IDCheck a job's status
ads jobs JOB_ID --followStream live progress
ads jobs cancel JOB_IDCancel a running job

ads create and --follow watch a job for up to 30 minutes at a time. On very large batches the CLI may stop watching with a "Still running" message before the job ends. That is not a failure: the job keeps running on the server. Pick it up again with ads jobs JOB_ID --follow. In that case the exit code is 2 (0 means success and 1 means an error).

You can run one ad creation job at a time per user. If you start another while one is running, the CLI tells you to wait for it to finish or cancel it.

Rate Limits

The CLI is rate limited per user and per kind of request. Normal use never hits the limits, but a runaway script gets a 429 Rate Limit Exceeded response with a Retry-After header. Do not wrap CLI commands in polling loops (such as watch or shell while loops). Use ads jobs JOB_ID --follow for live progress instead.

If Ads Uploader is briefly unreachable (an HTTP 502, 503 or 504 from the network in front of it), the CLI reports Ads Uploader is briefly unavailable. Wait a few seconds and retry once. with the code SERVICE_UNAVAILABLE. Changes are never retried automatically; for a change, the message asks you to check whether it went through before retrying.

Settings and Environment Variables

Run ads config to check your setup. It shows whether you are signed in, your email, your default ad account, the API URL and the config folder (~/.config/adsuploader/). Your login is saved in credentials.json in that folder, readable only by you. ads whoami shows your email, default account and API URL.

Environment variableWhat it does
ADS_API_TIMEOUT_MSAPI request timeout in milliseconds (default 60000). The --api-timeout flag does the same for one command.
ADS_API_URLThe Ads Uploader address the CLI talks to. Leave it unset for normal use.

Tips

  • Always preview first. create:preview catches setup mistakes before any Meta ads are created.
  • Ads go live by default. Use --status PAUSED if you want to check them in Ads Manager first.
  • Uploads belong to one ad account. A batch ID only works with the ad account you uploaded to.
  • Presets carry over from the web app. Build presets in the web app, or save API presets with ads presets:save, then use them by ID in the CLI.

Using with AI

The CLI is built to be driven by AI agents such as Claude Code or Cursor. After you install and sign in, give your agent the skill file so it knows every command and spec option.

The skill file ships inside the npm package. For the global install above, it is at:

"$(npm root -g)/@adsuploader/cli/SKILL.md"

In Claude Code, install it as a skill named ads:

mkdir -p .claude/skills/ads
cp "$(npm root -g)/@adsuploader/cli/SKILL.md" .claude/skills/ads/SKILL.md

Claude Code then loads it when you ask for ad work, or you can type /ads. In other AI tools such as Cursor, add SKILL.md as a rule or context file.

The CLI is an interface for media buyers and their agents. It is not meant to be built into other applications.

Example Prompt

Once it is set up, give your agent instructions like:

I have new ad creatives in my downloads/ads folder. Upload them and create ads using the same settings as my Summer Sale purchase preset. Group them into ad sets of five named with today's date, with a $25 daily budget. Write unique copy for each image based on what it shows. Pause at the ad set level and preview first.

API Access

The CLI and the MCP server both talk to the Ads Uploader v1 API. They use the token you get from ads login or from signing in to the MCP server. For your own agents, scripts and direct API requests, create an API key under Account > MCP, CLI & API and send it as Authorization: Bearer adk_.... The CLI can use the same key through ADS_API_KEY. An API key also works with the MCP server. See API Keys.

Partnership Ads

The CLI can launch partnership ads in two ways:

  • With your own media. Use a normal image, video, carousel, flexible, Multi Media or ratio-variant spec and add profile.partnership.enabled: true with your partner's IDs. You can set a different partner per ad or per ad set, or choose No Partner for some rows.
  • From existing Instagram posts. Import an authorized Instagram post by URL, shortcode, media ID or ad code with mediaItems[].kind: "partnershipPost".

Approval is checked again at preview and at create. A partner whose approval is pending or not given yet does not block the launch: you get a PARTNERSHIP_APPROVAL_PENDING warning, and Meta may hold delivery until the partner approves. For the full spec shapes, scoping keys and limits, see Partnership ads with your own media and Instagram existing-post partnership spec in the CLI Full Reference.

Reporting beta and capacity

Reporting and edits are available to beta accounts while in beta. Existing CLI/MCP access is still required. Hosted MCP hides all reporting and edit tools for accounts without access; local MCP keeps them listed and the server enforces access. Existing campaign, ad set and ad lists remain available. The CLI/API static field catalog stays accessible; account-scoped discovery requires beta access. Access errors display the server's explanation.

Queries allow 10 requests per minute, report-job reads 60, and other new reads 20. Meta's reported API usage determines when fresh calls pause. At 75% usage, Ads Uploader waits for Meta's recovery estimate; without an estimate, the wait is 60 seconds, or 300 seconds at 90% usage. Fresh calls also share a concurrency cap of 5 across the app and 1 per account for each read family.

Ads Uploader also has a short-window read safety limit: a burst of 120 calls per user, replenished at 30 calls per minute, and 240 calls per ad account across users, replenished at 60 calls per minute. This is an Ads Uploader limit, not a Meta limit. Async status checks skip concurrency slots but use this safety limit. Existing campaign, ad set and ad lists keep their request burst limit and now respect Meta usage signals. Cached results make no Meta calls.

Edits, previews, applies and copies skip every Ads Uploader read limit, pause and budget, and go straight to Meta; only one apply runs at a time per ad account. They stop only when Meta itself throttles a request (Meta error 4, 17, 32, 613 or 80000-80014, or HTTP 429). That stop says it was Meta, with Meta's code and Meta's own wait, and carries source: "meta", metaCode and metaSubcode; untouched changes stay pending.

Respect Retry-After for META_READS_BUSY, META_READS_BACKING_OFF and READS_GUARD_LIMIT. READS_GUARD_LIMIT replaces META_READS_HOURLY_LIMIT and META_READS_ACCOUNT_LIMIT, which remain legacy aliases for the Ads Uploader safety limit. Never retry in a loop. Cached rows remain available during backoff; ENTITY_METADATA_DEFERRED and COMPARE_DEFERRED mean optional fresh reads were deferred.

Campaign health preserves native result arrays and adds result with its source, metric, value and cost per result. When native results are absent, it uses delivering ad sets' optimization goals; mixed goals remain separate and incomplete coverage is unknown. learningStageCounts is an empty object when no learning information was returned, and null when the ad sets could not be read. Account spend_cap: null with hasSpendCap: false means no cap; CLI tables show None. Major money amounts are numbers; raw minor amounts remain strings.

Edits And Edit Review

Enable edits with Agent Edit on the Account Permissions page: an account-wide default or a per-ad-account setting, which wins. Approval sends changes to the user's Agent Inbox; Allowed applies them directly. Edit commands return reviewUrl, reviewId and proposal IDs on compact items. Share the link with the user and let them select and apply changes on the page. Reuse --review-id <reviewId> on later previews to keep related proposals together. Reviews expire seven days after their last update.

Use edit:status, edit:archive, edit:delete, edit:rename, edit:budget, edit:spend-cap, edit:spend-limits, edit:bid, edit:schedule, edit:targeting, edit:adset-settings and edit:creative with --account and optional --json. ads edit:reviews lists reviews; ads edit:review <reviewId> shows proposal statuses and applied edit IDs. Both support tables and --json. edits, edit:show <editId> and edit:undo <editId> provide edit history, results and undo previews. Undo also goes to Edit Review in review mode.

Review mode queues proposals after batched reads and local checks, with zero Meta POSTs. Meta accepts or rejects each change when the user applies it; results show each item as applied, failed, skipped or pending. Pending means not sent yet, with a reason; apply the same review again to send these changes. Failed proposals retain a safe Meta reason. One ad's failure does not stop other ads; account errors stop the run. Direct previews also skip validate_only for status, archive, delete and rename; other direct edits still validate.

Previews return compact summary, items and grouped warnings by default. Use --detail full to request full change details and any performed validation. Money metadata appears only when money fields change. Calls support 100 objects (50 IDs per bulk operation), and a review holds 200 proposals.

Cancel pending suggestions with ads edits cancel <reviewId> --account act_123 --proposal <id> --proposal <id>. The review ID is required; omit --proposal to cancel every pending suggestion in that review. ads edit:cancel is an alias. Nothing changes in Meta. The command prints one result per suggestion, including refusal reasons and applied edit IDs, followed by “N cancelled, M still pending”. Use --json for the full response. Cancelled suggestions leave your Agent Inbox; in-progress and other non-pending suggestions are refused.

Discard a launch waiting in your Agent Inbox with ads launch:discard <buildId> --account act_123. Nothing is created in Meta and the saved build stays; use ads builds:delete <buildId> to remove it as well. Launches that already started cannot be discarded.

In Apply Directly mode, previews produce a plan that lasts 15 minutes. Show its changes and warnings first. --preview-only stops before applying. Interactive commands ask for approval; non-interactive apply requires --yes and the applicable explicit flags: --confirm-spend, --confirm-live-change, --confirm-irreversible, --allow-large-change. Set these only after the user approves the corresponding change. --yes is not spend approval. edit:apply <planId> refuses with EDITS_REVIEW_REQUIRED if review mode is active, even if the plan was created in direct mode. Follow its review link instead.

Archive and delete cannot be undone. Never retry partial results blindly; inspect edit:show and current Meta values first. Money inputs are major units and edit preview/detail money values are minor units; History rows use readable currency amounts. account:info shows the effective setting and its source. The CLI cannot change settings. See Meta Edits.

Target audiences with ads edit:targeting <adset-id> --add-country CA --remove-interest <id> --age-min 25 --preview-only. List flags are repeatable; use targeting search for IDs. --json-patch <file|-> accepts { "add": {}, "remove": {}, "set": {}, "group": 0 } instead of patch flags. Use --detail full to show per-path differences and the full resulting audience; the default preview summarizes detail paths. Several detailed-targeting groups require an explicit --group index. Unmentioned targeting stays in place, and undo restores the recorded full audience.

ads edit:adset-settings <adset-id> supports --optimization-goal, --billing-event, --promoted-object <json>, repeatable --attribution EVENT_TYPE:days, --incremental-attribution on|off, --dsa-beneficiary, --dsa-payor, or --json-patch <file|->. Standard attribution requires incremental attribution to be off first. Destination type is read-only.

edit:bid supports --strategy LOWEST_COST_WITH_MIN_ROAS and ad set --roas-floor 2.5 for 250%. Minimum ROAS requires VALUE optimization. With a campaign budget, set the strategy on the campaign and floors on its ad sets. Targeting and ad set settings do not count as spend increases, but live-change approval still applies. All new edits use the same Edit Review flow.

Editing An Ad Creative

ads edit:creative <ad-id> changes an existing ad's creative through the usual preview and Edit Review flow. Use --primary-text, --headline, --description, --cta, --link <https-url>, --display-link and --url-tags (without ?). For multi-text creatives use repeatable --body, --title, --desc instead of single-text flags. Duplicate variants are refused. Empty display link or URL tags clears inheritance.

To change an image or video, create a new ad in the Uploader. Text-only edits keep the existing image or video.

--post <pageId_postId> switches to an existing Page post and accepts only --url-tags alongside it. --json-patch <file|-> accepts the API creative fields instead of these flags.

ads edit:creative 12345 --primary-text "Updated primary text" --account act_123 --preview-only
ads edit:creative 12345 --body "First variant" --body "Second variant" --preview-only

Use --detail full for the table of creative field values; the compact default includes a detail-path count and warnings. Old-post likes, comments and shares do not carry over, except when switching to an existing post. Expect Meta review and a possible learning reset. Up to 10 creative edits fit in a plan. Undo reattaches the old creative. Existing-post sources refuse text edits. Plain and placement-customised carousels support primary text, whole-carousel link, CTA, display link and URL tags. Use --json-patch with cards: [{ card: 2, headline: "Updated headline", description: "Updated description" }] for per-card text; card numbers start at 1. Plain carousels also accept per-card link; placement-customised carousels refuse it. Scalar headline and description edits are refused on carousels. Flexible, catalog and partnership creatives point to the Uploader's copy-settings flow. Inspect error.details.orphanCreativeId if creation succeeded but attachment failed; never retry a partial result blindly.

Product Showcase

Product Showcase shows products from a catalog under a single image or video ad. Full catalog (dynamic/DPA) ads are not supported yet.

IDs must be strings of digits. Recommendations require Sales campaigns (OUTCOME_SALES); manual catalog/set choices also work on other objectives. Carousel, flexible, multi-media, existing-post and Partnership ads cannot enable Product Showcase.

Discover catalogs and sets, then request a recommendation:

ads catalogs list --account act_123
ads catalogs product-sets 3390205374500501 --product-set 29588799347389253 --json
ads catalogs recommend --url https://www.asphaltgold.com/collections/nike-sneaker --page 1234 --pixel 5678
ads create:preview spec.json --product-showcase recommended
ads create spec.json --product-showcase on --catalog 3390205374500501 --product-set 29588799347389253
ads create:preview spec.json --product-showcase off

Create and preview accept productShowcase: { enabled?, catalogId?, productSetId?, useRecommendation? }; enabled defaults to true. Omit the field to inherit the source creative. RECOMMENDED selects Meta’s default (choiceSource: "DEFAULT"); MUST_PICK requires a returned candidate ID as productSetId ("MANUAL"). MANUAL requires a catalog and product set. A validated override of Meta’s default uses "MANUAL".

CLI create and preview flags: --product-showcase <on|off|recommended>, --catalog <id>, --product-set <id>. IDs alone mean on; recommended maps to useRecommendation: true. Off maps to { enabled: false } and cannot include IDs; it disables showing products while keeping the source ad’s own set attached.

Preview returns productShowcase with enabled, catalog/set IDs, optional set name, choiceSource and source (recommendation, manual, source_ad), or null when omitted. Recommendations are read-only and do not enable the feature. Test Mode makes no catalog calls, requires explicit catalog/set IDs when enabled and notes that membership/recommendations were not verified. Saved UI choices map to Product Showcase; saved defaults are checked again before launch.