@mcp_server.tool()
@meta_api_tool
async def update_ad_creative(
creative_id: str,
access_token: Optional[str] = None,
name: Optional[str] = None,
message: Optional[str] = None,
headline: Optional[str] = None,
headlines: Optional[List[str]] = None,
description: Optional[str] = None,
descriptions: Optional[List[str]] = None,
dynamic_creative_spec: Optional[Dict[str, Any]] = None,
call_to_action_type: Optional[str] = None,
lead_gen_form_id: Optional[str] = None
) -> str:
"""
Update an existing ad creative with new content or settings.
Args:
creative_id: Meta Ads creative ID to update
access_token: Meta API access token (optional - will use cached token if not provided)
name: New creative name
message: New ad copy/text
headline: Single headline for simple ads (cannot be used with headlines)
headlines: New list of headlines for dynamic creative testing (cannot be used with headline)
description: Single description for simple ads (cannot be used with descriptions)
descriptions: New list of descriptions for dynamic creative testing (cannot be used with description)
dynamic_creative_spec: New dynamic creative optimization settings
call_to_action_type: New call to action button type
lead_gen_form_id: Lead generation form ID for lead generation campaigns. Required when using
lead generation CTAs like 'SIGN_UP', 'GET_OFFER', 'SUBSCRIBE', etc.
Returns:
JSON response with updated creative details
"""
# Check required parameters
if not creative_id:
return json.dumps({"error": "No creative ID provided"}, indent=2)
# Validate headline/description parameters - cannot mix simple and complex
if headline and headlines:
return json.dumps({"error": "Cannot specify both 'headline' and 'headlines'. Use 'headline' for single headline or 'headlines' for multiple."}, indent=2)
if description and descriptions:
return json.dumps({"error": "Cannot specify both 'description' and 'descriptions'. Use 'description' for single description or 'descriptions' for multiple."}, indent=2)
# Validate dynamic creative parameters (plural forms only)
if headlines:
if len(headlines) > 5:
return json.dumps({"error": "Maximum 5 headlines allowed for dynamic creatives"}, indent=2)
for i, h in enumerate(headlines):
if len(h) > 40:
return json.dumps({"error": f"Headline {i+1} exceeds 40 character limit"}, indent=2)
if descriptions:
if len(descriptions) > 5:
return json.dumps({"error": "Maximum 5 descriptions allowed for dynamic creatives"}, indent=2)
for i, d in enumerate(descriptions):
if len(d) > 125:
return json.dumps({"error": f"Description {i+1} exceeds 125 character limit"}, indent=2)
# Prepare the update data
update_data = {}
if name:
update_data["name"] = name
# Choose between asset_feed_spec (dynamic creative) or object_story_spec (traditional)
# ONLY use asset_feed_spec when user explicitly provides plural parameters (headlines/descriptions)
if headlines or descriptions or dynamic_creative_spec:
# Handle dynamic creative assets via asset_feed_spec
asset_feed_spec = {}
# Add required ad_formats field for dynamic creatives
asset_feed_spec["ad_formats"] = ["SINGLE_IMAGE"]
# Handle headlines - Meta API uses "titles" not "headlines" in asset_feed_spec
if headlines:
asset_feed_spec["titles"] = [{"text": headline_text} for headline_text in headlines]
# Handle descriptions
if descriptions:
asset_feed_spec["descriptions"] = [{"text": description_text} for description_text in descriptions]
# Add message as bodies - Meta API uses "bodies" not "primary_texts" in asset_feed_spec
if message:
asset_feed_spec["bodies"] = [{"text": message}]
# Add call_to_action_types if provided
if call_to_action_type:
asset_feed_spec["call_to_action_types"] = [call_to_action_type]
update_data["asset_feed_spec"] = asset_feed_spec
else:
# Use traditional object_story_spec with link_data for simple creatives
if message or headline or description or call_to_action_type or lead_gen_form_id:
update_data["object_story_spec"] = {"link_data": {}}
if message:
update_data["object_story_spec"]["link_data"]["message"] = message
# Add headline (singular) to link_data
if headline:
update_data["object_story_spec"]["link_data"]["name"] = headline
# Add description (singular) to link_data
if description:
update_data["object_story_spec"]["link_data"]["description"] = description
# Add call_to_action to link_data for simple creatives
if call_to_action_type or lead_gen_form_id:
cta_data = {}
if call_to_action_type:
cta_data["type"] = call_to_action_type
# Add lead form ID to value object if provided (required for lead generation campaigns)
if lead_gen_form_id:
cta_data["value"] = {"lead_gen_form_id": lead_gen_form_id}
if cta_data:
update_data["object_story_spec"]["link_data"]["call_to_action"] = cta_data
# Add dynamic creative spec if provided
if dynamic_creative_spec:
update_data["dynamic_creative_spec"] = dynamic_creative_spec
# Prepare the API endpoint for updating the creative
endpoint = f"{creative_id}"
try:
# Make API request to update the creative
data = await make_api_request(endpoint, access_token, update_data, method="POST")
# If successful, get more details about the updated creative
if "id" in data:
creative_endpoint = f"{creative_id}"
creative_params = {
"fields": "id,name,status,thumbnail_url,image_url,image_hash,object_story_spec,url_tags,link_url,dynamic_creative_spec"
}
creative_details = await make_api_request(creative_endpoint, access_token, creative_params)
return json.dumps({
"success": True,
"creative_id": creative_id,
"details": creative_details
}, indent=2)
return json.dumps(data, indent=2)
except Exception as e:
return json.dumps({
"error": "Failed to update ad creative",
"details": str(e),
"update_data_sent": update_data
}, indent=2)