CLI Full Reference
This is the complete reference for the Ads Uploader CLI. For an introduction and getting started guide, see CLI Configuration.
Commands
Authentication
| Command | What it does |
|---|---|
ads login | Authenticate via browser (opens your default browser) |
ads logout | Clear stored credentials |
ads whoami | Show the currently logged-in user |
ads config | Show configuration (account, API URL, credentials path) |
Browsing
| Command | What it does |
|---|---|
ads accounts | List all ad accounts connected to your Meta account |
ads account <id> | Set a default ad account for future commands |
ads pages | List Facebook Pages you can advertise with, including any linked Instagram account, for use with profile overrides |
ads targeting:search "Austin" --type city | Find city keys for ad set targeting |
ads targeting:search "advertising" --type detailed | Find detailed-targeting IDs and types |
ads campaigns | List active campaigns |
ads campaigns --status all | Include paused and archived campaigns |
ads campaigns --search "text" | Filter campaigns by name |
ads campaign <id> | Show the ad sets inside a campaign |
ads adsets --campaign <id> | List ad sets in a campaign (supports --search, --status) |
ads adset <id> | Show the ads inside an ad set |
ads ad <id> | View full ad details including creative settings |
ads presets | List your saved API presets |
ads presets <id> | Show details for a specific preset |
ads presets:save --from-ad <adId> --name "Preset Name" | Save an existing ad as an API preset |
ads text-presets | List your saved text presets |
ads text-presets <id> | Show details for a specific text preset |
ads uploads | List recent upload batches |
ads uploads <batchId> | Show batch details (files, variants, hashes) |
Media Upload
| Command | What it does |
|---|---|
ads upload <inputs...> | Upload local paths and public HTTPS URLs to your ad account |
ads upload ./directory/ | Upload an entire directory |
ads upload:drive <folderUrl> | Import a public Google Drive folder as a background job |
HTTPS arguments, including public Google Drive file links, are detected automatically. You can mix local paths and URLs: local files are uploaded first, then the server imports URLs into the same batch so grouping works across every input. Public Drive folders must be shared as Anyone with the link (Viewer).
Local files are staged in parallel, and transient network failures are retried automatically with backoff. URL and Drive-folder imports use bounded parallel file pipelines in a background job while the CLI displays a completed/total counter and every active file. You rarely need to touch these flags, but they're available:
| Upload flag | Description |
|---|---|
--concurrency <n> | Number of files staged in parallel, 1-6 (default: 4). Large videos are throttled automatically to stay within memory. |
--upload-timeout <ms> | Per-file upload timeout (default: 120000) |
--api-timeout <ms> | API request timeout in milliseconds (default: 60000) |
Ad Creation
| Command | What it does |
|---|---|
ads create spec.json | Create ads from a spec file |
ads create:preview spec.json | Dry run showing what would be created |
ads create:interactive | Guided wizard (accepts all create flags) |
Job Management
| Command | What it does |
|---|---|
ads jobs <jobId> | Check the status of a job |
ads jobs <jobId> --follow | Stream live progress updates |
ads jobs cancel <jobId> | Cancel a running job |
Create Flags
These flags apply to ads create, ads create:preview, and ads create:interactive. They can be used instead of or alongside a spec file.
| Flag | Description |
|---|---|
--account <id> | Override default ad account |
--preset <id> | Use a saved API preset (alternative to spec file) |
--text-preset <id> | Load a saved text preset |
--copy-from <adId> | Copy settings from an existing ad |
--upload <batchId> | Specify the upload batch ID |
--status <PAUSED|ACTIVE> | Set ad status (default: ACTIVE) |
--pause-at <level> | Pause level: ad (default), adSet, or campaign |
--daily-budget <amount> | Override daily budget per ad set (currency units, e.g. 50 for $50) |
--bid-amount <amount> | Override bid/cost cap per ad set (currency units) |
--campaign-daily-budget <amount> | Set a CBO daily campaign budget in whole currency units. Mutually exclusive with the lifetime budget; omit both to inherit the source campaign's budget. |
--campaign-lifetime-budget <amount> | Set a CBO lifetime campaign budget in whole currency units. Mutually exclusive with the daily budget; omit both to inherit the source campaign's budget. |
--adset-min-spend <amount> | Set the ad-set minimum spend under CBO in whole currency units; 0 removes the limit inherited from the source ad set. |
--adset-max-spend <amount> | Set the ad-set maximum spend under CBO in whole currency units; 0 removes the limit inherited from the source ad set. |
--adset-min-spend-pct <5-100> | Set the ad-set minimum spend as a percentage of the campaign budget, in 5% steps. Also works with an existing CBO campaign; mutually exclusive with --adset-min-spend. |
--adset-max-spend-pct <5-100> | Set the ad-set maximum spend as a percentage of the campaign budget, in 5% steps. Also works with an existing CBO campaign; mutually exclusive with --adset-max-spend. |
--location <ISO> | Target a country by two-letter ISO code. Repeat the flag for multiple countries. |
--age-min <n> | Minimum age, from 13 to 65 |
--age-max <n> | Maximum age, from 13 to 65 (65 means 65+) |
--gender <gender> | all, men, or women. all clears a gender restriction inherited from the source ad set. |
--ai-disclosure | Self-disclose AI-generated creative content (Meta AI-content transparency). Off by default. |
--page <id> | Use this Facebook Page instead of the template's (see Profile Options) |
--instagram <id> | Use this Instagram account instead of the template's |
--use-page-identity | Use the Facebook Page as the Instagram identity; cannot be combined with --instagram |
--threads <id> | Use this Threads profile instead of the template's |
--text-file <path> | Load text configuration from a JSON file |
--expanded | Show full headline, primary text, and description values in previews |
Browse Flags
These flags are available on campaigns, adsets, adset, and campaign:
| Flag | Description |
|---|---|
--status <status> | active (default) or all |
--inactive | Shorthand for --status all (on campaigns) |
--search <text> | Filter by name (on campaigns, adsets) |
Targeting Search
City targeting and detailed targeting use Meta identifiers rather than names. Search through Ads Uploader to get values that can be pasted into a spec:
ads targeting:search "Austin" --type city
ads targeting:search "advertising" --type detailed
| Flag | Description |
|---|---|
--type <type> | Required. city returns targeting.cities[].key values; detailed returns entries for targeting.detailedTargetingGroups. |
--account <id> | Override the configured default ad account |
--limit <n> | Return 1-25 matches |
--json | Return pasteable structured results |
Detailed-targeting results include id, name, type, an audience-size range, and the category path. The type identifies the detailed-targeting category, such as interests, behaviors, work_employers, or work_positions. Always search for these identifiers; never guess them.
Detail Flags
These flags are available on ad:
| Flag | Description |
|---|---|
--expanded | Show full headline, primary text, and description values |
Common Flags
| Flag | Description |
|---|---|
--account <id> | Override default ad account for any command |
--json | Output raw JSON (available on most commands, intended for scripting) |
Spec File Format
The JSON spec file controls every aspect of ad creation. Provide a template source (adPresetId or copyFromAd) plus either uploadId or captured mediaItems with a Facebook mediaHash for every image and mediaId for every video. Standard, carousel, flexible, and placement videos also need thumbnailHash; Multi Media videos use their captured public thumbnail URL.
Minimal Spec
{
"adPresetId": "your_preset_id",
"uploadId": "batch_abc123"
}
Full Example
{
"adPresetId": "preset_id_here",
"uploadId": "batch_abc123",
"adSet": {
"name": "My New Ad Set",
"dailyBudget": 50
},
"adNamePattern": "{filename}",
"texts": {
"perAd": {
"hero.jpg": {
"headlines": ["Main Headline"],
"bodies": ["Ad copy here."],
"descriptions": ["Short description"],
"cta": "SHOP_NOW",
"link": "https://example.com/landing"
}
}
},
"creativeEnhancements": "none",
"options": {
"status": "PAUSED",
"pauseAt": "adSet"
}
}
Template Source
You need one of these to tell the CLI which ad configuration to use as a base.
| Field | Description |
|---|---|
adPresetId | A saved API preset ID. Locks in the campaign, ad set, and ad config. |
copyFromAd | A Facebook ad ID to copy settings from. |
When using copyFromAd, provide the upload batch, or launch a saved web build whose captured images have mediaHash and videos have mediaId. You can optionally set the campaign and ad set:
{
"copyFromAd": "120233848667930472",
"uploadId": "batch_abc123",
"campaign": { "id": "120233848666410472" },
"adSet": { "id": "120233848666620472" }
}
To find the right ad ID, drill through your account: ads campaigns then ads campaign <id> then ads adset <id> then ads ad <id>.
Profile Options
By default, new ads inherit the Facebook Page, Instagram account, and Threads profile from the template ad or preset. This is the same Profile Options control available via the Defaults panel in the web app. Override any of them with a profile block (or the --page / --instagram / --use-page-identity / --threads flags, which take precedence over the spec file):
{
"copyFromAd": "120233848667930472",
"uploadId": "batch_abc123",
"profile": {
"pageId": "123456789012345",
"instagramId": "17841400000000000",
"threadsId": "987654321098765"
}
}
Run ads pages to list the Page IDs you can use, along with each Page's linked Instagram account.
If you override only the Page, Ads Uploader automatically uses its linked Instagram account. If no Instagram account is linked, it uses the Facebook Page as the Instagram identity. Threads still resets because it may belong to the old Page; set it explicitly when needed.
For an explicit Page-actor choice, use --use-page-identity or set "useFacebookPage": true inside profile. Do not combine it with --instagram or instagramId. You can also change just the Instagram or Threads profile without touching the Page by setting only those fields.
For multi-campaign launches, assign identities independently with profile.campaigns. Key each override by the matching campaign id (recommended) or by an unambiguous campaign name:
{
"campaign": {
"mode": "duplicate",
"campaigns": [
{ "id": "prospecting", "name": "Prospecting" },
{ "id": "retargeting", "name": "Retargeting" }
]
},
"profile": {
"campaigns": {
"prospecting": { "pageId": "page_1", "instagramId": "ig_1", "threadsId": null },
"retargeting": { "pageId": "page_2", "useFacebookPage": true, "threadsId": "threads_2" }
}
}
}
Every profile.campaigns key must match a campaign in campaign.campaigns; unmatched or ambiguous keys are rejected before launch.
Campaign Structure
By default, ads go into the template ad's campaign. You can create a new campaign by providing campaign.name.
For multi-campaign modes, use campaign.mode with a campaigns array:
{
"campaign": {
"mode": "duplicate",
"campaigns": [
{ "name": "Campaign A" },
{ "name": "Campaign B" }
]
}
}
| Mode | Behavior |
|---|---|
"single" | Default. One campaign. |
"duplicate" | All media is duplicated into each campaign. |
"split" | Media is split evenly across campaigns. |
Ad Set Modes
By default, ads go into the template ad's existing ad set. The following modes give you control over how ads are distributed across ad sets.
Create a new ad set:
{ "adSet": { "name": "My Ad Set" } }
Use an existing ad set by ID:
{ "adSet": { "id": "120233848666620472" } }
One ad set per uploaded file:
{ "adSet": { "mode": "perUpload" } }
Auto-group into ad sets of a fixed size:
{ "adSet": { "mode": "autoGroup", "adsPerAdSet": 5 } }
Custom groups with full control over which files go where:
{
"adSet": {
"groups": [
{ "name": "Images - April 10", "media": ["hero.jpg", "banner.jpg"] },
{ "name": "Videos - April 10", "media": ["promo.mp4"] }
]
}
}
Ad set naming pattern for multi-ad-set modes:
{ "adSet": { "mode": "perUpload", "namePattern": "Ad Set {index:01}" } }
Variant grouping groups ads by variation identifier into the same ad set:
{ "adSet": { "mode": "autoGroup", "groupVariations": true, "variationIdentifier": "-" } }
Budget and Bid Override
Override the daily budget and/or bid amount on new ad sets. Values are in your account's currency units (e.g. 50 for $50 or 50 euros).
dailyBudget and bidAmount are independent Meta fields:
- ABO campaigns (budget lives on the ad set): you can set
dailyBudget,bidAmount, or both. Bid-cap strategies likeCOST_CAP,LOWEST_COST_WITH_BID_CAP, andTARGET_COSTrequire abidAmountalongside the budget. - CBO campaigns (budget lives on the campaign): don't set
dailyBudgeton the ad set. Setcampaign.dailyBudgetorcampaign.lifetimeBudget(mutually exclusive) to override the source campaign's budget, or omit both to inherit it. Ad-set minimum and maximum spend limits can use whole-unit amounts or 5-100% of the campaign budget in 5% steps; percentage limits also work with an existing CBO campaign. For bid-cap strategies, set onlybidAmounton the ad set.
{ "adSet": { "dailyBudget": 50 } }
{ "adSet": { "bidAmount": 5 } }
{ "adSet": { "dailyBudget": 50, "bidAmount": 5 } }
Also available as CLI flags: --daily-budget 50, --bid-amount 5, --campaign-daily-budget 100, --campaign-lifetime-budget 1000, --adset-min-spend 20, --adset-max-spend 80, --adset-min-spend-pct 20, and --adset-max-spend-pct 80.
Ad Set Targeting
Targeting applies to new ad sets. Omit the targeting block to inherit the source ad set's audience unchanged.
{
"targeting": {
"countries": ["US"],
"cities": [
{
"key": "2525495",
"name": "Austin",
"region": "Texas",
"countryCode": "US",
"radius": 25,
"distance_unit": "mile"
}
],
"selectionVersion": 4,
"ageSelected": true,
"ageMin": 21,
"ageMax": 55,
"genderSelected": true,
"genders": "women",
"detailedTargetingSelected": true,
"detailedTargetingGroups": [[
{
"id": "112002898811624",
"name": "Advertising agency",
"type": "work_employers"
}
]]
}
}
Use ads targeting:search to find the city key and each detailed selection's id, name, and type. Omit both countries and cities to inherit the source locations. Countries and cities are combined as alternatives, so adding Austin to countries: ["US"] still targets the whole United States; omit countries to target only the city.
Text Configuration
Common text applies the same copy to all ads:
{
"texts": {
"common": {
"headlines": ["Headline 1", "Headline 2"],
"bodies": ["Primary text"],
"descriptions": ["Description"]
},
"strategy": "flexible"
}
}
Per-ad text lets you set unique copy for each file:
{
"texts": {
"perAd": {
"hero.jpg": {
"headlines": ["Hero Headline"],
"bodies": ["Hero copy"],
"descriptions": ["Hero desc"],
"cta": "LEARN_MORE",
"link": "https://example.com/hero",
"urlTags": "utm_content=hero"
},
"banner.jpg": {
"headlines": ["Banner Headline"],
"bodies": ["Banner copy"]
}
}
}
}
Per-ad keys are filenames (not full paths). Each entry supports: headlines, bodies, descriptions, cta, link, displayUrl, urlTags. Fields you don't specify inherit from the template ad.
Per-ad-set text applies one text block to every ad in a destination ad set. Use a plain ad set name/ID when it is unique, or a campaign::key entry when the same ad set name appears in more than one campaign:
{
"texts": {
"perAdset": {
"Prospecting::prospecting_set": {
"headlines": ["Prospecting headline"],
"bodies": ["Prospecting copy"],
"descriptions": ["Prospecting description"],
"cta": "SHOP_NOW",
"link": "https://example.com/prospecting",
"displayUrl": "example.com/prospecting",
"urlTags": "utm_campaign=prospecting",
"aiDisclosure": false
}
}
}
}
Every texts.perAdset key must match a planned ad set. Unmatched keys are rejected instead of falling back to common text.
Text presets let you load a saved text configuration:
{ "textPresetId": "preset_id_here" }
You cannot combine textPresetId with texts.
Strategy options control how multiple text variations are handled:
"flexible"(default) lets Meta optimize across your text variations. Multiple headlines and bodies become options that Facebook mixes and matches."separate"creates a separate ad for each text combination.
CTA and Links
A top-level CTA applies to all ads. Per-ad CTAs in texts.perAd and per-ad-set CTAs in texts.perAdset override it.
{
"cta": {
"type": "SHOP_NOW",
"link": "https://example.com",
"displayUrl": "example.com"
},
"urlTags": "utm_source=facebook&utm_medium=paid"
}
Standard CTA types: LEARN_MORE, SHOP_NOW, SIGN_UP, SUBSCRIBE, GET_OFFER, CONTACT_US, DOWNLOAD, ORDER_NOW, BUY_NOW, BOOK_NOW, APPLY_NOW, GET_QUOTE, GET_IN_TOUCH, WATCH_MORE, PLAY_GAME
Objective-specific CTAs are inherited from the template ad and should not be set manually. Setting these on the wrong campaign type will cause a Facebook API error.
| CTA | Required Campaign Objective |
|---|---|
MESSAGE_PAGE | Messenger destination |
WHATSAPP_MESSAGE | WhatsApp destination |
INSTAGRAM_MESSAGE | Instagram DM destination |
CALL_NOW | Call campaign |
URL Split-Test (Split Destination)
Provide 2–5 destination URLs under texts.urlVariants and every generated ad set is duplicated once per URL so Meta optimizes each ad and landing-page combination independently.
{
"texts": {
"common": { "headlines": ["Hero"], "bodies": ["Copy"] },
"urlVariants": [
{ "link": "https://example.com/homepage", "label": "homepage" },
{ "link": "https://example.com/quiz", "label": "quiz-v2" }
]
}
}
labelis optional. When omitted, the last URL path slug is used (/quiz-v2→quiz-v2), falling back to the hostname for root URLs.- In single-ad-set mode, each variant's label becomes the full ad set name.
- In
perUploadandautoGroupmodes, each duplicated ad set's name appends_{label}to the pattern (or substitutes a{destination}token if you include one). - The
{date}token in a label resolves to today's date (e.g.,launch-{date}→launch-2026-04-27). - Each destination URL must be unique. Identical URLs (or trailing-slash / case variants of the same URL) collapse to one entry, so make sure you have at least 2 distinct destinations.
- Your ad set budget is multiplied by the number of variants, since each duplicate is its own ad set.
- Not compatible with special-destination source ads (lead form, Messenger, WhatsApp, Instagram DM, Call). The CLI rejects this combination with a clear error - those formats don't route via
cta.link. - Per-ad
linkoverrides intexts.perAdlose to the variant URL when both are set.
Creative Enhancements
Control Advantage+ creative enhancements:
{ "creativeEnhancements": "none" }
| Value | Effect |
|---|---|
| omitted | All features off |
"metaDefaults" | Deprecated alias for "none" |
"all" | All features on |
"none" | All features off |
["feature1", "feature2"] | Only listed features on, rest off |
Available features: text_translation, inline_comment, enhance_cta, text_optimizations, reveal_details_over_time, show_destination_blurbs, image_brightness_and_contrast, image_touchups, video_auto_crop, video_filtering, pac_relaxation, image_animation, image_templates, adapt_to_placement, product_extensions, product_tags, description_automation, add_text_overlay, music, carousel_to_video, carousel_dynamic_description, multi_share_end_card, multi_share_optimized
The show_destination_blurbs key is displayed as Show spotlights, and pac_relaxation as Flex media.
When cherry-picking features, only list ones relevant to the media type. Video features (video_auto_crop, video_filtering) only apply to video ads. Carousel features (carousel_to_video, carousel_dynamic_description, multi_share_end_card, multi_share_optimized) only apply to carousel ads.
product_tags applies to image and video ads and is clone-only. It can be enabled only when the source ad has an associated catalog and explicit positioned product tags; all source tags and their positions are preserved. "all" includes it only for an eligible source and never invents a product tag.
Carousel Ads
Group uploaded files into carousel ads with per-card text and optional overall carousel text. cardTexts controls individual cards. Overall carousel text can be colocated on the carousel object or set in texts.perAd using the carousel name; colocated fields win when both are present.
{
"carousel": [
{
"name": "My Carousel",
"cards": ["slide1.jpg", "slide2.jpg", "slide3.jpg"],
"headlines": ["Overall Carousel Headline"],
"bodies": ["Overall primary text"],
"descriptions": ["Overall description"],
"cta": "SHOP_NOW",
"link": "https://example.com/carousel",
"urlTags": "utm_content=my_carousel",
"cardTexts": [
{ "headline": "Slide 1", "description": "First card", "link": "https://example.com/1" },
{ "headline": "Slide 2", "description": "Second card", "link": "https://example.com/2" }
]
}
]
}
Alternative overall-text shape:
{
"texts": {
"perAd": {
"My Carousel": {
"headlines": ["Overall Carousel Headline"],
"bodies": ["Overall primary text"],
"descriptions": ["Overall description"],
"cta": "SHOP_NOW",
"link": "https://example.com/carousel",
"urlTags": "utm_content=my_carousel"
}
}
},
"carousel": [
{
"name": "My Carousel",
"cards": ["slide1.jpg", "slide2.jpg", "slide3.jpg"]
}
]
}
Cards must reference filenames from the upload batch or captured saved-build media. Minimum 2 cards per carousel. Files claimed by a carousel are removed from the standard ad list.
Flexible Ads
Group multiple assets into a single flexible ad where Meta picks the best asset per placement:
{
"flexible": [
{
"name": "Multi-Asset Ad",
"assets": ["hero.jpg", "promo.mp4", "banner.jpg"]
}
]
}
Minimum 2 assets per group. Files claimed by a flexible group are removed from the standard ad list.
Multi Media Ads
Group 2-10 uploaded images or videos into a Meta Multi Media ad:
{
"multimedia": [
{
"name": "Mixed Media Ad",
"assets": ["hero.jpg", "promo.mp4", "banner.jpg"],
"assetTexts": [
{
"headline": "Hero headline",
"primaryText": "Hero primary text",
"description": "Hero description",
"link": "https://example.com/hero",
"displayUrl": "example.com/hero"
}
]
}
]
}
Assets must reference filenames from the upload batch or captured saved-build media. assetTexts is optional and lines up with assets by index; each field is a single override for that asset and blank fields fall back to the ad's main text and URLs. The primary rendered asset uses the ad's main text and URL, and any primary asset text override becomes the first main text option. Videos need a captured cached public thumbnail URL. Files claimed by a Multi Media group are removed from the standard ad list.
Ad Naming
Customize how your ads are named:
{ "adNamePattern": "{filename} - {date}" }
| Placeholder | What it inserts |
|---|---|
{filename} | Original filename without extension |
{index:01} | Zero-padded index (01, 02, 03...) |
{variation} | Variation identifier if variant grouping is on |
{campaign} | Campaign name |
{date} | Current date (YYYY-MM-DD) |
{date:short} | Short date (MM-DD) |
{timestamp} | Unix timestamp |
Options
{
"options": {
"status": "PAUSED",
"pauseAt": "adSet",
"schedule": {
"startTime": "2026-04-01T09:00:00",
"endTime": "2026-04-30T23:59:59"
}
}
}
| Field | Values | Description |
|---|---|---|
status | "PAUSED", "ACTIVE" | Ad launch status (default: ACTIVE) |
pauseAt | "ad", "adSet", "campaign" | Which level to pause at (default: ad) |
schedule.startTime | ISO 8601 string | Scheduled start time (uses ad account timezone) |
schedule.endTime | ISO 8601 string | Scheduled end time (optional) |
Upload and Variant Detection
Variant groups are detected automatically from filename conventions, just like in the web application. See Aspect Ratio Variations for full details on naming conventions.
Ratio suffixes: hero_1x1.jpg + hero_4x5.jpg + hero_9x16.jpg + hero_16x9.jpg + hero_1.91x1.jpg are grouped as one variant ad. Up to 5 ratios per group.
Token position: the ratio token can appear at the end (hero_4x5.jpg), in the middle (hero_4x5_v2.jpg), or at the start (4x5_hero.jpg).
Legacy word suffixes: hero.jpg + hero_vertical.jpg + hero_horizontal.jpg still work and map to 9x16 and 16x9.
The default delimiter is _. You can change it (or accept multiple) in Account > Defaults > Placements > Filename Separator.
Common Patterns
Upload and create with a preset
ads upload ./creatives/hero.jpg ./creatives/banner.jpg
ads create:preview spec.json
ads create spec.json
Where spec.json contains:
{ "adPresetId": "PRESET_ID", "uploadId": "BATCH_ID" }
Copy settings from an existing ad
Browse your account to find the ad:
ads campaigns
ads campaign 120233848666410472
ads adset 120233848666620472
ads ad 120233848667930472
Then create a spec referencing it:
{
"copyFromAd": "120233848667930472",
"uploadId": "BATCH_ID"
}
The source ad must have inline creative settings. If it was built from an existing Page post, the CLI will reject it before submitting a create request.
Save an API preset from an existing ad
ads presets:save --from-ad 120233848667930472 --name "Spring Purchase Template"
ads create --preset PRESET_ID --upload BATCH_ID
This saves the same API preset shape used by the web app. The source ad must have inline creative settings; Page-post-backed ads cannot be saved as API presets.
Per-ad text with unique copy per file
{
"adPresetId": "PRESET_ID",
"uploadId": "BATCH_ID",
"texts": {
"perAd": {
"hero.jpg": {
"headlines": ["Summer Sale Now On"],
"bodies": ["Save up to 50% on all items"],
"cta": "SHOP_NOW",
"link": "https://example.com/summer"
},
"banner.jpg": {
"headlines": ["New Collection Available"],
"bodies": ["Browse our latest styles"],
"cta": "LEARN_MORE",
"link": "https://example.com/new"
}
}
}
}
Auto-group into multiple ad sets
{
"adPresetId": "PRESET_ID",
"uploadId": "BATCH_ID",
"adSet": { "mode": "autoGroup", "adsPerAdSet": 3 }
}
Important Notes
- Always preview first.
create:previewcatches config errors before touching Facebook. - Ads are active by default. Use
--status PAUSEDor"status": "PAUSED"in the spec to create them paused. uploadIdcomes from the upload output. It is the batch ID returned byads upload.- Uploads are tied to an ad account. Files are uploaded directly to the selected account's Facebook media library. The batch ID can only be used with the same account.
copyFromAdneeds resolvable media. ProvideuploadId, or launch a saved build whose captured images havemediaHashand videos havemediaId. Optionally providecampaign.idandadSet.idto control placement.- Per-ad text keys are filenames. Use
"hero.jpg", not"/path/to/hero.jpg". textPresetIdandtextsare mutually exclusive. Use one or the other, not both.- Objective-specific CTAs are inherited from the template. Do not set
MESSAGE_PAGE,WHATSAPP_MESSAGE, etc. manually.