Workflow Teardown: Building an Automated Competitor Ad Analysis Pipeline
Your competitors are shipping new ad creatives every single day. Manually tracking and analyzing these campaigns is impossible at scale. In this workflow teardown, we explore a production-ready automated competitor ad analysis pipeline that captures ad creatives, converts them to markdown using ocrskill, and leverages a secondary AI model to structure that text for downstream competitive intelligence.
The Ad Analysis Automation Architecture
A robust ad tracking automation pipeline consists of four distinct stages:
- Capture - A headless browser scrapes ad libraries (e.g., Meta Ad Library, Google Ads Transparency Center).
- Transcribe - ocrskill converts each visual ad creative into clean, readable markdown.
- Structure - A separate Large Language Model (LLM) maps the extracted markdown into a strict JSON schema.
- Report - Structured competitive data is pushed to BI dashboards and Slack alerts.
Why OCR Matters for Competitor Ad Tracking
Ad creatives are designed to be visually compelling to humans, not machine-readable for scrapers. Critical text is embedded in images, overlaid on videos, and styled in ways that easily defeat simple HTML scraping.
ocrskill handles the complex OCR (Optical Character Recognition) layer, transforming those visuals into markdown that preserves both the readable text and the original layout cues.
This distinction is crucial: ocrskill is not the system deciding what constitutes a headline, a Call-To-Action (CTA), a special offer, or legal disclaimers. It is purely the OCR data extraction step. The actual schema mapping and intelligence gathering happen afterward using a general-purpose AI model.
Step 1: OCR Data Extraction to Markdown
import asyncio
from openai import AsyncOpenAI
ocr_client = AsyncOpenAI(
base_url="https://api.ocrskill.com/v1",
api_key="sk-your-key"
)
async def image_to_markdown(image_url: str) -> str:
response = await ocr_client.chat.completions.create(
model="ocrskill-v1",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Convert this ad creative to markdown. Preserve all visible text and basic structure."},
{"type": "image_url", "image_url": {"url": image_url}}
]
}]
)
return response.choices[0].message.content
The output from this initial step is raw markdown, not structured JSON. A typical result contains the main headline, supporting body text, offer language, and legal copy, exactly in the order it appears within the ad image.
Step 2: Structuring AI Ad Intelligence with an LLM
Once the ad creative is transcribed into markdown, a secondary AI model normalizes the text into structured fields for analysis. This is where you infer and extract specific competitive intelligence data points:
brand_nameheadlinecta_textoffer_detailsfine_printmessaging_theme
import json
from openai import AsyncOpenAI
llm_client = AsyncOpenAI(api_key="sk-your-openai-key")
AD_SCHEMA = {
"type": "object",
"properties": {
"brand_name": {"type": "string"},
"headline": {"type": "string"},
"cta_text": {"type": "string"},
"offer_details": {"type": "string"},
"fine_print": {"type": "string"},
"messaging_theme": {"type": "string"}
},
"required": [
"brand_name",
"headline",
"cta_text",
"offer_details",
"fine_print",
"messaging_theme"
],
"additionalProperties": False
}
async def structure_ad_markdown(markdown: str) -> dict:
response = await llm_client.responses.create(
model="gpt-5-mini",
input=[
{
"role": "system",
"content": (
"You convert OCR markdown from ad creatives into a strict JSON object. "
"Do not invent facts that are not present in the markdown."
),
},
{
"role": "user",
"content": f"Convert this ad creative markdown into JSON:\n\n{markdown}",
},
],
text={
"format": {
"type": "json_schema",
"name": "ad_creative",
"schema": AD_SCHEMA,
"strict": True
}
}
)
return json.loads(response.output_text)
Why Splitting OCR and Structuring Works Best
Separating the OCR extraction from the data structuring phase makes your automated pipeline significantly more reliable:
- Accuracy: ocrskill focuses solely on converting the image into highly accurate markdown.
- Flexibility: The secondary LLM focuses on schema mapping and light semantic interpretation.
- Efficiency: You can re-run the structuring step with updated schemas without needing to re-process the heavy image files.
- Quality Assurance: You maintain a human-readable markdown record for easy QA and debugging.
Scaling the Pipeline with Asynchronous Processing
To handle large daily batches of competitor ads efficiently, the pipeline parallelizes both steps: first the OCR extraction, followed by schema normalization.
async def process_creative(image_url: str) -> dict:
markdown = await image_to_markdown(image_url)
return await structure_ad_markdown(markdown)
async def process_batch(image_urls: list[str]) -> list[dict]:
tasks = [process_creative(url) for url in image_urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
return [item for item in results if not isinstance(item, Exception)]
The Competitive Analysis Layer
Once the raw markdown is normalized into a consistent JSON schema, you can seamlessly aggregate and analyze your competitor’s marketing strategies across multiple platforms:
- Messaging Trends: What messaging themes and angles are competitors doubling down on?
- Conversion Tactics: How are Call-to-Actions (CTAs) evolving month over month?
- Offer Strategy: Which promotions and offers correlate with increased ad spend?
Results and Business Impact
Implementing this two-step architecture gives marketing and data teams a cleaner, more robust foundation for competitive intelligence:
- OCR output remains entirely faithful to the original ad image.
- Structured fields are generated transparently in a separate, isolated step.
- New data extraction schemas can be introduced without altering or breaking the core OCR layer.
- Analysts can easily inspect the intermediate markdown whenever the final JSON output requires verification.
Key Takeaway for Ad Tracking Automation
The right mental model for this workflow is not “ocrskill magically extracts structured ad data.” A more accurate and scalable model is: “ocrskill turns the visual creative into markdown, and another AI model structures that markdown for analysis.”
This architectural separation makes your automated competitor ad analysis pipeline easier to debug, simpler to evolve, and aligns perfectly with how modern, real-world automation stacks are engineered.
