Marketing has always involved wrangling lots of data. Before digital marketing, businesses had to track sales, customer purchases, coupon redemptions, store traffic, and survey responses to understand what their customers wanted.
And while digital marketing didn't invent marketing analytics, it dramatically increased the amount of information out there.
Today, almost every interaction can produce data. A person might see an advertisement, click it, visit a website, read an article, subscribe to an email list, open several emails, add a product to a shopping cart, leave the website, return through a search engine, and eventually make a purchase. And every one of those interactions can potentially become a data point.
For a marketer, this creates an incredible opportunity. It also creates a problem: having more data doesn't automatically result in better decisions.
A company can have hundreds of thousands of customer records and still not know which customers are most valuable. A marketing team can have dozens of dashboards and still struggle to answer a simple question such as, "Which campaign should we invest more money into?"
This is where programming becomes useful. Python gives marketers a way to work with data programmatically. Instead of manually opening spreadsheets, copying formulas, filtering rows, and creating the same report every week, you can build reusable processes that perform those tasks automatically.
More importantly, Python allows you to go beyond simple reporting. You can use it to investigate customer behavior, discover patterns, perform statistical analysis, create predictive models, automate workflows, and build systems that continuously transform raw marketing data into useful information.
That doesn't mean every marketer needs to become a software engineer. It just means that programming can become another tool in a marketer's toolbox and this tutorial explains how.
Prerequisites
You don't need to be an experienced programmer or marketer to use this book, but a few basics will make the examples easier to follow.
- A computer with Python installed: Use a recent Python 3 version. If you're new to Python, an editor such as VS Code or PyCharm can help you run the examples.
- Basic computer and file skills: You should be comfortable opening files, creating folders, and working with CSV or Excel spreadsheets.
- A willingness to learn Python fundamentals: Part I starts with variables, strings, numbers, conditions, loops, and functions, so prior Python experience is helpful but not required.
- A little familiarity with marketing concepts: Terms such as campaign, conversion, revenue, clicks, and advertising spend will appear throughout. You can learn the metrics as you go. The book explains key terms where they're introduced.
- The ability to install packages: Later chapters use libraries such as pandas, NumPy, Matplotlib, scikit-learn, and requests. Examples show how to install them.
You can follow the book using sample data. You don't need access to a company's private customer data or paid marketing APIs. If you use real data, make sure you have permission to access it and avoid exposing personal or confidential information.
What This Book Will Teach You
This book focuses on three major ideas:
- Marketing analytics: using data to understand what happened and why.
- Marketing automation: using Python to perform repetitive marketing tasks more efficiently.
- Marketing intelligence: combining data, analytics, automation, and predictive techniques to support better decisions.
You'll begin with Python fundamentals and gradually move into more advanced applications.
By the end, you'll build a marketing intelligence system that combines many of these concepts into one project.
Here's What We'll Cover:
- Part I: Python Fundamentals for Marketers
- Part II: Working With Marketing Data
- Part III: Data Cleaning and Analysis With pandas
- Part IV: Marketing Visualization
- Part V: Customer and Campaign Analytics
- Part VI: Marketing Automation
- Part VII: Machine Learning for Marketing
- Part VIII: Building a Marketing Intelligence System
- Capstone Project: Build a Python Marketing Intelligence Platform
- Conclusion / Next Steps
The Philosophy of This Book
There's an important difference between learning Python and learning to use Python.
Learning Python means understanding syntax, data types, functions, loops, libraries, and other programming concepts. Using Python means taking those concepts and applying them to an actual problem.
This book focuses heavily on the second approach.
You'll still learn Python fundamentals, but the examples will consistently connect those fundamentals to marketing.
Instead of simply writing:
x = 10 y = 20 print(x + y) we'll work with things like:
campaign_budget = 10000 campaign_spend = 7350 remaining_budget = campaign_budget - campaign_spend print(remaining_budget) The programming concept is the same, but the context is different.
That context is what makes the skill useful.
Part I: Python Fundamentals for Marketers
Python doesn't know what a campaign is or what a customer is. And it doesn't know whether an advertisement is successful. Python simply processes the instructions you give it.
That means your first job is learning how to communicate with the computer.
Fortunately, you don't need to learn everything about Python before you can start working with marketing data. But you do need a practical foundation.
Thinking Like a Programmer
Programming can initially seem intimidating because computers are extremely literal.
If you tell a person: "Calculate how much money we have left from the campaign budget," they can probably figure out what you mean.
A computer needs explicit instructions. You need to tell it which values to use and which operation to perform.
For example:
budget = 10000 spent = 7350 remaining = budget - spent print(remaining) The output is:
2650 The computer didn't need to understand marketing. It simply followed the instructions.
This is the basic relationship between you and a programming language.
You provide instructions, and Python translates those instructions into operations the computer can perform.
Breaking Problems Into Steps
One of the most important programming skills is learning to break a large problem into smaller steps.
Imagine a marketing manager asks: "Can you tell me which campaigns are performing poorly?"
That sounds simple, but it actually contains several smaller questions.
What does "poorly" mean?
Is it:
- Low revenue?
- Low conversion rate?
- High CPA?
- Low ROAS?
- Low engagement?
- Low profit?
You'll need to know what data you need, where that data is stored, how you should clean it, how to calculate the relevant metrics, and what threshold should determine whether a campaign is considered weak.
Once you break the problem apart, the programming becomes much easier.
For example:
Load campaign data. Clean the data. Calculate ROAS. Calculate conversion rate. Filter campaigns below the chosen thresholds. Display the results. Python can then turn those steps into code.
This way of thinking is called decomposition. You'll use it constantly throughout this book.
Chapter 1: Learning and Implementing the Basics
Variables
Variables allow you to store information under a name.
campaign = "Back to School" budget = 5000 clicks = 1250 conversions = 84 Now Python knows that:
campaigncontains text.budgetcontains a number.clickscontains a number.conversionscontains a number.
You can use those variables later.
print(campaign) print(budget) print(clicks) print(conversions) You can also change their values.
budget = 5000 budget = 7500 print(budget) The final value is:
7500 Variables are useful because marketing programs constantly work with changing values.
Today's revenue is different from yesterday's revenue. This month's advertising budget is different from last month's. A customer's order count changes as they make purchases. And a campaign's performance changes over time.
Variables allow your program to represent those values.
Naming Variables Clearly
You could technically write:
x = 5000 But what does x mean? You would have to look elsewhere to find out.
A better approach is:
campaign_budget = 5000 The name immediately communicates its purpose.
Similarly:
total_revenue = 25000 total_cost = 8000 total_conversions = 450 Clear variable names make your code easier to understand. This becomes increasingly important as your programs grow.
Strings
Strings represent text.
campaign_name = "Holiday Sale" channel = "Email" customer_name = "Ava" You can combine strings, for example:
message = "Welcome, " + customer_name print(message) The result is:
Welcome, Ava But f-strings are usually easier to read.
message = f"Welcome, {customer_name}" print(message) You can include multiple variables:
customer_name = "Ava" discount = 20 message = ( f"Hi {customer_name}, " f"you have a {discount}% discount." ) print(message) This becomes particularly useful when generating personalized marketing messages.
Numbers
Python supports integers and floating-point numbers.
An integer is a whole number:
customers = 1500 A float contains a decimal:
conversion_rate = 4.75 You can perform mathematical operations:
revenue = 15000 cost = 5000 profit = revenue - cost print(profit) You can multiply:
average_order_value = 75 orders = 120 revenue = average_order_value * orders print(revenue) And divide:
revenue = 12000 orders = 150 average_order_value = revenue / orders print(average_order_value) These basic operations form the foundation for many marketing metrics.
Percentages
Marketing analytics uses percentages everywhere.
Suppose 500 people clicked an advertisement out of 10,000 impressions.
The click-through rate is:
clicks = 500 impressions = 10000 ctr = clicks / impressions * 100 print(ctr) The result is:
5.0 You could format it like this:
print(f"CTR: {ctr:.2f}%") The .2f tells Python to display two decimal places.
Boolean Values
Boolean values represent two states:
True False For example:
campaign_active = True You can use Booleans with conditions.
campaign_active = True if campaign_active: print("Campaign is currently running.") Booleans become particularly useful when your program needs to make decisions.
Comparison Operators
In marketing, one metric you'll often compare is return on ad spend (ROAS). ROAS measures the revenue attributed to advertising relative to the amount spent on that advertising:
ROAS = Revenue Attributed to Ads ÷ Advertising Cost
For example, if a campaign generates $4,000 in attributed revenue from $1,000 in ad spend, its ROAS is 4.0, often described as 4:1: the campaign generated $4 in revenue for every $1 spent on ads.
ROAS isn't the same as profit because it doesn't automatically subtract product costs, agency fees, or other business expenses. The right target depends on the company's margins and goals.
Using Python, you can compare the values of ROAS using this simple line of code:
roas = 4.2 print(roas > 3) The result is:
True Other comparisons include:
roas == 3 roas != 3 roas >= 3 roas <= 3 These comparisons allow you to build rules.
if roas >= 3: print("Campaign is performing well.") Combining Conditions
Marketing decisions often involve multiple conditions.
Suppose a campaign is considered promising when both ROAS is above 3 and conversion rate is above 4 percent.
roas = 3.8 conversion_rate = 5.2 if roas >= 3 and conversion_rate >= 4: print("Campaign meets performance criteria.") The and operator requires both conditions to be true.
You can also use or:
if roas >= 4 or conversion_rate >= 6: print("Campaign has at least one strong performance signal.") These simple logical operators become extremely useful when creating automated marketing rules.
Lists
A list stores multiple values.
channels = [ "Email", "Search", "Social Media", "Display" ] You can access an item using its position, like this:
print(channels[0]) The result is:
Email Python uses zero-based indexing, meaning the first item is at position 0.
You can add an item:
channels.append("Affiliate") You can see how many items are in the list:
print(len(channels)) Lists are useful for storing collections of marketing information.
Dictionaries
Dictionaries store information as key-value pairs.
campaign = { "name": "Holiday Sale", "channel": "Email", "budget": 5000, "conversions": 125 } You can retrieve a value:
print(campaign["channel"]) You can also update a value:
campaign["budget"] = 7500 Dictionaries are particularly useful when representing a single customer, campaign, product, or marketing event.
For example:
customer = { "name": "Ava", "age": 24, "orders": 7, "total_spent": 850 } You could access:
print(customer["total_spent"]) Lists of Dictionaries
Things become much more interesting when you combine lists and dictionaries.
campaigns = [ { "name": "Spring Sale", "channel": "Email", "revenue": 8500 }, { "name": "Summer Sale", "channel": "Social Media", "revenue": 6200 }, { "name": "Holiday Sale", "channel": "Search", "revenue": 12000 } ] Now you have a simple representation of multiple campaigns.
You can loop through them:
for campaign in campaigns: print(campaign["name"]) This produces:
Spring Sale Summer Sale Holiday Sale This is the beginning of working with collections of structured data.
Loops
A loop repeats an operation.
channels = [ "Email", "Search", "Social Media", "Display" ] for channel in channels: print(channel) You could also perform calculations:
revenues = [5000, 7500, 9000, 6200] for revenue in revenues: print(revenue * 0.1) The loop processes each value.
Imagine having 10,000 customers. You wouldn't want to manually write the same operation 10,000 times.
A loop gives Python instructions for handling each record. Later in this book, you'll see how pandas can make this kind of work even more powerful and efficient.
Conditional Logic Inside Loops
You can combine loops and conditions.
campaigns = [ {"name": "Spring Sale", "roas": 2.5}, {"name": "Summer Sale", "roas": 4.8}, {"name": "Holiday Sale", "roas": 1.2} ] for campaign in campaigns: if campaign["roas"] >= 3: print( f"{campaign['name']} is performing well." ) Only the campaigns meeting the condition are printed.
This is conceptually similar to filtering a marketing dataset.
Functions
Functions package reusable logic.
Suppose you repeatedly need to calculate ROAS. Instead of writing:
roas = revenue / cost every time, create a function.
def calculate_roas(revenue, cost): return revenue / cost Then:
roas = calculate_roas( 15000, 5000 ) print(roas) You can reuse the same function:
campaign_one = calculate_roas(12000, 3000) campaign_two = calculate_roas(8000, 4000) campaign_three = calculate_roas(15000, 2500) This is much cleaner.
Adding Validation to Functions
A good function should account for problematic inputs.
Consider:
def calculate_roas(revenue, cost): return revenue / cost What happens if cost is zero?
Python will raise an error because division by zero is undefined.
A safer version is:
def calculate_roas(revenue, cost): if cost == 0: return None return revenue / cost Now:
print(calculate_roas(10000, 0)) returns:
None rather than crashing.
This idea of protecting your code from unexpected input becomes increasingly important when we begin working with real datasets.
Returning Multiple Values
A function can return multiple results.
def campaign_metrics( revenue, cost, clicks, conversions ): if cost == 0: roas = None else: roas = revenue / cost if clicks == 0: conversion_rate = None else: conversion_rate = ( conversions / clicks * 100 ) return roas, conversion_rate You can use it like this:
roas, conversion_rate = campaign_metrics( 12000, 4000, 1000, 80 ) print(roas) print(conversion_rate) Later, we'll replace or integrate functions like into reusable analytics modules.
Chapter 2: Libraries in Python
Importing Libraries
Python's built-in functionality is useful, but marketing analytics requires additional tools.
Libraries provide pre-built functionality.
For example:
import pandas as pd The as pd part gives pandas a shorter alias.
Now you can use:
pd.read_csv() rather than:
pandas.read_csv() You'll see this convention throughout data analysis projects.
NumPy is commonly imported as:
import numpy as np Matplotlib's plotting module is commonly imported as:
import matplotlib.pyplot as plt Installing Libraries
If a library isn't already installed, use pip.
pip install pandas For several libraries:
pip install pandas numpy matplotlib scikit-learn requests openpyxl The exact installation process can vary depending on your Python environment.
If you use a virtual environment, it's a good practice to install project dependencies inside that environment rather than globally.
Virtual Environments
Suppose one project requires one version of a library and another project requires a different version. Installing everything globally can create conflicts.
A virtual environment gives a project its own isolated Python environment.
You can create one like this:
python -m venv marketing-env And then activate it like this on Windows:
marketing-env\Scripts\activate Or like this on macOS or Linux:
source marketing-env/bin/activate Then install your packages:
pip install pandas numpy matplotlib scikit-learn You can save your dependencies:
pip freeze > requirements.txt Another developer can then install them with:
pip install -r requirements.txt This becomes useful when you turn your capstone into a shareable project.
Part II: Working With Marketing Data
Marketing data rarely arrives in the exact format you want. That's one of the first realities you need to accept.
A spreadsheet might contain missing values, an exported report might have inconsistent capitalization, a CRM might use one customer ID format while an advertising platform uses another, dates might appear in different formats, or numbers might be stored as strings.
A dataset can look perfectly normal while containing subtle problems that produce incorrect analysis.
Before you calculate anything, understand what you're working with.
Understanding the Marketing Data Lifecycle
A useful way to think about marketing analytics is as a lifecycle.
- Data is first collected.
- Then it's stored.
- Then it's cleaned.
- Then it's transformed.
- Then it's analyzed.
- Then the findings are communicated.
- Finally, someone uses those findings to make a decision.
Python can participate in almost every stage.
For example, Python can retrieve information from an API, store it in a DataFrame, clean it with pandas, calculate KPIs, create visualizations, train a machine learning model, and export a report.
That's why Python becomes more valuable as your marketing workflow becomes more complex.
Structured and Unstructured Data
Structured data is information organized into predictable fields.
A spreadsheet containing:
customer_id date campaign channel revenue is structured data.
Each row represents a record, and each column represents a variable. This is the kind of data pandas handles particularly well.
Unstructured data is different. Examples include:
- Social media posts
- Customer reviews
- Email text
- Survey responses
- Images
- Videos
Python can also work with unstructured data, but that often requires additional techniques such as natural language processing or computer vision.
This book primarily focuses on structured marketing data because it provides a strong foundation for analytics, automation, and machine learning.
Rows and Columns
Suppose your dataset contains:
customer_id | channel | clicks | conversions | revenue 101 | Email | 500 | 40 | 3200 102 | Search | 800 | 65 | 5100 103 | Social | 300 | 18 | 1400 Each row represents an observation, while each column represents an attribute.
Understanding this distinction is important because many pandas operations operate either across rows, across columns, or across groups of rows.
The Unit of Analysis
One concept that deserves special attention is the unit of analysis.
Ask: What does one row represent? Does it represent:
- A customer?
- A purchase?
- A campaign?
- An advertisement?
- A website session?
- An email?
- A day?
This matters enormously.
Imagine you have a dataset where every row represents a purchase. A customer who made ten purchases will appear ten times.
If you count rows and call that the number of customers, your analysis will be wrong.
You would instead need the number of unique customer IDs.
df["customer_id"].nunique() The unit of analysis should always be understood before calculating metrics.
Marketing Metrics Are Context-Dependent
Suppose Campaign A has a 10 percent conversion rate.
That sounds excellent, but what if it generated only ten visitors and one conversion?
Now suppose Campaign B has a 4 percent conversion rate but generated 100,000 visitors and 4,000 conversions.
The percentage alone doesn't tell the complete story.
This is why good marketing analytics considers other factors, like:
- Volume
- Rate
- Cost
- Revenue
- Profitability
- Customer quality
- Time period
- Business objective
A metric isn't an answer by itself. It's evidence.
Chapter 3: Working With CSV Data
CSV stands for Comma-Separated Values, and it's one of the simplest and most common ways of exchanging tabular data.
Suppose campaigns.csv contains:
campaign,channel,impressions,clicks,conversions,cost,revenue Spring Sale,Email,10000,850,120,500,6000 Summer Sale,Social Media,25000,1100,75,1200,4200 Holiday Sale,Search,18000,1400,180,2000,9500 Load it:
import pandas as pd df = pd.read_csv("campaigns.csv") Inspect the first five rows:
df.head() You can see more rows if you want:
df.head(10) Or the last five:
df.tail() Understanding DataFrame Shape
Use:
df.shape You might receive:
(1000, 8) That means the DataFrame contains 1,000 rows and 8 columns.
This is one of the quickest ways to understand the size of your dataset.
Viewing Column Names
df.columns You might see:
Index([ 'campaign', 'channel', 'impressions', 'clicks', 'conversions', 'cost', 'revenue' ], dtype='object') If you're writing reusable code, knowing the exact column names is important.
Selecting a Column
Select one column:
df["revenue"] Or select multiple columns:
df[ [ "campaign", "revenue", "cost" ] ] This lets you create smaller views of your dataset.
Selecting Rows
You can select rows using conditions.
df[df["revenue"] > 5000] This returns campaigns where revenue is greater than $5,000.
You can combine conditions:
df[ (df["revenue"] > 5000) & (df["cost"] < 3000) ] Parentheses are important when combining pandas conditions because each comparison needs to be evaluated as a complete condition before pandas combines the results. Operators such as & and | have different precedence rules from Python's ordinary and and or, so leaving out parentheses can cause an error or an unintended expression.
Put each condition in its own parentheses, and use & for AND and | for OR when filtering a DataFrame.
For example:
# Select campaigns with more than 1,000 clicks # and a conversion rate above 2% filtered_campaigns = df[ (df["clicks"] > 1000) & (df["conversion_rate"] > 0.02) ] Here, both conditions must be true for a row to be included in filtered_campaigns.Sorting Data
Sort by revenue:
df.sort_values( "revenue", ascending=False ) This places the highest-revenue campaigns first.
Sort by cost:
df.sort_values( "cost", ascending=True ) You can sort by multiple columns:
df.sort_values( ["channel", "revenue"], ascending=[True, False] ) This first groups the results by channel and then orders revenue within each channel.
Chapter 4: Working With Excel
Many marketing departments still rely heavily on Excel.
Fortunately, pandas makes it relatively easy to work with Excel files.
df = pd.read_excel( "marketing_data.xlsx" ) You can specify a worksheet:
df = pd.read_excel( "marketing_data.xlsx", sheet_name="Campaigns" ) You can inspect available sheets using:
excel_file = pd.ExcelFile( "marketing_data.xlsx" ) print(excel_file.sheet_names) This is useful when working with large workbooks.
Exporting Data to Excel
You can export a DataFrame:
df.to_excel( "marketing_report.xlsx", index=False ) You can create multiple worksheets:
with pd.ExcelWriter( "marketing_report.xlsx" ) as writer: df.to_excel( writer, sheet_name="Data", index=False ) channel_summary.to_excel( writer, sheet_name="Channels" ) This capability will become important in the capstone because the final system will produce a structured marketing report.
Chapter 5: Dates and Time
Marketing performance is highly dependent on time.
A campaign that generated $20,000 in revenue over six months is very different from one that generated $20,000 in revenue over two days.
Start by converting date columns:
df["date"] = pd.to_datetime( df["date"] ) Now you can extract components.
df["year"] = df["date"].dt.year df["month"] = df["date"].dt.month df["day"] = df["date"].dt.day You can extract the month name:
df["month_name"] = ( df["date"] .dt.month_name() ) And weekday:
df["weekday"] = ( df["date"] .dt.day_name() ) Time-Based Marketing Questions
Once dates are properly formatted, you can answer questions such as:
- Which month generated the most revenue?
- Which weekday has the highest conversion rate?
- Did campaign performance improve after a specific change?
- Is there seasonal behavior?
- Are customer purchases becoming less frequent?
For example:
monthly_revenue = ( df.groupby( df["date"].dt.to_period("M") )["revenue"] .sum() ) This produces monthly revenue totals.
Chapter 6: Working With Marketing APIs
A file is useful when someone manually exports data. But what if you want your marketing intelligence system to collect data automatically?
This is where APIs become important.
An API, or Application Programming Interface, provides a structured way for one program to communicate with another.
For example, a platform might expose an endpoint that returns campaign information.
Python can send a request:
import requests response = requests.get( "https://example.com/api/campaigns" ) print(response.status_code) A successful response commonly has a status code such as:
200 You can inspect the response body:
print(response.text) If the response contains JSON:
data = response.json() You can inspect it:
print(data) API Authentication
Real APIs often require authentication.
For example:
api_key = "your-api-key" But you should not place actual secret keys directly inside code that you intend to share publicly.
Instead, use environment variables.
import os api_key = os.getenv( "MARKETING_API_KEY" ) You can then construct headers:
headers = { "Authorization": f"Bearer {api_key}" } And make the request:
response = requests.get( "https://example.com/api/campaigns", headers=headers ) The exact authentication system depends on the API.
API Errors
Your program shouldn't assume that every request succeeds.
response = requests.get( api_url, timeout=30 ) if response.status_code == 200: data = response.json() else: print( f"Request failed: " f"{response.status_code}" ) When you send an API request, receiving a response doesn't necessarily mean the request succeeded. The server might return an error status, such as 404 Not Found or 500 Internal Server Error.
The raise_for_status() method checks the HTTP response status. If the response indicates an unsuccessful request, it raises an exception instead of allowing your program to continue as if everything worked.
A try block lets you handle that exception:
import requests try: response = requests.get( "https://example.com/api/campaigns", timeout=30 ) response.raise_for_status() campaigns = response.json() except requests.exceptions.RequestException as error: print(f"Could not retrieve campaign data: {error}") Here, requests.get() sends the request, timeout=30 prevents the program from waiting indefinitely, and raise_for_status() raises an exception for an unsuccessful HTTP response. The except block catches request-related errors and displays a message.
In a production workflow, you should also log the error and decide whether the process should retry, skip the failed source, or stop report generation. Avoid silently treating a failed request as an empty dataset because that could produce misleading marketing reports.
This becomes especially important when your system runs automatically. If an API goes down at 3 AM, you want the program to report the problem rather than silently producing an empty marketing report.
API Pagination
Some APIs return only a limited number of records per request. If an account contains thousands of campaign records, for example, the API might return the first 100 and provide a link or token for retrieving the next group. This process is called pagination.
The pagination method depends on the API. Some services use page numbers, others use offsets, and some provide a continuation token or a URL for the next page. Always check the API's documentation before implementing pagination.
The following example demonstrates an API that returns a results list and a next URL:
import requests def fetch_all_campaigns(api_url, headers): all_campaigns = [] next_url = api_url while next_url: response = requests.get( next_url, headers=headers, timeout=30 ) response.raise_for_status() payload = response.json() all_campaigns.extend(payload["results"]) next_url = payload.get("next") return all_campaigns The function starts with the initial API URL and continues requesting pages while next_url contains a URL. The extend() method adds the records from each page to the same list. When the API returns no next-page URL, the loop stops.
This is an illustrative pattern, not a universal solution. Your API might use different field names or require a different pagination method. For a real integration, follow the service's documentation and consider safeguards such as retry limits and maximum page counts.
API Rate Limits
APIs often limit how many requests a client can make within a particular period. These restrictions are called rate limits. For example, a service might allow only a certain number of requests per minute.
If your marketing automation script sends too many requests, the API may return an HTTP 429 Too Many Requests response. Instead of repeatedly sending requests immediately, your program should pause and try again according to the service's rules.
Some APIs provide a Retry-After response header that indicates how long the client should wait. Here's a simplified example:
import time import requests def get_with_rate_limit(url, headers, max_retries=5): for attempt in range(max_retries): response = requests.get( url, headers=headers, timeout=30 ) if response.status_code == 429: retry_after = response.headers.get( "Retry-After", "2" ) time.sleep(float(retry_after)) continue response.raise_for_status() return response raise RuntimeError( "API request failed after repeated rate-limit responses." ) This function retries a limited number of times when the API returns a 429 response. If the response includes a Retry-After header, the function uses that value to determine how long to wait. If the header is absent, it waits for two seconds.
This is a starting point rather than a complete production retry system. Some APIs use different retry rules, and production systems should also consider temporary server errors, logging, exponential backoff, and safeguards against excessive retries. Always follow the API provider's documentation.
Rate-limit handling is particularly important when automating reports across multiple campaigns or accounts. Respecting these limits makes your integration more reliable and reduces the chance of interrupted data collection.
Part III: Data Cleaning and Analysis with Pandas
The first rule of marketing analytics should be simple: Don't trust your dataset until you've inspected it.
That doesn't mean your data is bad. It just means you should verify it before analyzing it and making decisions based off of it.
A dataset can contain millions of perfectly formatted rows and still represent the wrong thing.
Chapter 7: Inspecting Data Quality
Start with:
df.info() This provides information about:
- Number of rows
- Column names
- Missing values
- Data types
- Memory usage
You can also use:
df.describe() This gives statistical information for numerical columns.
For example, you might discover that:
revenue count 1000 mean 5820 min -250 max 85000 The negative minimum should immediately make you investigate.
A negative revenue value might be legitimate if it represents a refund, or it could indicate a data-entry error.
The number alone doesn't tell you which.
Missing Values
Check for missing values with:
df.isnull().sum() You might see:
customer_id 0 campaign 3 channel 0 revenue 12 cost 4 Now you know exactly where missing information exists.
Why Missing Data Matters
Suppose you calculate average revenue:
df["revenue"].mean() pandas generally excludes missing values from the calculation.
That may be convenient, but you still need to understand why the values are missing.
If the missing values are random, one approach may work. If all missing revenue values come from one specific sales channel, simply ignoring them could bias your conclusions.
Missing data isn't merely a technical problem. It can change the conclusions you draw from your marketing analysis.
Imagine that several campaign records have no revenue value. If you replace every missing value with zero, you're assuming those campaigns generated no revenue. But a missing value could mean that revenue tracking failed, the data hasn't arrived yet, or the purchase information is stored somewhere else.
Those situations aren't equivalent.
Before filling or removing missing values, ask yourself:
- Why is the value missing?
- Does a missing value genuinely mean zero, or is the information unknown?
- How many records are affected?
- Could removing those records bias the analysis?
For example, filling missing advertising costs with zero could cause a campaign's return on ad spend to become misleading or undefined. Similarly, removing every row with a missing customer attribute could exclude an important group of customers from your analysis.
Use fillna() or dropna() only after deciding what the missing values mean in the context of your dataset. Document important assumptions so that other people can understand how the results were produced.
Duplicate Records
Check duplicates like this:
df.duplicated().sum() If the result is 25, you have 25 rows that are exact duplicates.
You can remove them like this:
df = df.drop_duplicates() But remember the unit of analysis.
Two rows with the same customer ID aren't necessarily duplicates, as a customer can make multiple purchases.
Two rows with the same campaign name aren't necessarily duplicates, as a campaign may have data recorded across multiple days.
Never delete records merely because they share one or two values. Always check those values first.
Chapter 8: Cleaning Text Data
Marketing datasets often contain inconsistent text.
You may find:
Email email EMAIL Email Marketing Python treats these as different values.
Start by standardizing case:
df["channel"] = ( df["channel"] .str.lower() ) Then remove unnecessary whitespace:
df["channel"] = ( df["channel"] .str.strip() ) Now this:
Email email EMAIL will all become standardized to this:
email You can also standardize specific labels:
df["channel"] = df["channel"].replace({ "email marketing": "email", "paid social": "social media", "social": "social media" }) Why Standardization Matters
Imagine you calculate revenue by channel before cleaning the data.
You might get:
email $20,000 Email $15,000 EMAIL $8,000 The business may actually have generated $43,000 from one channel, but if you haven't standardized, your analysis would incorrectly treat those as separate channels.
This is a small example of how data quality can affect business decisions.
Chapter 9: Cleaning Numeric Data
Numbers sometimes arrive in a dataset as text instead of as numerical values. This is especially common when working with marketing reports exported from spreadsheets, advertising platforms, or customer relationship management (CRM) systems.
For example, a revenue column might contain these values:
"$5,000" "$7,500" "$2,300" Although these values represent money, Python may store them as strings because they contain characters such as dollar signs and commas. A string is a sequence of characters, not a number that Python can directly use in mathematical calculations.
For example, Python can't reliably calculate a total or average from strings containing currency symbols. Before calculating marketing metrics, you need to clean these values and convert them into an appropriate numeric type.
Removing Currency Symbols and Separators
The first step i...
![Python in Marketing: A Practical Guide to Marketing Analytics, Automation, and Data-Driven Growth [Full Book]](https://cdn.hashnode.com/uploads/covers/6a581501af6af179dc1987d5/7be7abc8-7b4d-43c4-b8b7-42991d9a3901.png)