upload_ad_image
Upload an image directly to Meta Ads for creative use. Supports data URLs, base64 strings, or direct image links. Returns JSON with image details for campaign integration.
Instructions
Upload an image to use in Meta Ads creatives.
Args:
access_token: Meta API access token (optional - will use cached token if not provided)
account_id: Meta Ads account ID (format: act_XXXXXXXXX)
file: Data URL or raw base64 string of the image (e.g., "data:image/png;base64,iVBORw0KG...")
image_url: Direct URL to an image to fetch and upload
name: Optional name for the image (default: filename)
Returns:
JSON response with image details including hash for creative creation
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| access_token | No | ||
| account_id | No | ||
| file | No | ||
| image_url | No | ||
| name | No |
Implementation Reference
- meta_ads_mcp/core/ads.py:567-744 (handler)The core handler function for the 'upload_ad_image' MCP tool. Decorated with @mcp_server.tool() for automatic registration. Handles image upload to Meta Ads adimages endpoint from either base64 data or URL, normalizes API response to provide image_hash, with comprehensive error handling and suggestions.async def upload_ad_image( account_id: str, access_token: Optional[str] = None, file: Optional[str] = None, image_url: Optional[str] = None, name: Optional[str] = None ) -> str: """ Upload an image to use in Meta Ads creatives. Args: account_id: Meta Ads account ID (format: act_XXXXXXXXX) access_token: Meta API access token (optional - will use cached token if not provided) file: Data URL or raw base64 string of the image (e.g., "data:image/png;base64,iVBORw0KG...") image_url: Direct URL to an image to fetch and upload name: Optional name for the image (default: filename) Returns: JSON response with image details including hash for creative creation """ # Check required parameters if not account_id: return json.dumps({"error": "No account ID provided"}, indent=2) # Ensure we have image data if not file and not image_url: return json.dumps({"error": "Provide either 'file' (data URL or base64) or 'image_url'"}, indent=2) # Ensure account_id has the 'act_' prefix for API compatibility if not account_id.startswith("act_"): account_id = f"act_{account_id}" try: # Determine encoded_image (base64 string without data URL prefix) and a sensible name encoded_image: str = "" inferred_name: str = name or "" if file: # Support data URL (e.g., data:image/png;base64,...) and raw base64 data_url_prefix = "data:" base64_marker = "base64," if file.startswith(data_url_prefix) and base64_marker in file: header, base64_payload = file.split(base64_marker, 1) encoded_image = base64_payload.strip() # Infer file extension from MIME type if name not provided if not inferred_name: # Example header: data:image/png;... mime_type = header[len(data_url_prefix):].split(";")[0].strip() extension_map = { "image/png": ".png", "image/jpeg": ".jpg", "image/jpg": ".jpg", "image/webp": ".webp", "image/gif": ".gif", "image/bmp": ".bmp", "image/tiff": ".tiff", } ext = extension_map.get(mime_type, ".png") inferred_name = f"upload{ext}" else: # Assume it's already raw base64 encoded_image = file.strip() if not inferred_name: inferred_name = "upload.png" else: # Download image from URL try: image_bytes = await try_multiple_download_methods(image_url) except Exception as download_error: return json.dumps({ "error": "We couldn’t download the image from the link provided.", "reason": "The server returned an error while trying to fetch the image.", "image_url": image_url, "details": str(download_error), "suggestions": [ "Make sure the link is publicly reachable (no login, VPN, or IP restrictions).", "If the image is hosted on a private app or server, move it to a public URL or a CDN and try again.", "Verify the URL is correct and serves the actual image file." ] }, indent=2) if not image_bytes: return json.dumps({ "error": "We couldn’t access the image at the link you provided.", "reason": "The image link doesn’t appear to be publicly accessible or didn’t return any data.", "image_url": image_url, "suggestions": [ "Double‑check that the link is public and does not require login, VPN, or IP allow‑listing.", "If the image is stored in a private app (for example, a self‑hosted gallery), upload it to a public URL or a CDN and try again.", "Confirm the URL is correct and points directly to an image file (e.g., .jpg, .png)." ] }, indent=2) import base64 # Local import encoded_image = base64.b64encode(image_bytes).decode("utf-8") # Infer name from URL if not provided if not inferred_name: try: path_no_query = image_url.split("?")[0] filename_from_url = os.path.basename(path_no_query) inferred_name = filename_from_url if filename_from_url else "upload.jpg" except Exception: inferred_name = "upload.jpg" # Final name resolution final_name = name or inferred_name or "upload.png" # Prepare the API endpoint for uploading images endpoint = f"{account_id}/adimages" # Prepare POST parameters expected by Meta API params = { "bytes": encoded_image, "name": final_name, } # Make API request to upload the image print(f"Uploading image to Facebook Ad Account {account_id}") data = await make_api_request(endpoint, access_token, params, method="POST") # Normalize/structure the response for callers (e.g., to easily grab image_hash) # Typical Graph API response shape: # { "images": { "<hash>": { "hash": "<hash>", "url": "...", "width": ..., "height": ..., "name": "...", "status": 1 } } } if isinstance(data, dict) and "images" in data and isinstance(data["images"], dict) and data["images"]: images_dict = data["images"] images_list = [] for hash_key, info in images_dict.items(): # Some responses may omit the nested hash, so ensure it's present normalized = { "hash": (info.get("hash") or hash_key), "url": info.get("url"), "width": info.get("width"), "height": info.get("height"), "name": info.get("name"), } # Drop null/None values normalized = {k: v for k, v in normalized.items() if v is not None} images_list.append(normalized) # Sort deterministically by hash images_list.sort(key=lambda i: i.get("hash", "")) primary_hash = images_list[0].get("hash") if images_list else None result = { "success": True, "account_id": account_id, "name": final_name, "image_hash": primary_hash, "images_count": len(images_list), "images": images_list } return json.dumps(result, indent=2) # If the API returned an error-like structure, surface it consistently if isinstance(data, dict) and "error" in data: return json.dumps({ "error": "Failed to upload image", "details": data.get("error"), "account_id": account_id, "name": final_name }, indent=2) # Fallback: return a wrapped raw response to avoid breaking callers return json.dumps({ "success": True, "account_id": account_id, "name": final_name, "raw_response": data }, indent=2) except Exception as e: return json.dumps({ "error": "Failed to upload image", "details": str(e) }, indent=2)