How to Automate Meta Ads Campaign Creation via API
Create and launch Meta ad campaigns programmatically using the Marketing API.
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
- A Meta Business account with a System User
- The System User needs
ads_managementpermission - An ad account ID (format:
act_123456789) - Python 3.8+ with
requestsinstalled - A Facebook Page connected to your ad account
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
- AI in Paid Advertising: The Complete Overview - the full landscape of AI in ads
- Building Your First Automation: A Complete Guide - foundational automation concepts
- The Infrastructure Mindset - why infrastructure thinking matters for ad operations
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