Systems Library / Marketing Automation / How to Automate Meta Ads Campaign Creation via API
Marketing Automation paid advertising

How to Automate Meta Ads Campaign Creation via API

Create and launch Meta ad campaigns programmatically using the Marketing API.

Jay Banlasan

Jay Banlasan

The AI Systems Guy

When you automate Meta Ads campaign creation through the API, you go from spending an hour clicking through Ads Manager to running a single script. I use this for every new campaign launch. Define the structure in a config file, run the script, and the campaign is live in under a minute.

The Marketing API is well documented but has sharp edges. This walkthrough covers the exact flow that works in production.

What You Need Before Starting

Step 1: Create the Campaign

import requests
import os
from dotenv import load_dotenv

load_dotenv()

TOKEN = os.getenv("META_ACCESS_TOKEN")
AD_ACCOUNT = os.getenv("META_AD_ACCOUNT_ID")
API_VERSION = "v19.0"
BASE_URL = f"https://graph.facebook.com/{API_VERSION}"

def create_campaign(name, objective="OUTCOME_LEADS", status="PAUSED"):
    resp = requests.post(f"{BASE_URL}/{AD_ACCOUNT}/campaigns", params={
        "access_token": TOKEN,
        "name": name,
        "objective": objective,
        "status": status,
        "special_ad_categories": "[]",
    })
    data = resp.json()
    if "id" in data:
        print(f"Campaign created: {data['id']}")
        return data["id"]
    else:
        print(f"Error: {data}")
        return None

Step 2: Create the Ad Set

import json

def create_adset(campaign_id, name, daily_budget, targeting, pixel_id, optimization_goal="LEAD_GENERATION"):
    resp = requests.post(f"{BASE_URL}/{AD_ACCOUNT}/adsets", params={
        "access_token": TOKEN,
        "campaign_id": campaign_id,
        "name": name,
        "daily_budget": int(daily_budget * 100),  # Meta uses cents
        "billing_event": "IMPRESSIONS",
        "optimization_goal": optimization_goal,
        "bid_strategy": "LOWEST_COST_WITHOUT_CAP",
        "targeting": json.dumps(targeting),
        "promoted_object": json.dumps({"pixel_id": pixel_id, "custom_event_type": "LEAD"}),
        "status": "PAUSED",
    })
    data = resp.json()
    if "id" in data:
        print(f"Ad Set created: {data['id']}")
        return data["id"]
    else:
        print(f"Error: {data}")
        return None

Step 3: Build Your Targeting Object

def build_targeting(age_min=25, age_max=55, genders=None, countries=None, interests=None):
    targeting = {
        "age_min": age_min,
        "age_max": age_max,
        "geo_locations": {
            "countries": countries or ["GB"],
            "location_types": ["home", "recent"]
        },
    }
    
    if genders:
        targeting["genders"] = genders  # [1] = male, [2] = female
    
    if interests:
        targeting["flexible_spec"] = [{"interests": interests}]
    
    return targeting

# Example: Broad targeting in the UK
broad_targeting = build_targeting(age_min=28, age_max=55, countries=["GB"])

# Example: Interest-based targeting
interest_targeting = build_targeting(
    age_min=28, age_max=55, countries=["GB"],
    interests=[{"id": "6003139266461", "name": "Small business"}]
)

Step 4: Create the Ad Creative and Ad

def create_ad_creative(page_id, headline, primary_text, link_url, image_hash):
    resp = requests.post(f"{BASE_URL}/{AD_ACCOUNT}/adcreatives", params={
        "access_token": TOKEN,
        "name": f"Creative - {headline}",
        "object_story_spec": json.dumps({
            "page_id": page_id,
            "link_data": {
                "message": primary_text,
                "link": link_url,
                "name": headline,
                "image_hash": image_hash,
                "call_to_action": {"type": "LEARN_MORE"},
            }
        }),
    })
    return resp.json().get("id")

def create_ad(adset_id, creative_id, name):
    resp = requests.post(f"{BASE_URL}/{AD_ACCOUNT}/ads", params={
        "access_token": TOKEN,
        "adset_id": adset_id,
        "creative": json.dumps({"creative_id": creative_id}),
        "name": name,
        "status": "PAUSED",
    })
    return resp.json().get("id")

Step 5: Launch the Full Campaign

def launch_campaign(config):
    campaign_id = create_campaign(config["campaign_name"])
    if not campaign_id:
        return
    
    for adset_config in config["adsets"]:
        targeting = build_targeting(**adset_config["targeting"])
        adset_id = create_adset(
            campaign_id, adset_config["name"],
            adset_config["daily_budget"], targeting,
            config["pixel_id"]
        )
        if not adset_id:
            continue
        
        for ad_config in adset_config["ads"]:
            creative_id = create_ad_creative(
                config["page_id"], ad_config["headline"],
                ad_config["primary_text"], config["link_url"],
                ad_config["image_hash"]
            )
            create_ad(adset_id, creative_id, ad_config["name"])
    
    print(f"Campaign {config['campaign_name']} ready. Set to ACTIVE when ready to launch.")

Everything launches paused. Review in Ads Manager, then flip to active. Never launch directly to active through the API until you have validated the system dozens of times.

What to Build Next

Add a naming convention enforcer that validates campaign, ad set, and ad names against your standard before creating. Then build a template library so launching common campaign types is a single config file swap.

Related Reading

Want this system built for your business?

Get a free assessment. We will map every system your business needs and show you the ROI.

Get Your Free Assessment

Related Systems