AI Marketing Content Generator

Build a Python tool that generates multi-platform marketing copy with Gemini.

Introduction

30 Second Summary

Marketing teams face the same challenge every day: creating consistent, on-brand content across multiple platforms takes hours of work. What if you could generate an entire content package from a single product description?

In this project, you will build a command-line tool that uses Google's Gemini AI to generate tailored marketing content for social media, email, and product pages. You'll learn how to engineer prompts that produce structured output you can copy and use immediately.

What You'll Build

A Python script that takes a product brief and brand voice as input, then calls the Gemini API to produce a complete marketing content package in JSON format.

By the end of this project, you'll have:

  • A working Python script (marketing_generator.py) that calls the Gemini API to generate multi-platform marketing content from a single product brief.
  • Hands-on experience with prompt engineering techniques including system instructions and platform-specific constraints.
  • Structured JSON output using Pydantic models that delivers ready-to-use marketing copy in a clean, parseable format.
  • Secret Mission: Add an A/B test variant generator that creates professional and casual versions of your content, then compares them side-by-side.

Are there any prerequisites?

None. This guide walks you through setting up Python, getting your free Gemini API key, and installing everything you need. No credit card or billing account is required.

Set Up Python and Get Your Gemini API Key

Before you can build an AI-powered content generator, you need a few things in place: Python to run your script, and an API key to connect to Google's Gemini model.

In this step, you will install Python and the Google Gen AI SDK, then grab a free API key from Google AI Studio.

In this step, get ready to:
  • Install Python and the Google Gen AI SDK.
  • Create a free Gemini API key from Google AI Studio.
  • Set up your project folder and files.
Install Python and the Google Gen AI SDK

The Google Gen AI SDK requires Python 3.10 or higher. Let's confirm you have the right version installed.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or Command Prompt (Windows) and press Enter to open it.
  • Check your Python version by running this command:

macOS

python3 --version

Windows

python --version

You should see a version number printed, like Python 3.12.4.

✔️ I see version 3.10 or higher

Your Python version is good to go. Skip ahead to installing the SDK below.

ⓧ I see an older version

You have Python installed, but it is below 3.10. You need to install a newer version.

  • Download and install the latest Python from the official site: https://www.python.org/downloads/
  • During installation on Windows, check the box that says Add python.exe to PATH.
  • On macOS, the installer may install alongside your old version. After installing, use python3 --version to verify the new version shows 3.10 or higher.

macOS default Python version

macOS often ships with an older Python version. After installing a newer version, the python3 command should point to the new version. If it still shows the old version, try running python3.12 --version (replacing 3.12 with whatever you installed) to confirm the new binary is available.

ⓧ Command not found

Python is not installed on your machine yet. Follow these steps to install it.

  • Download and install the latest Python from the official site: https://www.python.org/downloads/
  • During installation on Windows, check the box that says Add python.exe to PATH.
  • After installation completes, close and reopen your terminal.
  • Run the version check command again to confirm it works.

Now that Python is ready, you can install the google-genai SDK. This is the official Google library for calling the Gemini API from Python.

  • Install the Google Gen AI SDK by running this command:
pip install google-genai

What does this command do?

pip is Python's package installer. This command downloads and installs the google-genai package, which gives your script access to Gemini's content generation capabilities.

You should see output ending with a success message that includes the package name and version.

Seeing a permissions error or pip not found?

  • On macOS, try pip3 install google-genai instead.
  • On Windows, make sure you checked Add python.exe to PATH during installation. If you missed it, reinstall Python with that box checked.
  • If you see a permissions error on macOS/Linux, try adding --user to the end of the command.

Still stuck? Help me fix my pip install error for google-genai

Get your Gemini API key from Google AI Studio

Google AI Studio is Google's free web tool for testing and managing Gemini API access. You will create an API key here that your Python script uses to authenticate with the Gemini model.

  • Open your browser and go to https://aistudio.google.com.
  • Sign in with your Google account.
  • Click Get API key in the left sidebar.
  • Click Create API key.
  • If prompted, select Create API key in new project.
  • Copy the generated API key and save it somewhere safe (you will need it in the next substep).

Why is this free?

Google's Gemini API has a free tier that is generous enough for this entire project. No credit card or billing account is required. The free tier allows 10 requests per minute and 1,500 requests per day.

The tradeoff is that on the free tier, your inputs and outputs may be used to improve Google's models. Do not send confidential or proprietary content through the free tier.

Getting a permission denied error?

If you see a permission error when creating your key, try using a personal Google account instead of a work or school account. Organization-managed accounts sometimes have restrictions that block API key creation.

Need more help? Help me troubleshoot my Google AI Studio API key creation error

Create your project folder and files

With your API key ready, it is time to create the project folder and the two files your script needs: a .env file for your API key and the main marketing_generator.py script.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir marketing-generator
cd marketing-generator
  • Create the two project files by running these commands:

macOS

touch .env
touch marketing_generator.py

Windows

New-Item .env
New-Item marketing_generator.py
  • Confirm the files exist by running:
ls

You should see .env and marketing_generator.py listed in the output.

Hidden files on macOS

Files starting with a dot (like .env) are hidden by default in Finder. If you use ls -a instead of ls, you will see all files including hidden ones.

Now add your API key to the .env file. This keeps your key separate from your code so you never accidentally share it.

  • Open the .env file in a text editor and paste the following, replacing your-api-key-here with the API key you copied from Google AI Studio:
GEMINI_API_KEY=your-api-key-here

Why use a .env file?

Storing secrets in a .env file keeps them out of your source code. If you ever share your code or push it to GitHub, your API key stays private as long as you add .env to your .gitignore file.

  • Save the .env file.

The marketing_generator.py file stays empty for now. You will write the script code in the next step.

✔️ Awesome, I've got everything!

Great. Double check you have saved your .env file with your API key pasted in.

ⓧ I'd like to double check the full code

Here is what your .env file should look like (with your actual key instead of the placeholder):

GEMINI_API_KEY=your-api-key-here

Your marketing_generator.py file should be empty at this point.

Your environment is all set. You have Python, the Google Gen AI SDK, an API key, and a project folder ready to go. Next up, you will connect to Gemini and generate your first piece of marketing content.

Connect to Gemini and Generate Your First Content

You have Python and the Gemini SDK installed. Now it is time to write actual code that talks to the Gemini API and gets back marketing content.

In this step, you will connect your script to the Gemini API, define a system instruction that shapes how the model writes, and test the entire flow with a sample product.

In this step, get ready to:
  • Connect to the Gemini API using your API key.
  • Define a system instruction that shapes the model's writing style.
  • Test the connection with a sample marketing prompt.
Write the imports and connect to Gemini

Before you can generate any content, your script needs to import the SDK, load your API key, and create a client that handles communication with Google's servers.

  • In marketing_generator.py, paste the following code:
import os
from google import genai
from google.genai import types
from pydantic import BaseModel, Field
from typing import List

# --- Configuration ---

GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "your-api-key-here")

client = genai.Client(api_key=GEMINI_API_KEY)

MODEL = "gemini-2.5-flash"

What does this code do?

  • from google import genai imports the official Google Gen AI SDK. from google.genai import types imports configuration classes you will use shortly.
  • os.environ.get("GEMINI_API_KEY", "your-api-key-here") reads your API key from the environment. If the variable is not set, it falls back to the placeholder string.
  • genai.Client(api_key=GEMINI_API_KEY) creates a client that handles all requests to the Gemini API.
  • MODEL stores the model name so you can reference it easily throughout the script.
  • The pydantic and typing imports are not used yet. You will need them in a later step for structured output.
  • Save marketing_generator.py.

You will not see any output yet. The next chunk adds the system instruction, and the chunk after that runs the actual API call.

Add a system instruction for brand voice

A system instruction tells the model who it is and how it should behave. Without one, the model responds in a generic style. With one, you can control tone, length, and rules across every response.

  • Below the MODEL = "gemini-2.5-flash" line, add the following function:

# --- System Instruction ---

def build_system_instruction(brand_voice: str) -> str:
    return f"""You are an expert marketing copywriter with 10 years of experience creating 
high-converting content for digital platforms. 

Your writing style is: {brand_voice}

Rules you always follow:
- Every piece of content must have a clear call-to-action
- Use power words that create urgency or emotion
- Tailor content length and tone to each platform's best practices
- Twitter/X posts must be under 280 characters (excluding hashtags)
- LinkedIn posts should be professional and value-driven, 1-3 short paragraphs
- Instagram captions should be engaging and emoji-friendly
- Email subject lines should be under 60 characters and curiosity-driven
- Never use generic filler phrases like "in today's world" or "game-changer"
"""

What does this function do?

build_system_instruction takes a brand_voice string (like "playful and bold") and inserts it into a detailed prompt that defines the model's persona.

The rules section constrains the model's output. Platform-specific limits (280 characters for Twitter/X, professional tone for LinkedIn) keep generated content ready to use without manual editing.

  • Save marketing_generator.py.

You still will not see output yet. The next chunk ties everything together with an actual API call.

Test with a sample product

Time to make your first API call. You will send a prompt about an eco-friendly water bottle and print whatever the model generates.

  • Below the build_system_instruction function, add this test code at the bottom of the file:

# --- Basic test call ---

system_instruction = build_system_instruction("playful and bold")

response = client.models.generate_content(
    model=MODEL,
    contents="Write a short marketing post for an eco-friendly water bottle.",
    config=types.GenerateContentConfig(
        system_instruction=system_instruction,
    ),
)

print(response.text)

What does this code do?

  • build_system_instruction("playful and bold") generates the full system instruction with a playful brand voice baked in.
  • client.models.generate_content(...) sends your prompt to the Gemini API along with the system instruction.
  • The config parameter passes the system instruction as a GenerateContentConfig object. This tells the model how to behave before it reads your actual prompt.
  • print(response.text) outputs the generated marketing copy to your terminal.
  • Save marketing_generator.py.
  • Set your GEMINI_API_KEY environment variable so the script can read it. The command differs by platform.

macOS

  • In your terminal, export your API key by running this command (replace the placeholder with your actual key from the .env file):
export GEMINI_API_KEY=your-api-key-here

Windows

  • In PowerShell, set your API key by running this command (replace the placeholder with your actual key from the .env file):
$env:GEMINI_API_KEY="your-api-key-here"
  • Run the script by entering this command:
python marketing_generator.py

You should see a short marketing post about an eco-friendly water bottle printed in your terminal. The tone should feel playful and bold, matching the brand voice you passed in.

Seeing an authentication error?

  • A 401 or 403 error means your API key is invalid or missing. Double-check that you copied the full key from Google AI Studio and that the export (or $env:) command ran successfully in the same terminal session.
  • If you opened a new terminal tab after exporting, the variable is gone. Re-run the export command in the new tab.
  • Make sure there are no extra spaces or quotes around your key value.

Help me debug my Gemini API authentication error.

Why use a system instruction?

Without a system instruction, the model writes in a generic, neutral tone. It produces correct content but with no personality or constraints.

With a system instruction, every response follows your brand rules automatically. You do not need to repeat "be playful" or "keep it short" in every prompt. The system instruction handles that behind the scenes.

✔️ Awesome, I've got everything!

Great. Double check you have saved marketing_generator.py.

ⓧ I'd like to double check the full code

import os
from google import genai
from google.genai import types
from pydantic import BaseModel, Field
from typing import List

# --- Configuration ---

GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "your-api-key-here")

client = genai.Client(api_key=GEMINI_API_KEY)

MODEL = "gemini-2.5-flash"


# --- System Instruction ---

def build_system_instruction(brand_voice: str) -> str:
    return f"""You are an expert marketing copywriter with 10 years of experience creating 
high-converting content for digital platforms. 

Your writing style is: {brand_voice}

Rules you always follow:
- Every piece of content must have a clear call-to-action
- Use power words that create urgency or emotion
- Tailor content length and tone to each platform's best practices
- Twitter/X posts must be under 280 characters (excluding hashtags)
- LinkedIn posts should be professional and value-driven, 1-3 short paragraphs
- Instagram captions should be engaging and emoji-friendly
- Email subject lines should be under 60 characters and curiosity-driven
- Never use generic filler phrases like "in today's world" or "game-changer"
"""


# --- Basic test call ---

system_instruction = build_system_instruction("playful and bold")

response = client.models.generate_content(
    model=MODEL,
    contents="Write a short marketing post for an eco-friendly water bottle.",
    config=types.GenerateContentConfig(
        system_instruction=system_instruction,
    ),
)

print(response.text)

Your script now connects to the Gemini API and generates marketing content shaped by a system instruction. Next up, you will engineer a multi-platform prompt that generates content for Twitter/X, LinkedIn, Instagram, and email all at once.

Engineer Multi-Platform Marketing Prompts

Your script can talk to Gemini and generate basic marketing copy. The problem is that a single generic prompt produces a single generic response.

Real marketing teams need content tailored for each platform. Twitter has character limits, LinkedIn expects professional tone, and Instagram thrives on emoji. In this step, you will engineer a structured prompt template that produces a complete multi-platform content package from a single product brief.

In this step, get ready to:
  • Design a prompt template with platform-specific constraints for social media posts.
  • Add email subject line and product description generation to your prompt.
  • Test your multi-platform prompt with a sample product.
Design your multi-platform prompt template

A well-engineered prompt gives the model specific constraints and a clear structure to follow. Instead of asking for "a marketing post," you will ask for an entire content package with platform-specific rules baked into the request.

  • In marketing_generator.py, find the entire # --- Basic test call --- section (everything from the comment down to print(response.text)).
  • Delete that entire section and replace it with the following code:
# --- Multi-platform prompt test ---

product_name = "EcoFlow Bottle"
product_description = "A 32oz stainless steel water bottle made from 100% recycled materials."
target_audience = "eco-conscious millennials"
brand_voice = "playful and bold"

system_instruction = build_system_instruction(brand_voice)

prompt = f"""Create a complete marketing content package for the following product:

Product Name: {product_name}
Product Description: {product_description}
Target Audience: {target_audience}

Generate:
1. Three social media posts (one each for Twitter/X, LinkedIn, and Instagram)
2. Three email subject line variations optimized for open rates
3. A compelling 2-3 sentence product description for a website

Make every word count. Be specific, not generic."""

response = client.models.generate_content(
    model=MODEL,
    contents=prompt,
    config=types.GenerateContentConfig(
        system_instruction=system_instruction,
    ),
)

print(response.text)

What does this code do?

The first block defines sample product variables. These will become user inputs later, but hardcoding them now lets you test the prompt quickly.

The prompt f-string is the core of this step. It injects the product variables into a structured request and tells the model exactly what to generate. The "Generate:" list gives the model three distinct tasks with clear boundaries.

The final line Make every word count. Be specific, not generic. is a guardrail instruction that pushes the model away from filler phrases.

  • Save marketing_generator.py.

You won't see a change yet. The next substep walks through the engineering decisions, then you will run the script.

Understand the email and product description constraints

Your prompt does not just ask for content. It encodes marketing best practices as constraints that guide the model toward higher-quality output.

Why request email subject lines under 60 characters?

Most email clients truncate subject lines on mobile devices between 33 and 50 characters. Keeping subjects under 60 characters ensures the core message stays visible across all devices.

Requesting 3 variations gives you options to pick from. The system instruction already tells the model to make subjects "curiosity-driven," so the model will aim for open-rate optimization automatically.

Why a 2-3 sentence product description?

Website product descriptions need to be scannable. Two to three sentences is enough to communicate the value proposition and create urgency without losing the reader. Longer descriptions get skipped.

Notice how the prompt and the system instruction work together. The prompt defines WHAT to generate. The system instruction defines HOW to write it (brand voice, power words, calls-to-action).

Test with a sample product

Your prompt is ready. Time to run the script and see the full content package Gemini produces.

  • Run your script by entering this command in the terminal:
python marketing_generator.py

You should see a large block of text in your terminal containing social media posts for Twitter/X, LinkedIn, and Instagram, three email subject lines, and a product description.

What should I see?

The output will be unstructured text (not JSON yet). You should see content for all three platforms, each with a different tone and length. Twitter posts should be short and punchy. LinkedIn posts should read like professional mini-articles. Instagram captions should include emoji.

The email subject lines should be concise hooks, and the product description should be a tight 2-3 sentence pitch. If you see all of these sections in the output, your prompt engineering is working.

Seeing an error or empty output?

If you see an authentication error, make sure your GEMINI_API_KEY environment variable is still set in your terminal session. Terminal sessions reset when you close the window.

If the output looks generic or ignores your constraints, check that the system instruction is being passed in the config parameter. Without it, the model does not know about your brand voice rules.

help me debug my Gemini API call for multi-platform marketing content

✔️ Awesome, I've got everything!

Great. Double check you have saved marketing_generator.py and that your terminal output includes content for all three platforms.

ⓧ I'd like to double check the full code

import os
from google import genai
from google.genai import types
from pydantic import BaseModel, Field
from typing import List

# --- Configuration ---

GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "your-api-key-here")

client = genai.Client(api_key=GEMINI_API_KEY)

MODEL = "gemini-2.5-flash"


# --- System Instruction ---

def build_system_instruction(brand_voice: str) -> str:
    return f"""You are an expert marketing copywriter with 10 years of experience creating 
high-converting content for digital platforms. 

Your writing style is: {brand_voice}

Rules you always follow:
- Every piece of content must have a clear call-to-action
- Use power words that create urgency or emotion
- Tailor content length and tone to each platform's best practices
- Twitter/X posts must be under 280 characters (excluding hashtags)
- LinkedIn posts should be professional and value-driven, 1-3 short paragraphs
- Instagram captions should be engaging and emoji-friendly
- Email subject lines should be under 60 characters and curiosity-driven
- Never use generic filler phrases like "in today's world" or "game-changer"
"""


# --- Multi-platform prompt test ---

product_name = "EcoFlow Bottle"
product_description = "A 32oz stainless steel water bottle made from 100% recycled materials."
target_audience = "eco-conscious millennials"
brand_voice = "playful and bold"

system_instruction = build_system_instruction(brand_voice)

prompt = f"""Create a complete marketing content package for the following product:

Product Name: {product_name}
Product Description: {product_description}
Target Audience: {target_audience}

Generate:
1. Three social media posts (one each for Twitter/X, LinkedIn, and Instagram)
2. Three email subject line variations optimized for open rates
3. A compelling 2-3 sentence product description for a website

Make every word count. Be specific, not generic."""

response = client.models.generate_content(
    model=MODEL,
    contents=prompt,
    config=types.GenerateContentConfig(
        system_instruction=system_instruction,
    ),
)

print(response.text)

Your prompt now produces a full content package from a single product brief. The output is useful, but it is unstructured text that would be hard to parse programmatically. Next up, you will add structured JSON output so the content comes back in a clean, machine-readable format you can plug into any workflow.

Add Structured JSON Output with Pydantic

Your script generates marketing content, but it comes back as unstructured plain text. Copying individual posts out of a wall of text is tedious and error-prone.

Structured output solves this by forcing the model to return valid JSON that matches a schema you define. You will use Pydantic models to describe exactly what shape the response must take, then pass that schema to the Gemini API.

In this step, get ready to:
  • Define Pydantic models that represent your marketing content package.
  • Configure the Gemini API to return JSON matching your schema.
  • Parse and validate the structured response in your terminal.
Define Pydantic models for your content structure

Before asking the model to return structured data, you need to describe the shape of that data. Pydantic models act as a blueprint that tells Gemini exactly which fields to include and what type each field should be.

  • In marketing_generator.py, find the # --- Configuration --- comment.
  • Add the following Pydantic model classes above that comment, directly below your import statements:
# --- Pydantic Models for Structured Output ---

class SocialPost(BaseModel):
    platform: str = Field(description="The social media platform (twitter, linkedin, or instagram)")
    content: str = Field(description="The post content tailored for the platform")
    hashtags: List[str] = Field(description="Relevant hashtags for the post")


class MarketingContent(BaseModel):
    social_posts: List[SocialPost] = Field(description="Social media posts for different platforms")
    email_subject_lines: List[str] = Field(description="Three email subject line variations")
    product_description: str = Field(description="A compelling 2-3 sentence product description")

What does this code do?

You are defining two classes that describe the structure of your marketing content package.

  • SocialPost represents a single social media post with a platform name, the post content, and a list of hashtags.
  • MarketingContent is the full package. It contains a list of SocialPost objects, three email subject lines, and a product description.
  • The Field(description=...) values help Gemini understand what each field should contain. They act as hints during generation.
  • Save marketing_generator.py.

Why Pydantic for structured output?

Without a schema, the model might return JSON one time and plain text the next. Pydantic guarantees the response will always match your defined structure. If the model returns malformed data, Pydantic raises a validation error instead of silently passing bad data downstream.

This is the professionally correct pattern for extracting structured content from LLMs. It is a transferable skill you can apply with any AI API that supports schema-based output.

Configure Gemini to return structured JSON

Now you will tell Gemini to constrain its output to JSON that matches your MarketingContent schema. This uses the response_format parameter in the API configuration.

  • In marketing_generator.py, scroll down to the # --- Multi-platform prompt test --- comment.
  • Change that comment to:
# --- Structured content generation test ---
  • Now find the config parameter inside your client.models.generate_content call. It currently looks like this:
    config=types.GenerateContentConfig(
        system_instruction=system_instruction,
    ),
  • Replace that entire config block with the following, which adds response_format to the configuration:
    config=types.GenerateContentConfig(
        system_instruction=system_instruction,
        response_format={
            "text": {
                "mime_type": "application/json",
                "schema": MarketingContent.model_json_schema(),
            }
        },
    ),

What does this configuration do?

  • response_format tells Gemini to constrain its output format instead of returning free-form text.
  • mime_type: "application/json" signals that the response must be valid JSON.
  • MarketingContent.model_json_schema() converts your Pydantic model into a JSON Schema dictionary. Gemini uses this schema to know exactly which fields and types to include in its response.
  • Save marketing_generator.py.
Parse the response and verify the output

The final piece is to parse the raw JSON string from Gemini into a validated Python object. Pydantic's model_validate_json method handles both parsing and validation in one call.

  • In marketing_generator.py, find the last line of your file:
print(response.text)
  • Replace that line with the following two lines:
content = MarketingContent.model_validate_json(response.text)
print(content)

What does this do?

  • model_validate_json(response.text) takes the raw JSON string from Gemini, parses it, and validates every field against your MarketingContent schema.
  • If the model returns JSON that is missing a required field or has the wrong type, Pydantic raises a ValidationError immediately. No silent failures.
  • The result stored in content is a fully typed Python object. You can access content.social_posts, content.email_subject_lines, and content.product_description directly.
  • Save marketing_generator.py.
  • Run the script in your terminal by running:
python marketing_generator.py
  • You should see structured output in your terminal showing a MarketingContent object with social_posts, email_subject_lines, and product_description fields populated with marketing copy for the EcoFlow Bottle.

Seeing a ValidationError or empty output?

  • Make sure your Pydantic model classes are defined above the code that uses them. Python reads top-to-bottom, so MarketingContent must exist before it is referenced in model_json_schema().
  • Check that the response_format dictionary is nested correctly. The "schema" key must be inside "text", which is inside response_format.
  • If you see a rate limit error (429), wait a moment and try again. The free tier allows 10 requests per minute.

Why is my structured output failing?

✔️ Awesome, I've got everything!

Great. Double check you have saved marketing_generator.py and your output shows structured fields.

ⓧ I'd like to double check the full code

Here is the full marketing_generator.py file at this point:

import os
from google import genai
from google.genai import types
from pydantic import BaseModel, Field
from typing import List


# --- Pydantic Models for Structured Output ---

class SocialPost(BaseModel):
    platform: str = Field(description="The social media platform (twitter, linkedin, or instagram)")
    content: str = Field(description="The post content tailored for the platform")
    hashtags: List[str] = Field(description="Relevant hashtags for the post")


class MarketingContent(BaseModel):
    social_posts: List[SocialPost] = Field(description="Social media posts for different platforms")
    email_subject_lines: List[str] = Field(description="Three email subject line variations")
    product_description: str = Field(description="A compelling 2-3 sentence product description")


# --- Configuration ---

GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "your-api-key-here")

client = genai.Client(api_key=GEMINI_API_KEY)

MODEL = "gemini-2.5-flash"


# --- System Instruction ---

def build_system_instruction(brand_voice: str) -> str:
    return f"""You are an expert marketing copywriter with 10 years of experience creating 
high-converting content for digital platforms. 

Your writing style is: {brand_voice}

Rules you always follow:
- Every piece of content must have a clear call-to-action
- Use power words that create urgency or emotion
- Tailor content length and tone to each platform's best practices
- Twitter/X posts must be under 280 characters (excluding hashtags)
- LinkedIn posts should be professional and value-driven, 1-3 short paragraphs
- Instagram captions should be engaging and emoji-friendly
- Email subject lines should be under 60 characters and curiosity-driven
- Never use generic filler phrases like "in today's world" or "game-changer"
"""


# --- Structured content generation test ---

product_name = "EcoFlow Bottle"
product_description = "A 32oz stainless steel water bottle made from 100% recycled materials."
target_audience = "eco-conscious millennials"
brand_voice = "playful and bold"

system_instruction = build_system_instruction(brand_voice)

prompt = f"""Create a complete marketing content package for the following product:

Product Name: {product_name}
Product Description: {product_description}
Target Audience: {target_audience}

Generate:
1. Three social media posts (one each for Twitter/X, LinkedIn, and Instagram)
2. Three email subject line variations optimized for open rates
3. A compelling 2-3 sentence product description for a website

Make every word count. Be specific, not generic."""

response = client.models.generate_content(
    model=MODEL,
    contents=prompt,
    config=types.GenerateContentConfig(
        system_instruction=system_instruction,
        response_format={
            "text": {
                "mime_type": "application/json",
                "schema": MarketingContent.model_json_schema(),
            }
        },
    ),
)

content = MarketingContent.model_validate_json(response.text)
print(content)

Your script now produces validated, structured marketing content instead of unpredictable plain text. Next up, you will turn this into an interactive tool that accepts any product and brand voice as input.

Build the Interactive Content Generator

Your generation logic works and returns structured marketing content. Right now though, the product details are hardcoded directly in the script.

In this step, you will turn the script into a proper interactive tool. The learner provides product details at runtime, and the script generates a complete content package on demand.

In this step, get ready to:
  • Add user input prompts for product name, description, audience, and brand voice.
  • Wrap your generation logic in a reusable function.
  • Add formatted display output with platform labels and headers.
Add user input prompts

A useful tool asks for input at runtime instead of relying on values embedded in the code. Python's built-in input() function pauses the script and waits for the user to type a response.

  • In marketing_generator.py, scroll to the bottom of the file.
  • Select everything from the # --- Structured content generation test --- comment all the way to the end of the file (the print(content) line).
  • Delete the selected code and replace it with the following:
# --- Main ---

def main():
    print("\n\U0001f680 AI Marketing Content Generator")
    print("-" * 40)

    product_name = input("\nProduct name: ")
    product_desc = input("Product description (1-2 sentences): ")
    target_audience = input("Target audience: ")
    brand_voice = input("Brand voice (e.g., 'playful and bold', 'professional and trustworthy'): ")

    print("\n\u23f3 Generating your marketing content package...")

    content = generate_marketing_content(
        product_name=product_name,
        product_description=product_desc,
        target_audience=target_audience,
        brand_voice=brand_voice,
    )

    display_content(content, product_name)


if __name__ == "__main__":
    main()

What does this code do?

The main() function is the entry point for your script. It collects four pieces of information from the user: the product name, a short description, the target audience, and the desired brand voice.

These values flow into generate_marketing_content() (which you will create next) and the result passes to display_content() for formatted output. The if __name__ == "__main__" guard ensures main() only runs when you execute the file directly.

Run scripts from your terminal

Since this script uses input(), you must run it from a terminal (not an editor's built-in output panel). In Cursor, open the integrated terminal with Ctrl+` (macOS and Windows).

Wrap the generation logic in a reusable function

The prompt template and API call currently live as top-level script code. Wrapping them in a function makes the logic reusable and keeps main() clean.

  • In marketing_generator.py, find the build_system_instruction function's closing triple-quote. You will add the new function directly below it.
  • Delete the leftover section comment # --- Structured content generation test --- if it still exists (it should already be gone from the previous substep).
  • Add the following function between the system instruction section and the # --- Main --- section:
# --- Content Generation ---

def generate_marketing_content(
    product_name: str,
    product_description: str,
    target_audience: str,
    brand_voice: str,
) -> MarketingContent:
    """Generate a complete marketing content package."""

    system_instruction = build_system_instruction(brand_voice)

    prompt = f"""Create a complete marketing content package for the following product:

Product Name: {product_name}
Product Description: {product_description}
Target Audience: {target_audience}

Generate:
1. Three social media posts (one each for Twitter/X, LinkedIn, and Instagram)
2. Three email subject line variations optimized for open rates
3. A compelling 2-3 sentence product description for a website

Make every word count. Be specific, not generic."""

    response = client.models.generate_content(
        model=MODEL,
        contents=prompt,
        config=types.GenerateContentConfig(
            system_instruction=system_instruction,
            response_format={
                "text": {
                    "mime_type": "application/json",
                    "schema": MarketingContent.model_json_schema(),
                }
            },
        ),
    )

    return MarketingContent.model_validate_json(response.text)

What does this code do?

This is the same prompt template and API call you tested in the previous step, now wrapped inside a function called generate_marketing_content(). It accepts four parameters, builds the system instruction, constructs the prompt, calls Gemini, and returns a validated MarketingContent object.

By returning a MarketingContent instance (not raw text), the rest of your code can access fields like content.social_posts and content.email_subject_lines directly.

Add a formatted display function

Raw JSON output is hard to scan. A display function formats each piece of content with clear labels so you can copy and use it immediately.

  • Add the following function between the # --- Content Generation --- section and the # --- Main --- section:
# --- Display ---

def display_content(content: MarketingContent, product_name: str) -> None:
    """Display generated content in a formatted way."""

    print("\n" + "=" * 60)
    print(f"  MARKETING CONTENT PACKAGE: {product_name.upper()}")
    print("=" * 60)

    print("\n--- Social Media Posts ---\n")
    for post in content.social_posts:
        print(f"[{post.platform.upper()}]")
        print(f"{post.content}")
        print(f"Hashtags: {' '.join('#' + tag for tag in post.hashtags)}")
        print()

    print("--- Email Subject Lines ---\n")
    for i, subject in enumerate(content.email_subject_lines, 1):
        print(f"  {i}. {subject}")
    print()

    print("--- Product Description ---\n")
    print(f"  {content.product_description}")
    print("\n" + "=" * 60)

What does this code do?

The display_content() function prints each section of the marketing package with visual separators and platform labels. Social posts show the platform name in brackets, followed by the content and hashtags. Email subject lines are numbered 1 through 3. The product description appears at the end.

This makes it easy to scan the terminal output and copy individual pieces directly into your marketing channels.

  • Save marketing_generator.py.
  • Run the script by executing this command in your terminal:
python marketing_generator.py
  • The script will prompt you for four inputs. Try these example values:
  • Product name: EcoFlow Bottle
  • Product description: A 32oz stainless steel water bottle made from 100% recycled materials
  • Target audience: eco-conscious millennials
  • Brand voice: playful and bold

You should see a formatted marketing content package in your terminal with labeled sections for social posts, email subject lines, and a product description.

Seeing an error when you run the script?

  • If you see NameError: name 'generate_marketing_content' is not defined, make sure the generate_marketing_content() function appears ABOVE the main() function in your file. Python reads top to bottom.
  • If you see NameError: name 'display_content' is not defined, the same rule applies. Move display_content() above main().
  • If the script hangs without showing prompts, make sure you are running it from your terminal and not from an editor output panel that does not support interactive input.

Still stuck? Help me debug my marketing_generator.py script

✔️ Awesome, I've got everything!

Great. Double check you have saved your file before moving on.

ⓧ I'd like to double check the full code

Here is the complete marketing_generator.py at this point:

import os
from google import genai
from google.genai import types
from pydantic import BaseModel, Field
from typing import List


# --- Pydantic Models for Structured Output ---

class SocialPost(BaseModel):
    platform: str = Field(description="The social media platform (twitter, linkedin, or instagram)")
    content: str = Field(description="The post content tailored for the platform")
    hashtags: List[str] = Field(description="Relevant hashtags for the post")


class MarketingContent(BaseModel):
    social_posts: List[SocialPost] = Field(description="Social media posts for different platforms")
    email_subject_lines: List[str] = Field(description="Three email subject line variations")
    product_description: str = Field(description="A compelling 2-3 sentence product description")


# --- Configuration ---

GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "your-api-key-here")

client = genai.Client(api_key=GEMINI_API_KEY)

MODEL = "gemini-2.5-flash"


# --- System Instruction ---

def build_system_instruction(brand_voice: str) -> str:
    return f"""You are an expert marketing copywriter with 10 years of experience creating 
high-converting content for digital platforms. 

Your writing style is: {brand_voice}

Rules you always follow:
- Every piece of content must have a clear call-to-action
- Use power words that create urgency or emotion
- Tailor content length and tone to each platform's best practices
- Twitter/X posts must be under 280 characters (excluding hashtags)
- LinkedIn posts should be professional and value-driven, 1-3 short paragraphs
- Instagram captions should be engaging and emoji-friendly
- Email subject lines should be under 60 characters and curiosity-driven
- Never use generic filler phrases like "in today's world" or "game-changer"
"""


# --- Content Generation ---

def generate_marketing_content(
    product_name: str,
    product_description: str,
    target_audience: str,
    brand_voice: str,
) -> MarketingContent:
    """Generate a complete marketing content package."""

    system_instruction = build_system_instruction(brand_voice)

    prompt = f"""Create a complete marketing content package for the following product:

Product Name: {product_name}
Product Description: {product_description}
Target Audience: {target_audience}

Generate:
1. Three social media posts (one each for Twitter/X, LinkedIn, and Instagram)
2. Three email subject line variations optimized for open rates
3. A compelling 2-3 sentence product description for a website

Make every word count. Be specific, not generic."""

    response = client.models.generate_content(
        model=MODEL,
        contents=prompt,
        config=types.GenerateContentConfig(
            system_instruction=system_instruction,
            response_format={
                "text": {
                    "mime_type": "application/json",
                    "schema": MarketingContent.model_json_schema(),
                }
            },
        ),
    )

    return MarketingContent.model_validate_json(response.text)


# --- Display ---

def display_content(content: MarketingContent, product_name: str) -> None:
    """Display generated content in a formatted way."""

    print("\n" + "=" * 60)
    print(f"  MARKETING CONTENT PACKAGE: {product_name.upper()}")
    print("=" * 60)

    print("\n--- Social Media Posts ---\n")
    for post in content.social_posts:
        print(f"[{post.platform.upper()}]")
        print(f"{post.content}")
        print(f"Hashtags: {' '.join('#' + tag for tag in post.hashtags)}")
        print()

    print("--- Email Subject Lines ---\n")
    for i, subject in enumerate(content.email_subject_lines, 1):
        print(f"  {i}. {subject}")
    print()

    print("--- Product Description ---\n")
    print(f"  {content.product_description}")
    print("\n" + "=" * 60)


# --- Main ---

def main():
    print("\n\U0001f680 AI Marketing Content Generator")
    print("-" * 40)

    product_name = input("\nProduct name: ")
    product_desc = input("Product description (1-2 sentences): ")
    target_audience = input("Target audience: ")
    brand_voice = input("Brand voice (e.g., 'playful and bold', 'professional and trustworthy'): ")

    print("\n\u23f3 Generating your marketing content package...")

    content = generate_marketing_content(
        product_name=product_name,
        product_description=product_desc,
        target_audience=target_audience,
        brand_voice=brand_voice,
    )

    display_content(content, product_name)


if __name__ == "__main__":
    main()

Your content generator is complete. You can run it for any product, any audience, and any brand voice. Next up, a secret mission that adds A/B testing so you can compare different content styles side by side.

Secret mission

A/B Test Variant Generator

Your marketing generator produces great content for a single brand voice. But real marketing teams never publish a single version. They test multiple approaches against each other to find what resonates. Can you extend your tool to generate professional and casual variants side-by-side, then have Gemini recommend which one would perform better?

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally and uses the free Gemini API tier. There are no ongoing costs to worry about.

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Project folder on your Desktop containing marketing_generator.py and .env.
  • google-genai Python package.
  • Google AI Studio API key.

Keep everything running

No action needed. Choose this if you plan to keep experimenting with your marketing generator or want to adapt it for other products.

  • Your project files stay on your Desktop ready to use anytime.
  • Your Gemini API key remains active on the free tier at no cost.
  • The google-genai package stays installed in your Python environment.

Pause - I'll come back to this later

Since the Gemini API free tier has no ongoing charges, there is nothing to pause from a cost perspective. Your API key and project files sit idle at no cost until you return.

  • No running processes to stop. The script only runs when you execute it manually.
  • Your API key remains valid and free to use whenever you come back.

Delete - I don't want to use this again

Remove all project resources and revoke your API key so nothing is left behind.

  • Delete your project folder by running this command in your terminal:

macOS

cd ~/Desktop
rm -rf [[PROJECT_FOLDER="your project folder name"]]

Windows

cd ~\Desktop
Remove-Item -Recurse -Force [[PROJECT_FOLDER="your project folder name"]]
  • Uninstall the google-genai package by running:
pip uninstall google-genai
  • Delete your Gemini API key from Google Cloud Console:
  • Go to the Google Cloud Console credentials page at console.cloud.google.com/apis/credentials.
  • Select the project associated with your Gemini API key.
  • Find your API key in the list and click the delete icon (trash can) next to it.
  • Confirm the deletion when prompted.

Nice Work!

Nice Work!

You just built a fully functional AI marketing content generator powered by Google Gemini.

From a single product brief, your tool now produces platform-ready social media posts, email subject lines, and product descriptions in clean, structured JSON.

You've learned how to:

  • Built a Python tool that calls the Gemini API to generate multi-platform marketing content from a single product brief.
  • Applied prompt engineering techniques including system instructions, structured prompts, and platform-specific constraints to shape AI output.
  • Used Pydantic models with Gemini's structured output to get clean, parseable JSON responses every time.
  • Added A/B testing to compare content variations across tones and get AI-powered recommendations on which performs better.

Ready to quiz yourself?