Create a modular pillar asset, then map it to ten distribution channels using a content matrix, automation scripts, and a unified editorial calendar.
Step‑by‑step workflow
1. Research & pillar definition – Use Ahrefs Content Gap (--mode=gap) to identify 5‑keyword clusters; select the cluster with ≥2 k monthly searches and ≤0.25 KD.
2. Modular asset creation – Write a 2,500‑word long‑form piece in Google Docs, embed 3‑minute video (YouTube API videos.insert) and 5‑slide deck (Google Slides API presentations.create).
3. Channel matrix – Populate a CSV (channel,format,hook,length,CTA) e.g.:
| Channel | Format | Hook | Length | CTA |
|--------|--------|------|--------|-----|
| LinkedIn | Carousel | Data point | 8 slides | Lead gen |
| Twitter | Thread | Quote | ≤280 char each | Link |
| Instagram Reels | 30‑s video | Visual tip | 30 s | Swipe‑up |
| ... | ... | ... | ... | ... |
4. Automation – Python script (see below) reads the CSV, calls:
- facebook_graph_api.publish for FB posts,
- twitter_api.update_status for threads,
- linkedin_api.shareArticle for articles,
- tiktok_api.upload_video with --watermark=false.
5. Scheduling – Load the script into Airflow DAG with schedule_interval='0 9 MON,THU'; set retries=2 and retry_delay=5m.
6. Performance tracking – Push UTM‑tagged URLs to Snowflake; run a daily dbt model calculating CTR = clicks/impressions and flag any channel below 0.8 % for review.
7. Iterate – Every 30 days, rerun Ahrefs to refresh keyword clusters and adjust the matrix.
import pandas as pd, requests, json, time
df = pd.read_csv('matrix.csv')
for _,row in df.iterrows():
payload = {"text": row['hook'], "link": row['CTA']}
if row['Channel']=='Twitter':
requests.post('https://api.twitter.com/2/tweets', json=payload, headers={'Authorization':f'Bearer {TOKEN}'})
elif row['Channel']=='LinkedIn':
requests.post('https://api.linkedin.com/v2/shares', json=payload, headers={'Authorization':f'Bearer {LI_TOKEN}'})
time.sleep(31) # respect LinkedIn rate limit
# add other channel branches hereGotcha: LinkedIn’s API enforces a 30‑second delay between successive shareArticle calls; exceeding it returns 429 and stalls the DAG, so add time.sleep(31) after each LinkedIn publish.