AI for SMM: a content plan and batches of posts via API
A monthly content plan and thirty posts in one voice from a single script. How to describe the brand voice, generate in batches and check the output.
5 min · · автоматизация · бизнес
Most people use AI for SMM like this: open a chat, ask for "a post about discounts" and get text that would suit any brand. The problem is not the model but that it was given no brand voice and no structure. If you describe them once and run generation with a script, you get a monthly content plan and a series of posts in one style. Below is a four-step scheme and the code that implements it.
AI for SMM, step 1: describe the brand voice once
Brand voice is not "friendly and expert". The model needs rules you can verify. Build a brand.md file along these lines:
- Who speaks and to whom. "A coffee supplies shop, talking to home enthusiasts, formal 'you'".
- Tone. Short, no exclamation marks, no words like "unique" or "best".
- Format. Post length 400-700 characters, headline on the first line, at most one emoji or none.
- Prohibitions. Topics and words that must not be used, promises that must not be made.
- Three to five real successful posts as samples. Examples work better than any adjectives.
This block will be the stable part of every request. An important detail: it does not change between requests, which means it can be cached, see below.
Step 2. The content plan as JSON
Ask not for "a monthly plan as text" but for a structure a script can pick up:
import json
import os
import anthropic
client = anthropic.Anthropic(
base_url="https://api.zukko.pro",
api_key=os.environ["ZUKKO_API_KEY"],
)
BRAND = open("brand.md", encoding="utf-8").read()
def make_plan(month, goals, events):
prompt = (
f"Month: {month}\nGoals: {goals}\nEvents and occasions: {events}\n\n"
"Build a 30-day content plan. Return a JSON array of objects: "Note the base URL: for the official Anthropic SDK it has no /v1, the SDK adds the path itself. With the OpenAI SDK the address would be https://api.zukko.pro/v1, but the gateway does not see the cache discount for Claude in that format, more in the article on cache in the two formats.
What to check in the plan before moving on: no two identical topics, rubrics are spread out, your real promo dates are included. The model does not know occasions and events on its own: give sale dates, releases and holidays in the request.
Step 3. Posts in a batch
Now we generate text for each plan row. The rule: one post, one request. That makes it easier to repeat a failed one without losing the whole batch.
def write_post(item):
prompt = (
f"Rubric: {item['rubric']}\nTopic: {item['topic']}\n"
f"Goal: {item['goal']}\nFormat: {item['format']}\n"
f"First sentence: {item['hook']}\n\n"
"Write the post strictly following the brand voice rules. "
"Do not invent prices, dates or specs absent from the brief. "
"If data is missing, add a final line 'NEEDS CLARIFICATION: ...'."
)
msg = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=800,
system=[{"type": "text", "text": BRAND,
"cache_control": {"type": "ephemeral"}}],
messages=[{"role": "user", "content": prompt}],
)
return msg.content[0].text
plan = make_plan("November", "audience growth", "sale on 11.11")
posts = [{"day": it["day"], "text": write_post(it)} for it in plan]The BRAND block is marked cacheable: at Anthropic the cache works when the repeating part of a request is long enough and comes first; see Anthropic's documentation for exact conditions. From the second request that block is read from cache at a lower price.
Step 4. Automatic checks before publishing
Put a simple filter between generation and the publishing calendar:
- length within limits;
- no words from the forbidden list;
- no "NEEDS CLARIFICATION" line, otherwise the post goes to a person;
- numbers and dates in the text match the brief (a regular expression check);
- no repeated first sentences in neighbouring posts.
Then comes a manual read. The model writes smoothly, but it does not know you answered subscribers in the comments yesterday or that one product ran out. Responsibility for publishing stays with you. If you need A/B variants, repeat the request with a different first sentence and different temperature; for sampling parameters see the article on temperature and top_p.
Which model to choose for posts
You cannot measure a model's taste, so take one brief and compare three models blind on ten posts. Prices at October 18, 2026 (Zukko, per 1M tokens, input / output):
| Model | Price | Strength index |
|---|---|---|
glm-5.3-flash | $0.04 / $0.04 | 42 |
gpt-6.1-sol | $0.27 / $1.32 | 52 |
claude-sonnet-5-5 | $0.4 / $2 | 48 |
The strength index is the Artificial Analysis Intelligence Index; it does not tell you how a model writes Russian in your tone. The blind test decides.
What a month of posts costs
Assumptions: 30 posts, each with 1500 input tokens (1200 for the brand block and 300 for the task) and 400 output tokens. Formula: (input × input price + output × output price) / 1,000,000; with cache, cache write and cache read lines are added at their own prices. For the cached case we assume the brand block meets the minimum length for caching.
claude-sonnet-5-5without cache: 45,000 × 0.4 / 1,000,000 + 12,000 × 2 / 1,000,000 = 0.018 + 0.024 = $0.042.claude-sonnet-5-5with cache in the Anthropic format: fresh input 30 × 300 = 9,000 tokens, 9,000 × 0.4 / 1,000,000 = 0.0036; cache write 1,200 × 0.5 / 1,000,000 = 0.0006; reads 29 × 1,200 = 34,800 tokens, 34,800 × 0.05 / 1,000,000 = 0.00174; output 0.024. Total about $0.030.gpt-6.1-sol: 45,000 × 0.27 / 1,000,000 + 12,000 × 1.32 / 1,000,000 = 0.01215 + 0.01584, about $0.028.glm-5.3-flash: (45,000 + 12,000) × 0.04 / 1,000,000, about $0.0023.
The 30-day plan adds one more request of a few thousand tokens, which is cents. With such a price spread, save on manual editing rather than on the model. Check current prices in GET https://api.zukko.pro/v1/models.
Connecting
Create a key in the dashboard, give it a daily dollar limit and keep it in an environment variable. The request format used in the examples is described in the /v1/messages reference. The balance is topped up from $1 via SBP (the payment page adds 8.5%), cryptocurrency or a MasterCard.
FAQ
How do I keep posts from sounding AI-written?
Give real samples instead of a tone description, ban clichés with a list, and edit the text. The strongest lever is examples of your best posts in the brand block.
Can I get an AI for creating texts without a subscription?
Yes, through the API you pay only for tokens, there is no subscription. That does not mean free unlimited access: every request is billed, but for posts it is fractions of a cent.
Do I need to label texts as AI-generated?
Labelling rules depend on the platform and jurisdiction. Check the rules of the social network you publish on.
How many variants per post should I ask for?
Three is enough for comparison. More only increases manual selection work.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- AI for testers: test cases, checklists and bug reportsHow a QA engineer can use a model for test cases, checklists and bug reports, check coverage, and avoid requirements the model made up.
- Anonymizing data before sending it to a neural network: a practical wayYou do not have to send personal data to a model for it to process a document. How to replace it with labels, restore it in the answer and check that nothing leaked.
- A neural network for HR: job posts, candidate emails and resume reviewA model can draft job posts and emails and do a first-pass resume review. What to hand over, how to treat candidates' personal data and how to prevent biased decisions.
- Generating an FAQ with a neural network from tickets and a knowledge baseA good FAQ is built on what customers really ask. A pipeline: group the tickets, answer from the knowledge base with a source reference, and verify the result.