Basic Structure
Every custom report has three components:- Level — Campaign, adset, or ad
- Fields — Metrics to include (spend, CTR, CPC, etc.)
- Breakdowns — How to slice the data (age, gender, device, etc.)
Command Syntax
The Level Parameter
Controls which object type you’re analyzing.- Campaign
- Adset
- Ad
Use when: You want account-level or campaign-level performance.Example Output:
Available Fields
Specify which metrics to include in your report.Performance Metrics
| Field | Description |
|---|---|
spend | Total amount spent |
impressions | Number of times ad was shown |
clicks | Number of clicks |
ctr | Click-through rate (clicks/impressions) |
cpc | Cost per click |
cpm | Cost per 1,000 impressions |
frequency | Average times each person saw the ad |
reach | Number of unique people who saw the ad |
Conversion Metrics
| Field | Description |
|---|---|
conversions | Total conversions (based on optimization goal) |
cost_per_conversion | Spend / conversions |
conversion_rate | Conversions / clicks |
purchases | Purchase events (if tracking) |
purchase_value | Total purchase value |
roas | Return on ad spend (purchase_value / spend) |
Engagement Metrics
| Field | Description |
|---|---|
post_engagement | Reactions, comments, shares |
video_views | 3-second video views |
video_avg_watch_time | Average watch time |
link_clicks | Clicks to destination URL |
Identity Fields
| Field | Description |
|---|---|
campaign_name | Campaign name |
adset_name | Ad set name |
ad_name | Ad name |
campaign_id | Campaign ID |
adset_id | Ad set ID |
ad_id | Ad ID |
Not all fields are available at all levels. For example,
ad_name only works with --level ad.Breakdowns
Slice your data by demographic, placement, or device.Demographic Breakdowns
- Age
- Gender
- Age + Gender
Performance by age range.Example Output:Insights:
- 25-34 is the highest performing age range (3.4% CTR, 14 conversions)
- Consider shifting budget to 25-44 age range
Placement Breakdowns
- Platform
- Device
Facebook vs Instagram performance.Example Output:Insights:
- Instagram placements outperform Facebook (3.4% vs 2.8% CTR)
- Instagram has lower CPC (1.12)
- Consider shifting budget to IG placements
Time Breakdowns
Use
--time-increment 1 for daily breakdowns. This is how the fatigue check works under the hood.Real-World Examples
Example 1: Find Best Performing Demographics
Goal: Identify which age/gender combo drives the most conversions.- Sort by
cost_per_conversion(lowest = best) - Identify winning segments (e.g., Women 25-34)
- Create dedicated ad sets targeting those segments
- Exclude poor performers from broad targeting
Example 2: Instagram vs Facebook Performance
Goal: Decide where to allocate budget.- Compare CTR, CPC, and ROAS across platforms
- If Instagram outperforms, shift budget there
- Or create platform-specific creatives optimized for each
Example 3: Track Creative Fatigue Over Time
Goal: See day-by-day CTR decline for a specific ad.- Plot CTR over time
- Identify when decline started
- Correlate with frequency — does fatigue start at frequency > 3?
- Use this to predict when future creatives will fatigue
Example 4: Audience Comparison
Goal: Which audience (adset) is most efficient?- Sort by
cost_per_conversion - Check frequency — audiences with frequency > 3 are saturated
- Scale winners (low CPA, frequency < 2.5)
- Pause or refresh fatigued audiences (high frequency, rising CPA)
Advanced: Combining Reports
Chain multiple custom reports to build a full analysis.Workflow: Optimize an Underperforming Campaign
Tips for Custom Reports
Keep field lists short
Keep field lists short
Requesting too many fields slows down the API and makes output hard to read.Good:Too much:Request only the metrics you need for the specific analysis.
Use breakdowns strategically
Use breakdowns strategically
Each breakdown multiplies the number of rows in the output. Combining 3+ breakdowns can create overwhelming data.Good for exploration:Too granular (hard to interpret):
Match preset to analysis type
Match preset to analysis type
| Analysis Type | Recommended Preset |
|---|---|
| Daily checks | last_7d |
| Fatigue detection | last_14d with --time-increment 1 |
| Monthly reporting | last_30d |
| Audience testing | last_7d (enough signal without noise) |
Export to CSV for deeper analysis
Export to CSV for deeper analysis
Pipe output to a CSV for analysis in Excel/Sheets:
Common Questions
What's the difference between custom reports and built-in reports?
What's the difference between custom reports and built-in reports?
Built-in reports (bleeders, winners, fatigue) are pre-configured for specific use cases and include analysis + recommendations.Custom reports are raw data queries — you get the metrics you ask for, but no interpretation.Use built-in reports for daily workflow.
Use custom reports for deep dives and one-off analysis.
Can I filter results (e.g., only ads with spend > $100)?
Can I filter results (e.g., only ads with spend > $100)?
Not directly in the command. Instead:
- Run the full report
- Export to CSV
- Filter in Excel/Sheets/Python
social-cli command with filtering:Why are some metrics missing in the output?
Why are some metrics missing in the output?
Possible reasons:
- Metric not available at that level: E.g.,
ad_nameonly works with--level ad - Insufficient data: If the time range is too short, some metrics won’t populate
- Conversion tracking not set up: Metrics like
purchasesrequire proper event tracking
Next Steps
Reports Overview
See all available reports and when to use each
Fatigue Detection
Learn how to use time-increment reports to catch fatigue