OnlyFans Chatter Training: Scripts, KPIs, and What Top Performers Do Differently
We've trained 40+ chatters. The gap between top and bottom performers is consistently 400% in revenue per conversation. Here's the data on what separates them — and how to build it into your training program.
The gap between our top and bottom chatters is not small. It is 400%. Top performers generate $40–$50 per conversation. Everyone else does $12–$15.
We have trained more than 40 chatters over two years. Some came in green — no experience, no frame of reference, learning from scratch. Others came in with scripts they had used before, confident they already knew what worked. The experienced ones were not consistently better. Not even close.
The pattern that actually predicted performance was one thing: whether someone could look at conversation-level data and change their behavior based on what they saw. Top chatters are not married to a script. They are married to the outcome.
Across 40+ chatters and roughly 180,000 conversations, four behaviors separated top performers from everyone else. Not skills, not charisma, not experience. Four specific, trainable, measurable behaviors. Here is the data on what they do differently, how to build those behaviors into your training program, and the code to generate the weekly performance report that makes the whole system work.
What We Measured and How
Before we could train around data, we needed to measure the right things. The instinct is to track messages sent and revenue generated, then divide. But that misses the behavioral layer — the specific actions that explain why some chatters generate more revenue.
We built attribution at the conversation level using /chats, /chats/{chatId}/messages, and /payouts/transactions. For each conversation, we tracked:
- Time from fan’s first message to chatter’s first response
- Whether the chatter used the fan’s name in the first three messages
- Whether the chatter referenced prior purchase history
- Time from last chatter message to PPV offer (if one was sent)
- Time from PPV offer to purchase (if purchase happened)
- Total messages sent per conversation
- Revenue attributed within a 4-hour window of the conversation
Four behaviors emerged. They were consistent across every creator account, every chatter cohort, every time period we ran the analysis.
Behavior 1: Response Time Under 3 Minutes
The most consistent predictor of conversion we found. Conversations where the first response came within 3 minutes converted at 31%. Between 3 and 10 minutes: 22%. Between 10 and 30 minutes: 14%. Over 30 minutes: 8%.
Nearly 4x conversion rate difference based entirely on response speed — before a single word of the conversation content matters.
The mechanism is not mysterious. Fans who open a conversation are in an active engagement moment. They are on the app, thinking about the creator, receptive. Every minute that passes is a minute of that window closing. By the time a 45-minute delayed response arrives, the fan has moved on.
Top chatters treat the first response as the most important message in any conversation. They may not close the PPV in the first exchange, but they protect the window by being there when it opens.
Training implication: Set a hard KPI of sub-3-minute first response on 90% of incoming conversations during active shift hours. Track it weekly. Make it visible. It is the single highest-leverage behavior change with the clearest performance link.
Behavior 2: Personalization in the First Two Messages
Generic openers kill conversion. “Hey! How are you doing today?” signals to every fan that they are not a person — they are an inbox item. Top chatters do not open that way.
In our transcript analysis, top chatters’ opening messages fell into two patterns:
Pattern A — Reference to purchase history: “Hey! I saw you grabbed that [content type] last month — she’s got something similar dropping this week that I think you’d actually like even more.”
Pattern B — Reference to account tenure or engagement: “Hey, been a while since I heard from you — you’ve been a member since [month], right? She was actually asking about some of her day-one fans the other day.”
Both patterns do the same thing: they tell the fan this is not a form letter. Someone actually knows who they are. That shift in tone changes the entire dynamic of the conversation.
The fan data to make this possible comes directly from the API. Before a chatter opens a conversation, they should have the fan’s purchase history, subscription start date, and lifetime value visible. That is three fields from /fans/info and /payouts/transactions. The training imperative is teaching chatters to actually use that information in their first message — not skim past it.
Training implication: Require that every opening message contains at least one piece of fan-specific information. Review opening messages weekly in group training and flag generic openers for correction. Zero tolerance for “Hey! How are you today?” on accounts with fan data available.
Behavior 3: PPV Framing — “Made This Thinking of You” vs. “New PPV Available”
This was the finding that surprised us most. The difference in language looks small. The conversion impact was not.
We tagged every PPV offer message across our chatter pool and classified the framing into two categories:
Transactional framing: “She just posted a new PPV — $18, really good content.” “New video available in her vault.” “Check out her latest PPV drop.”
Personal framing: “She actually made this thinking of her regulars — the people who really get her.” “This one’s not for everyone, but I think you’d love it based on what you’ve told me before.” “She filmed this specifically for fans who’ve been around since the beginning.”
Transactional framing converted at 9%. Personal framing converted at 24%. Same PPV, same price point, same creator — different framing, nearly 3x the conversion rate.
Personal framing works because it reframes the purchase from a transaction into an invitation. The fan is not buying a video — they are being recognized as someone who deserves to see this. For fans who are emotionally invested in the creator, that distinction matters more than the price.
Training implication: Build a library of personal framing templates and rotate them through the team. Train chatters to connect the PPV offer to something specific about that fan — their stated preferences, their purchase history, their tenure. Never allow generic “new PPV available” framing from a chatter who has fan context to work with.
Behavior 4: PPV Send Timing Within 15 Minutes of a Tip
Tips are the highest-intent signal a fan can send. A fan who just tipped is in an active generosity state — emotionally engaged, already made a financial gesture, warm in a way a fan who last interacted two weeks ago simply is not.
Our data showed that PPV offers sent within 15 minutes of a tip converted at 41%. The same offers sent 1–4 hours after a tip converted at 18%. After 4 hours: 11%.
The window is real and it closes fast. Top chatters have tip notifications set up so a tip triggers an immediate alert. They do not let an hour go by before following up. They are in the conversation within 15 minutes, acknowledging the tip with a genuine response, and making a PPV offer that feels like a natural next step rather than an interruption.
Training implication: This is an operational requirement as much as a training item. Chatters need tip alerts on the accounts they manage, and they need a clear protocol: tip received → acknowledge within 5 minutes → PPV offer within 15 minutes → log the outcome. Make this muscle memory before chatters go live.
The Weekly Chatter Performance Report
All of this is only actionable if you are measuring it consistently. Here is the code that generates our weekly chatter performance report, pulling from /chats, /chats/{chatId}/messages, and /payouts/transactions:
import requests
from datetime import datetime, timedelta
from collections import defaultdict
import statistics
API_BASE = "https://api.ofapi.dev/api/v1"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
def generate_weekly_chatter_report(creator_id: str, week_start: str, week_end: str) -> list:
"""
Generate a weekly performance report for all chatters on a creator account.
Tracks the four key behavioral metrics tied to conversion.
"""
# Pull all conversations for the week
chats_resp = requests.get(
f"{API_BASE}/chats",
headers=HEADERS,
params={
"creator_id": creator_id,
"start_date": week_start,
"end_date": week_end,
}
)
chats = chats_resp.json().get("chats", [])
# Pull all transactions for attribution
txn_resp = requests.get(
f"{API_BASE}/payouts/transactions",
headers=HEADERS,
params={
"creator_id": creator_id,
"start_date": week_start,
"end_date": week_end,
}
)
transactions = txn_resp.json().get("transactions", [])
# Build fan purchase lookup for attribution
fan_purchases = defaultdict(list)
for txn in transactions:
if txn["type"] in ("ppv_purchase", "tip", "subscription_renewal"):
fan_purchases[txn["fan_id"]].append({
"ts": datetime.fromisoformat(txn["created_at"]),
"amount": txn["net_amount"],
"type": txn["type"],
})
# Per-chatter stats
chatter_data = defaultdict(lambda: {
"conversations": 0,
"response_times_sec": [],
"used_personalization": 0,
"used_personal_ppv_framing": 0,
"ppv_offers_sent": 0,
"ppv_purchases": 0,
"tip_followup_within_15m": 0,
"tip_followup_total": 0,
"revenue_attributed": 0.0,
"total_messages_sent": 0,
})
# Personal framing keywords (simplified detection)
personal_framing_signals = [
"thinking of you", "made this for", "specifically for",
"i think you'd", "based on what you", "she made this",
"fans like you", "regulars", "day-one"
]
personalization_signals = [
"since you", "you grabbed", "you've been", "i saw you",
"your last", "member since", "you told me"
]
attribution_window = timedelta(hours=4)
for chat in chats:
chat_id = chat["id"]
fan_id = chat["fan_id"]
msg_resp = requests.get(
f"{API_BASE}/chats/{chat_id}/messages",
headers=HEADERS,
params={"start_date": week_start, "end_date": week_end}
)
messages = sorted(
msg_resp.json().get("messages", []),
key=lambda m: m["created_at"]
)
if not messages:
continue
chatter_msgs = [m for m in messages if m.get("sender_type") == "chatter"]
fan_msgs = [m for m in messages if m.get("sender_type") == "fan"]
if not chatter_msgs:
continue
chatter_id = chatter_msgs[0].get("chatter_id", "unknown")
stats = chatter_data[chatter_id]
stats["conversations"] += 1
stats["total_messages_sent"] += len(chatter_msgs)
# Response time: first fan message -> first chatter response
if fan_msgs and chatter_msgs:
first_fan_ts = datetime.fromisoformat(fan_msgs[0]["created_at"])
first_chatter_ts = datetime.fromisoformat(chatter_msgs[0]["created_at"])
if first_chatter_ts > first_fan_ts:
response_sec = (first_chatter_ts - first_fan_ts).total_seconds()
stats["response_times_sec"].append(response_sec)
# Personalization in first 2 chatter messages
first_two_chatter = [m["content"].lower() for m in chatter_msgs[:2]]
first_two_text = " ".join(first_two_chatter)
if any(sig in first_two_text for sig in personalization_signals):
stats["used_personalization"] += 1
# PPV framing analysis
for msg in chatter_msgs:
content_lower = msg.get("content", "").lower()
if msg.get("contains_ppv_offer"):
stats["ppv_offers_sent"] += 1
if any(sig in content_lower for sig in personal_framing_signals):
stats["used_personal_ppv_framing"] += 1
# Tip followup timing
fan_tips = [p for p in fan_purchases.get(fan_id, []) if p["type"] == "tip"]
for tip in fan_tips:
stats["tip_followup_total"] += 1
# Find first chatter message after tip
followup_msgs = [
m for m in chatter_msgs
if datetime.fromisoformat(m["created_at"]) > tip["ts"]
]
if followup_msgs:
followup_ts = datetime.fromisoformat(followup_msgs[0]["created_at"])
if (followup_ts - tip["ts"]).total_seconds() <= 900: # 15 minutes
stats["tip_followup_within_15m"] += 1
# Revenue attribution (4-hour window from last chatter message)
last_chatter_ts = datetime.fromisoformat(chatter_msgs[-1]["created_at"])
for purchase in fan_purchases.get(fan_id, []):
delta = purchase["ts"] - last_chatter_ts
if timedelta(0) <= delta <= attribution_window:
stats["revenue_attributed"] += purchase["amount"]
if purchase["type"] == "ppv_purchase":
chatter_data[chatter_id]["ppv_purchases"] = (
chatter_data[chatter_id].get("ppv_purchases", 0) + 1
)
# Build final report
report = []
for chatter_id, s in chatter_data.items():
convs = s["conversations"]
if convs == 0:
continue
avg_response_sec = (
statistics.mean(s["response_times_sec"]) if s["response_times_sec"] else None
)
report.append({
"chatter_id": chatter_id,
"conversations": convs,
"revenue_attributed": round(s["revenue_attributed"], 2),
"revenue_per_conversation": round(s["revenue_attributed"] / convs, 2),
"avg_response_time_sec": round(avg_response_sec, 0) if avg_response_sec else "N/A",
"pct_sub_3min_response": round(
sum(1 for r in s["response_times_sec"] if r <= 180)
/ len(s["response_times_sec"]) * 100, 1
) if s["response_times_sec"] else 0,
"personalization_rate_pct": round(s["used_personalization"] / convs * 100, 1),
"personal_ppv_framing_rate_pct": round(
s["used_personal_ppv_framing"] / s["ppv_offers_sent"] * 100, 1
) if s["ppv_offers_sent"] > 0 else 0,
"ppv_conversion_rate_pct": round(
s["ppv_purchases"] / s["ppv_offers_sent"] * 100, 1
) if s["ppv_offers_sent"] > 0 else 0,
"tip_followup_rate_pct": round(
s["tip_followup_within_15m"] / s["tip_followup_total"] * 100, 1
) if s["tip_followup_total"] > 0 else 0,
"avg_messages_per_conv": round(s["total_messages_sent"] / convs, 1),
})
return sorted(report, key=lambda x: x["revenue_per_conversation"], reverse=True)
# Run weekly report
week_start = "2025-12-23"
week_end = "2025-12-29"
report = generate_weekly_chatter_report("creator_abc123", week_start, week_end)
print(f"\nChatter Performance Report — Week of {week_start}\n{'='*60}")
for r in report:
print(f"\n{r['chatter_id']}")
print(f" Rev/conv: ${r['revenue_per_conversation']} | PPV conv: {r['ppv_conversion_rate_pct']}%")
print(f" Sub-3min response: {r['pct_sub_3min_response']}% | Personalization: {r['personalization_rate_pct']}%")
print(f" Personal PPV framing: {r['personal_ppv_framing_rate_pct']}% | Tip followup <15m: {r['tip_followup_rate_pct']}%")
We share this report with every chatter every Monday. They see their own numbers against the team average. The ranking is not punitive — it is information. Top chatters use it to understand what is driving their performance. Bottom performers use it to identify specifically which behavior to focus on improving, not just “be better at chatting.”
How to Use This in Training
The four behaviors map directly to your training program structure:
Week 1 (new chatters): Response time and coverage discipline. No content yet. Just the operational habit of being present and fast during shift hours. This is the foundation everything else builds on.
Week 2: Personalization practice. Pull fan data for fictional accounts, write five opening messages for each. Identify what information from the fan profile you would use and how you would weave it in naturally.
Week 3: PPV framing drills. Take 10 generic PPV announcements and rewrite each using personal framing. Practice connecting the offer to specific fan context. Run these in group sessions so chatters can calibrate against each other.
Week 4: Tip protocol. Simulate tip notifications, practice the acknowledgment message and the PPV follow within 15 minutes. Make this muscle memory before going live.
After week four, the weekly report does the ongoing training work. Chatters see their behavioral metrics, not just their revenue number. A chatter with 90% sub-3-minute response but 20% personal PPV framing knows exactly where to focus. A chatter with strong framing but slow response time knows what to prioritize.
The 400% performance spread does not close on its own. You close it by making the behavior visible, giving chatters the data they need to see exactly where they are losing, and running the weekly report consistently enough that the feedback loop becomes part of how the team operates.
Most agencies are still running on gut feel and vague feedback like “be more personal.” The data already exists in your chat history. The question is whether you are using it.
The attribution model used in this report builds directly on what we developed for measuring the 400% performance spread across our chatter team. The mass messaging segmentation that pairs with chatter work is covered in our mass messaging conversion guide.
To pull chatter performance data for your own roster, start with the getting started guide and see pricing for access tiers. The weekly report runs in under two minutes on a full creator account — it is one of the highest-return scripts you can run.