update_ad_creative
Modify existing Meta ad creative elements like text, headlines, descriptions, and call-to-action to improve campaign performance and A/B testing.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ad_creative_id | Yes | ||
| meta_access_token | No | ||
| name | No | ||
| primary_text | No | ||
| primary_text_variants | No | ||
| headline_text | No | ||
| headline_variants | No | ||
| description_text | No | ||
| description_variants | No | ||
| optimization_type | No | ||
| dynamic_creative_spec | No | ||
| call_to_action_type | No | ||
| lead_form_id | No | ||
| ad_formats | No |
Implementation Reference
- Implementation of the 'update_ad_creative' tool handler.
async def update_ad_creative( ad_creative_id: str, meta_access_token: Optional[str] = None, name: Optional[str] = None, primary_text: Optional[str] = None, primary_text_variants: Optional[List[str]] = None, headline_text: Optional[str] = None, headline_variants: Optional[List[str]] = None, description_text: Optional[str] = None, description_variants: Optional[List[str]] = None, optimization_type: Optional[str] = None, dynamic_creative_spec: Optional[Dict[str, Any]] = None, call_to_action_type: Optional[str] = None, lead_form_id: Optional[str] = None, ad_formats: Optional[List[str]] = None, ) -> str: if not ad_creative_id: return _json({"error": "No creative ID provided"}) primary_text_variants = _normalize_list_argument(primary_text_variants) headline_variants = _normalize_list_argument(headline_variants) description_variants = _normalize_list_argument(description_variants) ad_formats = _normalize_list_argument(ad_formats) attempted_content_fields = [ field for field, value in { "primary_text": primary_text, "primary_text_variants": primary_text_variants, "headline_text": headline_text, "headline_variants": headline_variants, "description_text": description_text, "description_variants": description_variants, "call_to_action_type": call_to_action_type, "lead_form_id": lead_form_id, }.items() if value is not None ] if attempted_content_fields: return _json( { "error": "Content updates are not allowed on existing creatives", "explanation": ( "The Meta API does not allow updating content fields (primary_text, headline_text, description_text, CTA, image, video, URL) " "on existing creatives." ), "workaround": ( "Create a new creative via create_ad_creative, then call update_ad with the new ad_creative_id." ), "ad_creative_id": ad_creative_id, "attempted_content_fields": attempted_content_fields, } ) if optimization_type and optimization_type != "DEGREES_OF_FREEDOM": return _json({"error": f"Invalid optimization_type '{optimization_type}'. Only 'DEGREES_OF_FREEDOM' is supported."}) update_payload: Dict[str, Any] = {} if name: update_payload["name"] = name if optimization_type or dynamic_creative_spec or ad_formats: resolved_update_formats = list(ad_formats) if ad_formats else ( ["AUTOMATIC_FORMAT"] if optimization_type == "DEGREES_OF_FREEDOM" else ["SINGLE_IMAGE"] ) feed: Dict[str, Any] = { "ad_formats": resolved_update_formats, } if optimization_type: feed["optimization_type"] = optimization_type update_payload["asset_feed_spec"] = feed if dynamic_creative_spec: update_payload["dynamic_creative_spec"] = dynamic_creative_spec if not update_payload: return _json({"error": "No update parameters provided"}) try: result = await make_api_request(ad_creative_id, meta_access_token, update_payload, method="POST") except Exception as exc: # noqa: BLE001 return _json( { "error": "Failed to update ad creative", "details": str(exc), "update_data_sent": update_payload, } ) if isinstance(result, dict) and result.get("id"): details = await make_api_request( ad_creative_id, meta_access_token, { "fields": ( "id,name,status,thumbnail_url,image_url,image_hash,object_story_spec," "url_tags,link_url,dynamic_creative_spec" ) }, ) return _json({"success": True, "ad_creative_id": ad_creative_id, "details": details}) error_obj = result.get("error", {}) if isinstance(result, dict) else {} details = error_obj.get("details", {}) if isinstance(error_obj, dict) else {} inner = details.get("error", {}) if isinstance(details, dict) else {} error_subcode = inner.get("error_subcode") if isinstance(inner, dict) else error_obj.get("error_subcode") if error_subcode == 1815573: return _json( { "error": "Content updates are not allowed on existing creatives", "explanation": ( "The Meta API does not allow updating content fields (primary_text, headline_text, description_text, CTA, image, video, URL) " "on existing creatives." ), "workaround": ( "Create a new creative via create_ad_creative, then call update_ad with the new ad_creative_id." ), "ad_creative_id": ad_creative_id, "attempted_updates": update_payload, } ) return _json(result if isinstance(result, dict) else {"data": result})