Documentation

Ad Configuration

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

CommandWhat it does
ads loginAuthenticate via browser (opens your default browser)
ads logoutClear stored credentials
ads whoamiShow the currently logged-in user
ads configShow configuration (account, API URL, credentials path)

Browsing

CommandWhat it does
ads accountsList all ad accounts connected to your Meta account
ads account <id>Set a default ad account for future commands
ads campaignsList active campaigns
ads campaigns --status allInclude 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 presetsList your saved API presets
ads presets <id>Show details for a specific preset
ads text-presetsList your saved text presets
ads text-presets <id>Show details for a specific text preset
ads uploadsList recent upload batches
ads uploads <batchId>Show batch details (files, variants, hashes)

Media Upload

CommandWhat it does
ads upload <files...>Upload images and videos to your ad account
ads upload ./directory/Upload an entire directory

Ad Creation

CommandWhat it does
ads create spec.jsonCreate ads from a spec file
ads create:preview spec.jsonDry run showing what would be created
ads create:interactiveGuided wizard (accepts all create flags)

Job Management

CommandWhat it does
ads jobs <jobId>Check the status of a job
ads jobs <jobId> --followStream 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.

FlagDescription
--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)
--text-file <path>Load text configuration from a JSON file

Browse Flags

These flags are available on campaigns, adsets, adset, and campaign:

FlagDescription
--status <status>active (default) or all
--inactiveShorthand for --status all (on campaigns)
--search <text>Filter by name (on campaigns, adsets)

Common Flags

FlagDescription
--account <id>Override default ad account for any command
--jsonOutput raw JSON (available on most commands, intended for scripting)

Spec File Format

The JSON spec file controls every aspect of ad creation. Only two fields are required: a template source (adPresetId or copyFromAd) and uploadId.

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.

FieldDescription
adPresetIdA saved API preset ID. Locks in the campaign, ad set, and ad config.
copyFromAdA Facebook ad ID to copy settings from.

When using copyFromAd, provide the upload batch and optionally 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>.

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" }
    ]
  }
}
ModeBehavior
"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 or bid amount on new ad sets. Values are in your account's currency units (e.g. 50 for $50 or 50 euros). Only one can be set at a time.

{ "adSet": { "dailyBudget": 50 } }
{ "adSet": { "bidAmount": 5 } }

Also available as CLI flags: --daily-budget 50 or --bid-amount 5.

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.

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.

A top-level CTA applies to all ads. Per-ad CTAs in texts.perAd 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

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.

CTARequired Campaign Objective
MESSAGE_PAGEMessenger destination
WHATSAPP_MESSAGEWhatsApp destination
INSTAGRAM_MESSAGEInstagram DM destination
CALL_NOWCall campaign

Creative Enhancements

Control Advantage+ creative enhancements:

{ "creativeEnhancements": "none" }
ValueEffect
"metaDefaults"Let Meta decide (default if omitted)
"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, image_brightness_and_contrast, image_touchups, video_auto_crop, video_filtering, image_animation, image_templates, adapt_to_placement, product_extensions, description_automation, add_text_overlay, music, carousel_to_video, carousel_dynamic_description, multi_share_end_card, multi_share_optimized

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.

Group uploaded files into carousel ads with per-card text:

{
  "carousel": [
    {
      "name": "My Carousel",
      "cards": ["slide1.jpg", "slide2.jpg", "slide3.jpg"],
      "cardTexts": [
        { "headline": "Slide 1", "description": "First card", "link": "https://example.com/1" },
        { "headline": "Slide 2", "description": "Second card", "link": "https://example.com/2" }
      ]
    }
  ]
}

Cards must reference filenames from the upload batch. 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.

Ad Naming

Customize how your ads are named:

{ "adNamePattern": "{filename} - {date}" }
PlaceholderWhat 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"
    }
  }
}
FieldValuesDescription
status"PAUSED", "ACTIVE"Ad launch status (default: ACTIVE)
pauseAt"ad", "adSet", "campaign"Which level to pause at (default: ad)
schedule.startTimeISO 8601 stringScheduled start time (uses ad account timezone)
schedule.endTimeISO 8601 stringScheduled 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_4x5.jpg + hero_9x16.jpg + hero_16x9.jpg are grouped as one variant ad.

Word suffixes: hero.jpg + hero_vertical.jpg + hero_horizontal.jpg are grouped as one variant ad.

Both _ and - delimiters work. A file without a ratio suffix (e.g. hero.jpg) only groups with _vertical/_horizontal variants. For ratio-based grouping, all files need ratio suffixes.

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"
}

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

  1. Always preview first. create:preview catches config errors before touching Facebook.
  2. Ads are active by default. Use --status PAUSED or "status": "PAUSED" in the spec to create them paused.
  3. uploadId comes from the upload output. It is the batch ID returned by ads upload.
  4. 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.
  5. copyFromAd needs uploadId. You must provide the upload batch. Optionally provide campaign.id and adSet.id to control placement.
  6. Per-ad text keys are filenames. Use "hero.jpg", not "/path/to/hero.jpg".
  7. textPresetId and texts are mutually exclusive. Use one or the other, not both.
  8. Objective-specific CTAs are inherited from the template. Do not set MESSAGE_PAGE, WHATSAPP_MESSAGE, etc. manually.