Bulk content production
For product descriptions and marketing copy at volume, a cheap chat model writes the drafts and a stronger model checks and polishes a sample of them. For 20,000 descriptions a month with 10% reviewed, at live prices in each model’s cheapest group, model costs are about ¥152 a month. Concurrency, queueing and retries all live in your own code.
Prices updated Nov 12, 2026, 8:17 PM|
01
The business problem
New listings, content marketing and ad campaigns all need a lot of text: a description per SKU, a set of titles per channel, dozens of lines per promotion. Writers cannot keep up, and outsourced quality varies.
Getting a model to write one description is easy. Producing tens of thousands reliably is harder: jobs need a queue and concurrency, rate limits and network errors need retries, the tone has to stay consistent, and the copy must not invent features.
The usual approach is to turn product attributes into structured data, generate drafts from a fixed template, then send a sample to a stronger model or a person for review and rerun any batch with repeated problems.
02
Recommended models
Drafting (chat model)
deepseek-v4.1-flashDeepSeekinput ¥2.8 / output ¥11.2 per 1M tokens· China Models groupHandles almost all of the volume, so pick a model with a low output price that writes your language naturally. Drafts only restate the given attributes; the model is not asked to invent selling points.
Spot-check and polish (stronger model)
Claude Sonnet 5.5Anthropicinput ¥3.2 / output ¥16 per 1M tokens· Claude Lite groupChecks a sample of drafts against the product attributes and the brand voice, and rewrites where needed. It only sees the sample, so a higher price is fine.
Recommended models are picked from the live catalog; prices are each model's cheapest group, updated Nov 12, 2026, 8:17 PM. Click a model to see every group's price.
03
Reference architecture
- 1
Prepare inputs
Turn product attributes (name, material, specs, use cases) and the brand voice guide into structured data, with banned words and claims in a separate list.
- 2
Queue with fixed concurrency
Put jobs in a queue and call the chat model with a fixed number of concurrent requests, say 8 to 16. Retry 429 and 5xx with exponential backoff; log 400-type errors and fix the input instead of retrying.
- 3
Generate drafts
Keep the system prompt fixed and put each product’s attributes in the user message. Cap the output length and ask for paragraphs or JSON so results go straight into your database.
- 4
Automatic checks
Check length, banned words and any numbers or certifications that are not in the attributes; send failures back for a rewrite.
- 5
Review a sample
Send about 10% to the stronger model for review and polish, rerun batches with repeated problems, and have someone skim before publishing.
04
Cost estimate
Assumptions: 20,000 product descriptions a month, each with about 800 input tokens (attributes, brand voice and one example) and about 400 output tokens; 10% (2,000) reviewed by a stronger model at about 1,200 input and 300 output tokens each.
| Item | Model | Usage assumed | Cost |
|---|---|---|---|
| Drafting | deepseek-v4.1-flash | 20,000 × (800 input + 400 output tokens) | ¥134.4 |
| Spot-check and polish | Claude Sonnet 5.5 | 2,000 × (1,200 input + 300 output tokens) | ¥17.28 |
| Total (per month) | ¥151.68 | ||
Calculated from live prices in each model's cheapest group, updated Nov 12, 2026, 8:17 PM.
Model usage only. Retries add calls, so a high failure rate raises the cost. Twice the items means roughly twice the cost; longer descriptions raise the output cost.
05
Sample code
A semaphore caps the requests in flight; 429, 5xx and network errors are retried with exponential backoff, other 4xx errors are logged, and results are written as JSONL for import.
import asyncio
import json
import os
import random
from openai import APIConnectionError, APIStatusError, AsyncOpenAI
client = AsyncOpenAI(base_url="https://open.tutujin.com/v1", api_key=os.environ["TUTUJIN_API_KEY"], max_retries=0)
LIMIT = asyncio.Semaphore(8) # requests in flight at the same time
PROMPT = (
"Write a product description for an online store in a friendly, concrete tone. "
"Use only the facts provided. Do not add awards, certifications or numbers that are not listed. "
"Return 3 short paragraphs."
)
async def write(product, attempts=4):
async with LIMIT:
for i in range(attempts):
try:
resp = await client.chat.completions.create(
model="deepseek-v4.1-flash",
messages=[
{"role": "system", "content": PROMPT},
{"role": "user", "content": json.dumps(product, ensure_ascii=False)},
],
max_tokens=600,
temperature=0.8,
)
return {"sku": product["sku"], "text": resp.choices[0].message.content}
except APIConnectionError:
pass # network problem or timeout: retry
except APIStatusError as e:
if e.status_code != 429 and e.status_code < 500:
# 4xx means the request itself is wrong (or no balance): fix it, do not retry
return {"sku": product["sku"], "error": e.status_code}
await asyncio.sleep(2 ** i + random.random()) # back off: ~1s, 2s, 4s, 8s
return {"sku": product["sku"], "error": "gave up after retries"}
async def main(products):
results = await asyncio.gather(*(write(p) for p in products))
with open("descriptions.jsonl", "w", encoding="utf-8") as f:
for r in results:
f.write(json.dumps(r, ensure_ascii=False) + "\n")
products = [
{"sku": "A1001", "name": "Ceramic mug", "material": "stoneware", "capacity_ml": 350, "dishwasher_safe": True},
]
asyncio.run(main(products))
# then send about 10% of the results to claude-sonnet-5-5 for a fact and tone checkFAQ
What do 20,000 product descriptions a month cost?
At about 800 input and 400 output tokens each, plus a 10% review by a stronger model, the cheapest groups come to about ¥152 a month. Longer output costs more, so set a length limit in the prompt.
Is there a dedicated bulk endpoint?
No. Bulk jobs are concurrent calls to the regular Chat Completions endpoint, billed by actual usage, with concurrency, queueing and retries handled by your code.
How much concurrency should I use?
Start with 4 to 8 concurrent requests and raise it while watching the share of 429 responses. On a 429, wait before retrying instead of resending immediately. Models and groups handle different loads; see the rate-limit notes in the docs.
How do I stop the model from inventing selling points?
Give the model only the attributes the product has, state in the prompt that it must not add benefits, certifications or numbers beyond them, and check the numbers and keywords in each output against the attributes.
Can generated copy go live directly?
Review a sample first. Product and ad copy are subject to advertising rules the model does not know for your market, so have marketing or legal check it before it goes live.