AI
Generate post captions and images with the PostPeer AI endpoints.
Overview
PostPeer ships two AI endpoints powered by Gemini:
POST /v1/ai/writegenerates a social-media caption from a free-form description.POST /v1/ai/imagegenerates a square, vertical, or landscape image and stores it in your project's media bucket.
Both endpoints use the same x-access-key auth as every other PostPeer endpoint.
Credit costs
| Endpoint | Credits per successful call |
|---|---|
/v1/ai/write | 2 |
/v1/ai/image | 10 |
Failed calls do not deduct credits. Costs come out of your subscription cycle first, then your one-time purchased credits.
Generate a caption
Pass a description and the platforms you intend to post to. The AI fits the caption to the strictest character limit among them.
curl -X POST https://api.postpeer.dev/v1/ai/write \
-H "x-access-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "We just shipped a Bluesky API. Same call as posting to Twitter, free tier, no OAuth complexity.",
"platforms": ["twitter", "bluesky", "linkedin"]
}'Response:
{
"success": true,
"content": "We shipped Bluesky support. Same one-line call as Twitter. Free tier. No OAuth dance. Try it: postpeer.dev",
"creditsCharged": 2
}You can also pass existingContent to ask the model to rewrite a draft instead of starting fresh:
-d '{
"description": "Make this snappier and add a question at the end.",
"existingContent": "We just released a new feature that helps with X.",
"platforms": ["twitter"]
}'Generate an image
Minimum required is a description. Everything else is optional.
curl -X POST https://api.postpeer.dev/v1/ai/image \
-H "x-access-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "A flat-lay desk with a laptop, coffee, and a notebook open to a diagram of a social media API. Soft natural light.",
"aspectRatio": "square",
"style": "photo"
}'Response:
{
"success": true,
"url": "https://assets.postpeer.dev/uploads/<projectId>/ai-<uuid>.png",
"mimeType": "image/png",
"creditsCharged": 10
}The url is permanent and ready to drop into the mediaItems array of POST /v1/posts/.
Aspect ratios
Pass aspectRatio as one of square, vertical, or landscape. The aspect ratio is enforced at the model level (via Gemini's imageConfig.aspectRatio), so it's preserved even when you pass reference images of a different shape.
| Value | Pixels | Use for |
|---|---|---|
square (default) | 1:1 | Instagram, Twitter, LinkedIn, Pinterest |
vertical | 9:16 | TikTok, Reels, YouTube Shorts |
landscape | 16:9 | YouTube thumbnails, blog headers |
Style presets
Pass style as one of these to bias the output. Omit for the model to pick whatever fits the brief.
| Value | Effect |
|---|---|
photo | Photorealistic, natural lighting |
illustration | Flat vector, bold shapes, limited palette |
minimal | Lots of negative space, two or three colors max |
vibrant | High-saturation, high-contrast pop |
cinematic | Strong directional light, deep shadows, film grain |
3d-render | Soft 3D, clay materials, smooth shading |
retro | 70s/80s palette, slight grain, period typography |
hand-drawn | Pencil lines, light watercolor wash |
Reference images vs. asset images
Two roles for image inputs, both are optional, both can be passed together:
referenceImageUrls(up to 4): style/vibe inspiration. The model uses them as visual context but does NOT copy them into the result. Use this for "match my brand look", "same composition as this", or "same vibe but different scene".assetImageUrls(up to 4): images that MUST appear in the result, faithfully. Use this for brand logos, product photos, or specific subjects you want placed into the generated image.
curl -X POST https://api.postpeer.dev/v1/ai/image \
-H "x-access-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "A laptop on a sunny desk with the company logo visible on its lid.",
"aspectRatio": "square",
"style": "photo",
"referenceImageUrls": [
"https://assets.postpeer.dev/uploads/<projectId>/brand-mood-1.jpg"
],
"assetImageUrls": [
"https://assets.postpeer.dev/uploads/<projectId>/postpeer-logo.png"
]
}'The model:
- Treats
referenceImageUrlsas inspiration only. It will not put your reference photo in the result. - Treats
assetImageUrlsas content to be placed into the result faithfully, without redrawing or restyling them. The surrounding scene matches your brief, the asset itself is preserved.
If you want to iteratively tweak a previously generated image, pass it as a referenceImageUrl along with a delta description like "warmer lighting" or "same vibe but on a beach". The aspect ratio is hard-locked server-side so the tweak preserves the original shape.
Backwards compat. The older single-image field
referenceImageUrlis still accepted and merged intoreferenceImageUrls. New integrations should use the array form.
End-to-end example
Generate a caption, generate an image with your logo placed in it, then publish both.
# Step 1: caption
curl -X POST https://api.postpeer.dev/v1/ai/write \
-H "x-access-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "description": "Friday product drop", "platforms": ["twitter"] }'
# → { "content": "Friday product drop. ..." }
# Step 2: image with brand logo placed in
curl -X POST https://api.postpeer.dev/v1/ai/image \
-H "x-access-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "Vertical desk shot, Friday vibes, our logo on the laptop lid",
"aspectRatio": "vertical",
"style": "cinematic",
"assetImageUrls": ["https://assets.postpeer.dev/uploads/.../logo.png"]
}'
# → { "url": "https://assets.postpeer.dev/uploads/.../ai-...png" }
# Step 3: publish
curl -X POST https://api.postpeer.dev/v1/posts \
-H "x-access-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Friday product drop. ...",
"platforms": [ { "platform": "twitter", "accountId": "your-account-id" } ],
"mediaItems": [ { "type": "image", "url": "https://assets.postpeer.dev/uploads/.../ai-...png" } ],
"publishNow": true
}'Total credits used: 2 (caption) + 10 (image) + 1 (publish) = 13.
Errors
402 Payment Requiredif your project doesn't have enough credits for the call. Buy more credits or upgrade your plan.503 Service Unavailableif AI features aren't configured on the server (rare; meansGEMINI_API_KEYis unset).500for any other failure. No credits are deducted on failure.
Tone of voice
POST /v1/ai/write is tuned to avoid the obvious AI tells: no em-dashes, no buzzwords (leverage, streamline, unlock, etc.), varied sentence length, no "Bold word: explanation" bullets, no transition openings (Moreover, Additionally, etc.). It writes captions, not press releases. The system prompt is locked, you don't need to ask for it in your description.