Skip to content

API reference

Your posts, wherever you need them.

A versioned REST API over the same data your dashboard reads. Create a key, call it from your server, and render your work anywhere.

Building with an AI assistant? Copy the whole reference as markdown and paste it in. It has everything it needs, including the caching rules.

View as markdown
Quickstart
curl "https://topics.so/api/v1/posts?perPage=5" \
  -H "Authorization: Bearer $TOPICS_API_KEY"

Overview

Every endpoint lives under one base URL and returns JSON. Requests are scoped to the brand that owns the key, so a key only ever sees its own posts, its own metrics, and nothing belonging to anyone else.

List endpoints answer with a data array and a pagination object. Single-resource endpoints answer with the object itself.

The version sits in the path. Anything that would break an existing integration ships as a new version, so v1 keeps working.

Base URL
https://topics.so/api
Response envelope
{
  "data": [
    "…"
  ],
  "pagination": {
    "page": 1,
    "perPage": 20,
    "total": 128,
    "pageCount": 7
  }
}

Build with AI

Most integrations here are written by an assistant, so the whole reference is published as one markdown file at /docs/api.md. Copy it, paste it into Claude or ChatGPT, and describe what you want built.

Assistants that fetch their own context will find /llms.txt at the root, following the llmstxt.org convention, and the OpenAPI document at /api/v1/openapi.json.

The markdown is generated from the same definitions as this page, so it is never out of date, and it states the two things assistants get wrong most often: the key belongs on the server, and the responses need caching.

View as markdown
Paste this into your assistant
Build a Topics.so API integration.
The complete reference is at:
https://topics.so/docs/api.md

Fetch it, follow it exactly, and pay attention
to the caching and server-side key rules.
Or point it at these
https://topics.so/llms.txt
https://topics.so/docs/api.md
https://topics.so/api/v1/openapi.json

Authentication

Create a key on the API page of your dashboard. The secret is shown once, at the moment it is created, and never again. Send it as a bearer token on every request.

A key is a secret. Call the API from your server and keep the key in an environment variable. Never ship it in browser code, where anyone can read it.

Each key carries scopes that decide what it can reach. A request for something outside a key's scopes returns 403 forbidden. Revoking a key in the dashboard takes effect immediately.

Request header
Authorization: Bearer tso_G_USxN6rS4rzt0Enh3B_-uxinWqPhJHa
Missing or invalid key
{
  "error": {
    "code": "unauthorized",
    "message": "That API key is not valid."
  }
}

Rate limits

Limits are per brand and scale with your plan. Requests are counted per second, and again across the calendar month.

PlanPer secondPer monthKeys
Free15001
Creator525,0005
Studio20250,00020

Every response reports where you stand on both, so a client can pace itself instead of waiting to be turned away.

HeaderMeaning
X-RateLimit-LimitRequests allowed in one second.
X-RateLimit-RemainingRequests left in the current second.
X-RateLimit-ResetUnix time, in seconds, when the current window resets.
X-Quota-LimitRequests allowed this calendar month.
X-Quota-RemainingRequests left this calendar month.
Retry-AfterSeconds to wait before retrying. Sent with both 429 responses.

Going over the per-second limit returns 429 rateLimitExceeded, and the window reopens the next second. Using up the monthly quota returns 429 quotaExceeded, which clears at the start of the next month.

Response headers
HTTP/1.1 200 OK
X-RateLimit-Limit: 1
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1785115680
X-Quota-Limit: 500
X-Quota-Remaining: 483

Caching

The metrics behind this API are refreshed every 30 minutes. Asking for the same data more often than that cannot return anything new, it only spends quota. Cache the responses and the free plan goes a long way; skip caching and 500 requests a month disappears in an afternoon.

  • Cache for at least 30 minutes. An hour or a day is entirely reasonable for published-post data.
  • Cache the rendered page, not just the HTTP call, so a traffic spike cannot become an API spike.
  • Fetch at build time or on a schedule where you can, rather than on every request.
  • Page in bulk. perPage=100 reads a hundred posts in one request; perPage=10 spends ten times the quota for the same posts.
  • Serve the last good response if a request fails, so a 429 degrades to slightly stale data instead of a broken page.

Throttled requests do not consume monthly quota, so a retry after a 429 costs nothing but the wait.

Next.js, revalidating hourly
const response = await fetch("https://topics.so/api/v1/posts?perPage=100", {
	headers: { Authorization: `Bearer ${process.env.TOPICS_API_KEY}` },
	// One request an hour, however much traffic the page gets.
	next: { revalidate: 3600 }
});

const { data, pagination } = await response.json();
Anywhere else
let cache = { at: 0, data: null };
const TTL_MS = 60 * 60 * 1000;

export async function getPosts() {
	if (cache.data && Date.now() - cache.at < TTL_MS) return cache.data;

	const response = await fetch("https://topics.so/api/v1/posts?perPage=100", {
		headers: { Authorization: `Bearer ${process.env.TOPICS_API_KEY}` }
	});
	if (!response.ok) {
		// Serve stale data rather than break the page on a 429.
		if (cache.data) return cache.data;
		throw new Error(`Topics.so API ${response.status}`);
	}

	cache = { at: Date.now(), data: await response.json() };
	return cache.data;
}

Errors

Every failure returns the same envelope. Branch on code: the message is written for people and may change.

CodeStatusMeaning
unauthorized401No key was sent, or the key is unknown or revoked.
forbidden403The key is valid but lacks a scope the endpoint requires.
notFound404Nothing with that id exists in this brand.
validationError400A parameter or body field is missing, malformed, or out of range.
rateLimitExceeded429Too many requests in one minute. Retry after the window resets.
quotaExceeded429The monthly request quota is used up. It resets at the start of next month.
creditsExhausted402The brand is out of image credits for the month. Upgrade the plan for more.
conflict409The resource isn't in a state that allows this (already publishing, not failed, and so on).
serviceUnavailable503An upstream dependency didn't answer. Safe to retry shortly.
Error response
{
  "error": {
    "code": "rateLimitExceeded",
    "message": "Too many requests. Slow down and try again shortly."
  }
}

MCP server

Agents get the same API as first-class tools. The MCP server lives at https://topics.so/api/mcp (streamable HTTP) and works with Claude Code, Cursor, and anything else that speaks MCP: no docs to paste, no glue code to write.

Authentication is the same bearer key, sent as a header. Every tool call passes through the same scopes, rate limits, and quotas as the REST API, and revoking the key shuts off both at once.

ToolsRequires
get_brand, list_posts, get_post, list_series, get_series_queue, list_assets, list_cast_memberspostsRead / createRead
compose_prompt, generate_image, set_brand_kit, add_asset, create_asset_upload, confirm_asset_upload, save_cast_member, delete_cast_member, save_series, set_series_active, run_autopilot_review, skip_item, regenerate_item, generate_item, rerender_overlay, publish_itemcreateWrite
publish_itempublishWrite
list_organizations, create_organization, update_organizationbrandsWrite

Keep the key out of committed files. Reference it as an environment variable in .mcp.json, exactly as the sample shows.

Claude Code
claude mcp add --transport http topics https://topics.so/api/mcp \
  --header "Authorization: Bearer $TOPICS_API_KEY"
Any MCP client (.mcp.json)
{
  "mcpServers": {
    "topics": {
      "type": "http",
      "url": "https://topics.so/api/mcp",
      "headers": {
        "Authorization": "Bearer ${TOPICS_API_KEY}"
      }
    }
  }
}

TypeScript SDK

@topics-so/sdk is a typed client for Node.js and server-side JavaScript, generated from the OpenAPI spec below so it can never disagree with the API.

It adds one convenience worth having: generateImage composes a prompt from your idea, spends the credit, and waits for the finished image, so the whole flow is a single call. Pass idempotencyKey and a retried call can never spend twice.

Everything else is available through topics.api, one typed method per endpoint. The key rules from Authentication apply: server-side only, never in browser code.

Install
pnpm add @topics-so/sdk
Generate an on-brand image
import { TopicsClient } from "@topics-so/sdk";

const topics = new TopicsClient({ apiKey: process.env.TOPICS_API_KEY! });

// One call: composes the prompt, spends one credit, waits for the image.
const image = await topics.generateImage({
	idea: "Morning espresso ritual, warm window light",
	aspectRatio: "landscape"
});

console.log(image.url);
Typed access to every endpoint
// Everything else, fully typed from the OpenAPI spec:
const { data } = await topics.api.GET("/v1/series");

await topics.api.PATCH("/v1/series/{id}", {
	params: { path: { id: "…" } },
	body: { guidance: "Lean on the dose calculator." }
});

List posts

GET/api/v1/posts

Every post across your connected accounts, newest first by default. Filter, search, sort, and page through them the same way the dashboard does. Each post carries its latest metrics.

Requires the postsRead scope.

Query parameters

pageintegeroptional
Page number, starting at 1.Defaults to 1.
perPageintegeroptional
Posts per page.Defaults to 20. Maximum 100.
qstringoptional
Free-text search across title, caption, and cover text.
fromstringoptional
Only posts published on or after this date (yyyy-mm-dd).
tostringoptional
Only posts published on or before this date (yyyy-mm-dd).
platformstringoptional
Restrict to one platform.One of instagram, linkedin, youtube.
formatstringoptional
Restrict to one content format.One of carousel, image, reel, story, video.
hashtagstringoptional
Restrict to posts carrying this hashtag, without the leading #.
sortstringoptional
Field to sort on.One of published, views, impressions, clicks, comments, engagementRate. Defaults to published.
dirstringoptional
Sort direction.One of asc, desc. Defaults to desc.
curl "https://topics.so/api/v1/posts?perPage=5&platform=instagram" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "data": [
    {
      "id": "cm8xk21p40001s6014r9e2n7q",
      "platform": "instagram",
      "format": "carousel",
      "title": "Three openings that land",
      "url": "https://www.instagram.com/p/DBv2Qm1Nq8x/",
      "caption": "Three ways to open a talk that actually land. #publicspeaking #storytelling",
      "hashtags": [
        "publicspeaking",
        "storytelling"
      ],
      "thumbnailUrl": "https://media.topics.so/posts/cm8xk21p40001/cover.jpg",
      "mediaUrls": [
        "https://media.topics.so/posts/cm8xk21p40001/1.jpg",
        "https://media.topics.so/posts/cm8xk21p40001/2.jpg"
      ],
      "publishedAt": "2026-07-11T15:02:11.000Z",
      "metrics": {
        "views": 18420,
        "impressions": 21031,
        "reach": 15877,
        "likes": 1204,
        "comments": 86,
        "shares": 143,
        "saves": 219,
        "clicks": null,
        "engagementRate": 0.0786,
        "capturedAt": "2026-07-24T06:00:00.000Z"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "perPage": 5,
    "total": 128,
    "pageCount": 26
  }
}

Retrieve a post

GET/api/v1/posts/{id}

One post by id, with its full metric history. Snapshots are cumulative lifetime readings, oldest first, so a daily series is the difference between consecutive entries.

Requires the postsRead scope.

Path parameters

idstringrequired
The post id, as returned by the list endpoint.
curl "https://topics.so/api/v1/posts/cm8xk21p40001s6014r9e2n7q" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "id": "cm8xk21p40001s6014r9e2n7q",
  "platform": "instagram",
  "format": "carousel",
  "title": "Three openings that land",
  "url": "https://www.instagram.com/p/DBv2Qm1Nq8x/",
  "caption": "Three ways to open a talk that actually land. #publicspeaking #storytelling",
  "hashtags": [
    "publicspeaking",
    "storytelling"
  ],
  "thumbnailUrl": "https://media.topics.so/posts/cm8xk21p40001/cover.jpg",
  "mediaUrls": [
    "https://media.topics.so/posts/cm8xk21p40001/1.jpg",
    "https://media.topics.so/posts/cm8xk21p40001/2.jpg"
  ],
  "publishedAt": "2026-07-11T15:02:11.000Z",
  "metrics": {
    "views": 18420,
    "impressions": 21031,
    "reach": 15877,
    "likes": 1204,
    "comments": 86,
    "shares": 143,
    "saves": 219,
    "clicks": null,
    "engagementRate": 0.0786,
    "capturedAt": "2026-07-24T06:00:00.000Z"
  },
  "snapshots": [
    {
      "views": 12045,
      "impressions": 14210,
      "reach": 15877,
      "likes": 1204,
      "comments": 86,
      "shares": 143,
      "saves": 219,
      "clicks": null,
      "engagementRate": 0.0721,
      "capturedAt": "2026-07-17T06:00:00.000Z"
    },
    {
      "views": 18420,
      "impressions": 21031,
      "reach": 15877,
      "likes": 1204,
      "comments": 86,
      "shares": 143,
      "saves": 219,
      "clicks": null,
      "engagementRate": 0.0786,
      "capturedAt": "2026-07-24T06:00:00.000Z"
    }
  ]
}

Get the brand kit

GET/api/v1/brand-kit

The brand's Create settings: the base style prompt prepended to every composed prompt, and the Discord webhook for digests and alerts.

Requires the createRead scope.

curl "https://topics.so/api/v1/brand-kit" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "baseStylePrompt": "Warm editorial lifestyle photography, natural light, shallow depth of field.",
  "discordWebhookUrl": null
}

Update the brand kit

PUT/api/v1/brand-kit

Replaces the brand kit. Send an empty string to clear a field.

Requires the createWrite scope.

curl "https://topics.so/api/v1/brand-kit" \
  -X PUT \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"baseStylePrompt":"Warm editorial lifestyle photography, natural light, shallow depth of field.","discordWebhookUrl":""}'
Request body
{
  "baseStylePrompt": "Warm editorial lifestyle photography, natural light, shallow depth of field.",
  "discordWebhookUrl": ""
}
Response
{
  "baseStylePrompt": "Warm editorial lifestyle photography, natural light, shallow depth of field.",
  "discordWebhookUrl": null
}

Get image credits

GET/api/v1/credits

The brand's image-credit position for the current month. Generation debits these, not the request quota.

Requires the createRead scope.

curl "https://topics.so/api/v1/credits" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "allowance": 200,
  "used": 46,
  "remaining": 154
}

List assets

GET/api/v1/assets

The brand's Create asset library: logos, inspiration images, and cast reference photos.

Requires the createRead scope.

Query parameters

kindstringoptional
Restrict to one kind.One of logo, inspiration, castReference.
curl "https://topics.so/api/v1/assets?kind=castReference" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "data": [
    {
      "id": "cmb2n81xq0003s601wdkq5r2a",
      "kind": "castReference",
      "name": "Maya, front-lit selfie",
      "contentType": "image/jpeg",
      "url": "https://media.topics.so/media/assets/org1/V1t2u3w4x5y6z7a8.jpg",
      "width": 1600,
      "height": 2000,
      "isPrimary": false,
      "createdAt": "2026-08-02T10:12:00.000Z"
    }
  ]
}

Add an asset

POST/api/v1/assets

Adds an image to the library. Send `sourceUrl` and it is fetched server-side from that public URL (JPEG, PNG, or WebP, up to 10 MB). Alternatively, finish a presigned upload by sending the `key` and `url` returned by the upload endpoint.

Requires the createWrite scope.

curl "https://topics.so/api/v1/assets" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind":"castReference","name":"Maya, front-lit selfie","sourceUrl":"https://example.com/maya-front.jpg"}'
Request body
{
  "kind": "castReference",
  "name": "Maya, front-lit selfie",
  "sourceUrl": "https://example.com/maya-front.jpg"
}
Response
{
  "id": "cmb2n81xq0003s601wdkq5r2a",
  "kind": "castReference",
  "name": "Maya, front-lit selfie",
  "contentType": "image/jpeg",
  "url": "https://media.topics.so/media/assets/org1/V1t2u3w4x5y6z7a8.jpg",
  "width": 1600,
  "height": 2000,
  "isPrimary": false,
  "createdAt": "2026-08-02T10:12:00.000Z"
}

Start a direct upload

POST/api/v1/assets/uploads

Step 1 of a direct upload: returns a presigned `uploadUrl`. PUT the raw bytes there with the same Content-Type, then POST `/v1/assets` with the returned `key` and `url`.

Requires the createWrite scope.

curl "https://topics.so/api/v1/assets/uploads" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind":"inspiration","name":"Balcony light study","contentType":"image/jpeg","size":482133}'
Request body
{
  "kind": "inspiration",
  "name": "Balcony light study",
  "contentType": "image/jpeg",
  "size": 482133
}
Response
{
  "uploadUrl": "https://s3.amazonaws.com/bucket/media/assets/org1/V1t2u3w4x5y6z7a8.jpg?X-Amz-Signature=…",
  "url": "https://media.topics.so/media/assets/org1/V1t2u3w4x5y6z7a8.jpg",
  "key": "media/assets/org1/V1t2u3w4x5y6z7a8.jpg",
  "kind": "inspiration",
  "name": "Balcony light study",
  "contentType": "image/jpeg"
}

Delete an asset

DELETE/api/v1/assets/{id}

Soft-deletes one asset. Cast members and creations that referenced it keep working; the photo simply drops out of their reference sets.

Requires the createWrite scope.

Path parameters

idstringrequired
The asset id.
curl "https://topics.so/api/v1/assets/cmb2n81xq0003s601wdkq5r2a" \
  -X DELETE \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
204 No Content

List cast members

GET/api/v1/cast-members

The recurring people this brand casts in its images, each with their reference photos.

Requires the createRead scope.

curl "https://topics.so/api/v1/cast-members" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "data": [
    {
      "id": "cmb2n9f2t0005s601m3hq8v1d",
      "name": "Maya",
      "lookPrompt": "24, lean build, dark curly shoulder-length hair, warm brown eyes, minimal athleisure wardrobe",
      "lifestylePrompt": "Lives in Lisbon; boutique gyms, espresso bars, coastal drives",
      "references": [
        {
          "id": "cmb2n81xq0003s601wdkq5r2a",
          "kind": "castReference",
          "name": "Maya, front-lit selfie",
          "contentType": "image/jpeg",
          "url": "https://media.topics.so/media/assets/org1/V1t2u3w4x5y6z7a8.jpg",
          "width": 1600,
          "height": 2000,
          "isPrimary": false,
          "createdAt": "2026-08-02T10:12:00.000Z"
        }
      ],
      "active": true,
      "createdAt": "2026-08-02T10:15:00.000Z"
    }
  ]
}

Create a cast member

POST/api/v1/cast-members

A cast member is a name, a look reproduced verbatim in every prompt, an optional lifestyle, and up to four reference photos (assets of kind `castReference`). Pin them to a creation or a series for a consistent face.

Requires the createWrite scope.

curl "https://topics.so/api/v1/cast-members" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Maya","lookPrompt":"24, lean build, dark curly shoulder-length hair, warm brown eyes, minimal athleisure wardrobe","lifestylePrompt":"Lives in Lisbon; boutique gyms, espresso bars, coastal drives","referenceAssetIds":["cmb2n81xq0003s601wdkq5r2a"]}'
Request body
{
  "name": "Maya",
  "lookPrompt": "24, lean build, dark curly shoulder-length hair, warm brown eyes, minimal athleisure wardrobe",
  "lifestylePrompt": "Lives in Lisbon; boutique gyms, espresso bars, coastal drives",
  "referenceAssetIds": [
    "cmb2n81xq0003s601wdkq5r2a"
  ]
}
Response
{
  "id": "cmb2n9f2t0005s601m3hq8v1d",
  "name": "Maya",
  "lookPrompt": "24, lean build, dark curly shoulder-length hair, warm brown eyes, minimal athleisure wardrobe",
  "lifestylePrompt": "Lives in Lisbon; boutique gyms, espresso bars, coastal drives",
  "references": [
    {
      "id": "cmb2n81xq0003s601wdkq5r2a",
      "kind": "castReference",
      "name": "Maya, front-lit selfie",
      "contentType": "image/jpeg",
      "url": "https://media.topics.so/media/assets/org1/V1t2u3w4x5y6z7a8.jpg",
      "width": 1600,
      "height": 2000,
      "isPrimary": false,
      "createdAt": "2026-08-02T10:12:00.000Z"
    }
  ],
  "active": true,
  "createdAt": "2026-08-02T10:15:00.000Z"
}

Retrieve a cast member

GET/api/v1/cast-members/{id}

One cast member by id.

Requires the createRead scope.

Path parameters

idstringrequired
The cast member id.
curl "https://topics.so/api/v1/cast-members/cmb2n9f2t0005s601m3hq8v1d" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "id": "cmb2n9f2t0005s601m3hq8v1d",
  "name": "Maya",
  "lookPrompt": "24, lean build, dark curly shoulder-length hair, warm brown eyes, minimal athleisure wardrobe",
  "lifestylePrompt": "Lives in Lisbon; boutique gyms, espresso bars, coastal drives",
  "references": [
    {
      "id": "cmb2n81xq0003s601wdkq5r2a",
      "kind": "castReference",
      "name": "Maya, front-lit selfie",
      "contentType": "image/jpeg",
      "url": "https://media.topics.so/media/assets/org1/V1t2u3w4x5y6z7a8.jpg",
      "width": 1600,
      "height": 2000,
      "isPrimary": false,
      "createdAt": "2026-08-02T10:12:00.000Z"
    }
  ],
  "active": true,
  "createdAt": "2026-08-02T10:15:00.000Z"
}

Update a cast member

PATCH/api/v1/cast-members/{id}

Partial update: fields you omit keep their stored value.

Requires the createWrite scope.

Path parameters

idstringrequired
The cast member id.
curl "https://topics.so/api/v1/cast-members/cmb2n9f2t0005s601m3hq8v1d" \
  -X PATCH \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lifestylePrompt":"Moved to Porto; surf mornings, studio afternoons"}'
Request body
{
  "lifestylePrompt": "Moved to Porto; surf mornings, studio afternoons"
}
Response
{
  "id": "cmb2n9f2t0005s601m3hq8v1d",
  "name": "Maya",
  "lookPrompt": "24, lean build, dark curly shoulder-length hair, warm brown eyes, minimal athleisure wardrobe",
  "lifestylePrompt": "Lives in Lisbon; boutique gyms, espresso bars, coastal drives",
  "references": [
    {
      "id": "cmb2n81xq0003s601wdkq5r2a",
      "kind": "castReference",
      "name": "Maya, front-lit selfie",
      "contentType": "image/jpeg",
      "url": "https://media.topics.so/media/assets/org1/V1t2u3w4x5y6z7a8.jpg",
      "width": 1600,
      "height": 2000,
      "isPrimary": false,
      "createdAt": "2026-08-02T10:12:00.000Z"
    }
  ],
  "active": true,
  "createdAt": "2026-08-02T10:15:00.000Z"
}

Delete a cast member

DELETE/api/v1/cast-members/{id}

Retires (soft-deletes) a cast member. Past creations keep their snapshots; a series still pointing at them falls back to its own inspiration set.

Requires the createWrite scope.

Path parameters

idstringrequired
The cast member id.
curl "https://topics.so/api/v1/cast-members/cmb2n9f2t0005s601m3hq8v1d" \
  -X DELETE \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
204 No Content

List creations

GET/api/v1/creations

The brand's generated images, newest first.

Requires the createRead scope.

Query parameters

pageintegeroptional
Page number, starting at 1.Defaults to 1.
perPageintegeroptional
Creations per page.Defaults to 24. Maximum 100.
curl "https://topics.so/api/v1/creations?perPage=10" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "data": [
    {
      "id": "cmb2nbqoe0007s601k2f9d3xz",
      "status": "complete",
      "aspectRatio": "landscape",
      "idea": "Morning mobility routine on a sunlit balcony",
      "prompt": "Editorial lifestyle photo of Maya (24, lean build, dark curly shoulder-length hair) mid-stretch on a sunlit Lisbon balcony…",
      "overlay": {
        "template": "captionBar",
        "text": "Three openings that land",
        "showLogo": true
      },
      "url": "https://media.topics.so/media/creations/org1/cmb2nbqoe0007-final-a1b2c3.jpg",
      "rawUrl": "https://media.topics.so/media/creations/org1/cmb2nbqoe0007-raw-a1b2c3.jpg",
      "error": null,
      "creditCost": 1,
      "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
      "createdAt": "2026-08-14T08:00:02.000Z",
      "completedAt": "2026-08-14T08:00:41.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "perPage": 10,
    "total": 46,
    "pageCount": 5
  }
}

Compose a prompt

POST/api/v1/creations/compose

Expands a rough idea into a full image prompt using the brand's base style and, when given, a cast member's look. Free: credits are only spent when the prompt is generated. Editing the returned prompt before generating is expected.

Requires the createWrite scope.

curl "https://topics.so/api/v1/creations/compose" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"idea":"Morning mobility routine on a sunlit balcony","castMemberId":"cmb2n9f2t0005s601m3hq8v1d"}'
Request body
{
  "idea": "Morning mobility routine on a sunlit balcony",
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d"
}
Response
{
  "idea": "Morning mobility routine on a sunlit balcony",
  "prompt": "Editorial lifestyle photo of Maya (24, lean build, dark curly shoulder-length hair) mid-stretch on a sunlit Lisbon balcony…"
}

Generate an image

POST/api/v1/creations

Starts a generation and answers 202 with the pending creation; the brand's base style, cast look, and reference photos are applied server-side. Debits one image credit. Poll the single-creation endpoint (ideally with `wait`) until `status` is `complete`. Send an `Idempotency-Key` header to make retries safe: a replay answers 200 with the original creation instead of debiting again.

Requires the createWrite scope.

curl "https://topics.so/api/v1/creations" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Editorial lifestyle photo of Maya (24, lean build, dark curly shoulder-length hair) mid-stretch on a sunlit Lisbon balcony…","idea":"Morning mobility routine on a sunlit balcony","aspectRatio":"landscape","castMemberId":"cmb2n9f2t0005s601m3hq8v1d","overlay":{"template":"captionBar","text":"Three openings that land","showLogo":true}}'
Request body
{
  "prompt": "Editorial lifestyle photo of Maya (24, lean build, dark curly shoulder-length hair) mid-stretch on a sunlit Lisbon balcony…",
  "idea": "Morning mobility routine on a sunlit balcony",
  "aspectRatio": "landscape",
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
  "overlay": {
    "template": "captionBar",
    "text": "Three openings that land",
    "showLogo": true
  }
}
Response
{
  "id": "cmb2nbqoe0007s601k2f9d3xz",
  "status": "pending",
  "aspectRatio": "landscape",
  "idea": "Morning mobility routine on a sunlit balcony",
  "prompt": "Editorial lifestyle photo of Maya (24, lean build, dark curly shoulder-length hair) mid-stretch on a sunlit Lisbon balcony…",
  "overlay": {
    "template": "captionBar",
    "text": "Three openings that land",
    "showLogo": true
  },
  "url": null,
  "rawUrl": null,
  "error": null,
  "creditCost": 1,
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
  "createdAt": "2026-08-14T08:00:02.000Z",
  "completedAt": null
}

Retrieve a creation

GET/api/v1/creations/{id}

One creation by id. Pass `wait` to long-poll: the response is held until the creation completes or fails, or the wait runs out, so a generation resolves in one request instead of a polling loop.

Requires the createRead scope.

Path parameters

idstringrequired
The creation id.

Query parameters

waitintegeroptional
Seconds to hold the response for a terminal status.Defaults to 0. Maximum 60.
curl "https://topics.so/api/v1/creations/cmb2nbqoe0007s601k2f9d3xz?wait=60" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "id": "cmb2nbqoe0007s601k2f9d3xz",
  "status": "complete",
  "aspectRatio": "landscape",
  "idea": "Morning mobility routine on a sunlit balcony",
  "prompt": "Editorial lifestyle photo of Maya (24, lean build, dark curly shoulder-length hair) mid-stretch on a sunlit Lisbon balcony…",
  "overlay": {
    "template": "captionBar",
    "text": "Three openings that land",
    "showLogo": true
  },
  "url": "https://media.topics.so/media/creations/org1/cmb2nbqoe0007-final-a1b2c3.jpg",
  "rawUrl": "https://media.topics.so/media/creations/org1/cmb2nbqoe0007-raw-a1b2c3.jpg",
  "error": null,
  "creditCost": 1,
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
  "createdAt": "2026-08-14T08:00:02.000Z",
  "completedAt": "2026-08-14T08:00:41.000Z"
}

Retry a creation

POST/api/v1/creations/{id}/retry

Re-runs a failed creation. Free while attempts remain; once they're spent, it debits a fresh credit and resets the attempt budget.

Requires the createWrite scope.

Path parameters

idstringrequired
The creation id.
curl "https://topics.so/api/v1/creations/cmb2nbqoe0007s601k2f9d3xz/retry" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "id": "cmb2nbqoe0007s601k2f9d3xz",
  "status": "pending",
  "aspectRatio": "landscape",
  "idea": "Morning mobility routine on a sunlit balcony",
  "prompt": "Editorial lifestyle photo of Maya (24, lean build, dark curly shoulder-length hair) mid-stretch on a sunlit Lisbon balcony…",
  "overlay": {
    "template": "captionBar",
    "text": "Three openings that land",
    "showLogo": true
  },
  "url": null,
  "rawUrl": null,
  "error": null,
  "creditCost": 1,
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
  "createdAt": "2026-08-14T08:00:02.000Z",
  "completedAt": null
}

Re-render the overlay

POST/api/v1/creations/{id}/overlay

Swaps or removes a creation's overlay, re-rendered from the stored raw image. Free: no model call, no credits. Send `"overlay": null` to remove it.

Requires the createWrite scope.

Path parameters

idstringrequired
The creation id.
curl "https://topics.so/api/v1/creations/cmb2nbqoe0007s601k2f9d3xz/overlay" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"overlay":{"template":"centeredTitle","text":"Move first, coffee second"}}'
Request body
{
  "overlay": {
    "template": "centeredTitle",
    "text": "Move first, coffee second"
  }
}
Response
{
  "id": "cmb2nbqoe0007s601k2f9d3xz",
  "status": "complete",
  "aspectRatio": "landscape",
  "idea": "Morning mobility routine on a sunlit balcony",
  "prompt": "Editorial lifestyle photo of Maya (24, lean build, dark curly shoulder-length hair) mid-stretch on a sunlit Lisbon balcony…",
  "overlay": {
    "template": "centeredTitle",
    "text": "Move first, coffee second"
  },
  "url": "https://media.topics.so/media/creations/org1/cmb2nbqoe0007-final-a1b2c3.jpg",
  "rawUrl": "https://media.topics.so/media/creations/org1/cmb2nbqoe0007-raw-a1b2c3.jpg",
  "error": null,
  "creditCost": 1,
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
  "createdAt": "2026-08-14T08:00:02.000Z",
  "completedAt": "2026-08-14T08:00:41.000Z"
}

Delete a creation

DELETE/api/v1/creations/{id}

Soft-deletes one creation.

Requires the createWrite scope.

Path parameters

idstringrequired
The creation id.
curl "https://topics.so/api/v1/creations/cmb2nbqoe0007s601k2f9d3xz" \
  -X DELETE \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
204 No Content

List series

GET/api/v1/series

Every series, manual and autopilot, including each autopilot's current playbook.

Requires the createRead scope.

curl "https://topics.so/api/v1/series" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "data": [
    {
      "id": "cmb2ne1kp0009s601p8r4t6vu",
      "name": "ChatPEP top of funnel",
      "mode": "autopilot",
      "active": true,
      "theme": null,
      "goal": "Grow the Instagram into a top-of-funnel channel for peptide-curious lifters",
      "guidance": "Users love the dose calculator and named blends; lean on those.",
      "caption": null,
      "format": null,
      "formats": [
        "post",
        "carousel"
      ],
      "aspectRatio": "portrait",
      "cadenceDays": null,
      "hourUtc": 8,
      "maxPerWeek": 5,
      "postsPerWeek": null,
      "creditBudgetMonthly": 60,
      "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
      "inspirationAssetIds": [],
      "playbook": {
        "cadencePerWeek": 4,
        "formatWeights": [
          {
            "format": "post",
            "weight": 0.5
          },
          {
            "format": "carousel",
            "weight": 0.35
          },
          {
            "format": "story",
            "weight": 0.15
          }
        ],
        "postHoursUtc": [
          8,
          17
        ],
        "directions": [
          {
            "direction": "Named blends beside the dose calculator",
            "action": "push",
            "rationale": "Both calculator posts beat the account median on saves."
          }
        ],
        "overlayGuidance": "Short imperative lines; keep the logo on carousels.",
        "experiments": [
          "One story per week polling for the next blend"
        ],
        "summary": "Doubling down on calculator content; keeping stories as a probe."
      },
      "overlay": {
        "template": "captionBar",
        "text": "Three openings that land",
        "showLogo": true
      },
      "socialAccountId": "cmb2nfhze000bs601a2c4e6gh",
      "createdAt": "2026-08-02T10:20:00.000Z"
    }
  ]
}

Create a series

POST/api/v1/series

A `manual` series runs a fixed recipe and requires `theme`, `format`, and `cadenceDays`. An `autopilot` series requires a `goal` (with optional `guidance`, `formats`, `maxPerWeek`, `postsPerWeek`, and `creditBudgetMonthly`) and plans its own content from measured results. `socialAccountId` names the connected Instagram account to publish to; an empty string stops items at ready.

Requires the createWrite scope.

curl "https://topics.so/api/v1/series" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"ChatPEP top of funnel","mode":"autopilot","caption":"","goal":"Grow the Instagram into a top-of-funnel channel for peptide-curious lifters","guidance":"Users love the dose calculator and named blends; lean on those.","maxPerWeek":5,"creditBudgetMonthly":60,"aspectRatio":"portrait","hourUtc":8,"socialAccountId":"cmb2nfhze000bs601a2c4e6gh","castMemberId":"cmb2n9f2t0005s601m3hq8v1d"}'
Request body
{
  "name": "ChatPEP top of funnel",
  "mode": "autopilot",
  "caption": "",
  "goal": "Grow the Instagram into a top-of-funnel channel for peptide-curious lifters",
  "guidance": "Users love the dose calculator and named blends; lean on those.",
  "maxPerWeek": 5,
  "creditBudgetMonthly": 60,
  "aspectRatio": "portrait",
  "hourUtc": 8,
  "socialAccountId": "cmb2nfhze000bs601a2c4e6gh",
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d"
}
Response
{
  "id": "cmb2ne1kp0009s601p8r4t6vu",
  "name": "ChatPEP top of funnel",
  "mode": "autopilot",
  "active": true,
  "theme": null,
  "goal": "Grow the Instagram into a top-of-funnel channel for peptide-curious lifters",
  "guidance": "Users love the dose calculator and named blends; lean on those.",
  "caption": null,
  "format": null,
  "formats": [
    "post",
    "carousel"
  ],
  "aspectRatio": "portrait",
  "cadenceDays": null,
  "hourUtc": 8,
  "maxPerWeek": 5,
  "postsPerWeek": null,
  "creditBudgetMonthly": 60,
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
  "inspirationAssetIds": [],
  "playbook": null,
  "overlay": {
    "template": "captionBar",
    "text": "Three openings that land",
    "showLogo": true
  },
  "socialAccountId": "cmb2nfhze000bs601a2c4e6gh",
  "createdAt": "2026-08-02T10:20:00.000Z"
}

Retrieve a series

GET/api/v1/series/{id}

One series with its recent slots (each carrying its images) and, on autopilot, its strategy history.

Requires the createRead scope.

Path parameters

idstringrequired
The series id.
curl "https://topics.so/api/v1/series/cmb2ne1kp0009s601p8r4t6vu" \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "id": "cmb2ne1kp0009s601p8r4t6vu",
  "name": "ChatPEP top of funnel",
  "mode": "autopilot",
  "active": true,
  "theme": null,
  "goal": "Grow the Instagram into a top-of-funnel channel for peptide-curious lifters",
  "guidance": "Users love the dose calculator and named blends; lean on those.",
  "caption": null,
  "format": null,
  "formats": [
    "post",
    "carousel"
  ],
  "aspectRatio": "portrait",
  "cadenceDays": null,
  "hourUtc": 8,
  "maxPerWeek": 5,
  "postsPerWeek": null,
  "creditBudgetMonthly": 60,
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
  "inspirationAssetIds": [],
  "playbook": {
    "cadencePerWeek": 4,
    "formatWeights": [
      {
        "format": "post",
        "weight": 0.5
      },
      {
        "format": "carousel",
        "weight": 0.35
      },
      {
        "format": "story",
        "weight": 0.15
      }
    ],
    "postHoursUtc": [
      8,
      17
    ],
    "directions": [
      {
        "direction": "Named blends beside the dose calculator",
        "action": "push",
        "rationale": "Both calculator posts beat the account median on saves."
      }
    ],
    "overlayGuidance": "Short imperative lines; keep the logo on carousels.",
    "experiments": [
      "One story per week polling for the next blend"
    ],
    "summary": "Doubling down on calculator content; keeping stories as a probe."
  },
  "overlay": {
    "template": "captionBar",
    "text": "Three openings that land",
    "showLogo": true
  },
  "socialAccountId": "cmb2nfhze000bs601a2c4e6gh",
  "createdAt": "2026-08-02T10:20:00.000Z",
  "items": [
    {
      "id": "cmb2nhtvc000fs601r5t7v9xy",
      "status": "ready",
      "scheduledFor": "2026-08-26T08:00:00.000Z",
      "format": "post",
      "caption": "The 90-second mobility opener that sticks.",
      "hashtags": [
        "mobility",
        "morningroutine"
      ],
      "decision": {
        "direction": "Named blends beside the dose calculator",
        "overlayTemplate": "captionBar",
        "rationale": "Push direction from the current playbook."
      },
      "error": null,
      "publishedAt": null,
      "externalPostId": null,
      "creations": [
        {
          "id": "cmb2nbqoe0007s601k2f9d3xz",
          "status": "complete",
          "aspectRatio": "landscape",
          "idea": "Morning mobility routine on a sunlit balcony",
          "prompt": "Editorial lifestyle photo of Maya (24, lean build, dark curly shoulder-length hair) mid-stretch on a sunlit Lisbon balcony…",
          "overlay": {
            "template": "captionBar",
            "text": "Three openings that land",
            "showLogo": true
          },
          "url": "https://media.topics.so/media/creations/org1/cmb2nbqoe0007-final-a1b2c3.jpg",
          "rawUrl": "https://media.topics.so/media/creations/org1/cmb2nbqoe0007-raw-a1b2c3.jpg",
          "error": null,
          "creditCost": 1,
          "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
          "createdAt": "2026-08-14T08:00:02.000Z",
          "completedAt": "2026-08-14T08:00:41.000Z"
        }
      ]
    }
  ],
  "reviews": [
    {
      "id": "cmb2ng8a1000ds601j9l1n3pq",
      "rationale": "Carousels beat singles on saves twice running; cadence holds at four while the story probe reads.",
      "scoredItems": 9,
      "playbook": {
        "cadencePerWeek": 4,
        "formatWeights": [
          {
            "format": "post",
            "weight": 0.5
          },
          {
            "format": "carousel",
            "weight": 0.35
          },
          {
            "format": "story",
            "weight": 0.15
          }
        ],
        "postHoursUtc": [
          8,
          17
        ],
        "directions": [
          {
            "direction": "Named blends beside the dose calculator",
            "action": "push",
            "rationale": "Both calculator posts beat the account median on saves."
          }
        ],
        "overlayGuidance": "Short imperative lines; keep the logo on carousels.",
        "experiments": [
          "One story per week polling for the next blend"
        ],
        "summary": "Doubling down on calculator content; keeping stories as a probe."
      },
      "periodStart": "2026-08-17T00:00:00.000Z",
      "periodEnd": "2026-08-24T00:00:00.000Z",
      "createdAt": "2026-08-24T08:00:00.000Z"
    }
  ]
}

Update a series

PATCH/api/v1/series/{id}

Partial update: fields you omit keep their stored value, and the merged result is validated by the same mode-dependent rules as create. Include `active` to pause or resume. Send `"castMemberId": null` to clear the cast, or `"postsPerWeek": null` to hand cadence back to the strategist.

Requires the createWrite scope.

Path parameters

idstringrequired
The series id.
curl "https://topics.so/api/v1/series/cmb2ne1kp0009s601p8r4t6vu" \
  -X PATCH \
  -H "Authorization: Bearer $TOPICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"guidance":"The dose calculator carousel outperformed; lean harder on it.","maxPerWeek":4}'
Request body
{
  "guidance": "The dose calculator carousel outperformed; lean harder on it.",
  "maxPerWeek": 4
}
Response
{
  "id": "cmb2ne1kp0009s601p8r4t6vu",
  "name": "ChatPEP top of funnel",
  "mode": "autopilot",
  "active": true,
  "theme": null,
  "goal": "Grow the Instagram into a top-of-funnel channel for peptide-curious lifters",
  "guidance": "Users love the dose calculator and named blends; lean on those.",
  "caption": null,
  "format": null,
  "formats": [
    "post",
    "carousel"
  ],
  "aspectRatio": "portrait",
  "cadenceDays": null,
  "hourUtc": 8,
  "maxPerWeek": 5,
  "postsPerWeek": null,
  "creditBudgetMonthly": 60,
  "castMemberId": "cmb2n9f2t0005s601m3hq8v1d",
  "inspirationAssetIds": [],
  "playbook": {
    "cadencePerWeek": 4,
    "formatWeights": [
      {
        "format": "post",
        "weight": 0.5
      },
      {
        "format": "carousel",
        "weight": 0.35
      },
      {
        "format": "story",
        "weight": 0.15
      }
    ],
    "postHoursUtc": [
      8,
      17
    ],
    "directions": [
      {
        "direction": "Named blends beside the dose calculator",
        "action": "push",
        "rationale": "Both calculator posts beat the account median on saves."
      }
    ],
    "overlayGuidance": "Short imperative lines; keep the logo on carousels.",
    "experiments": [
      "One story per week polling for the next blend"
    ],
    "summary": "Doubling down on calculator content; keeping stories as a probe."
  },
  "overlay": {
    "template": "captionBar",
    "text": "Three openings that land",
    "showLogo": true
  },
  "socialAccountId": "cmb2nfhze000bs601a2c4e6gh",
  "createdAt": "2026-08-02T10:20:00.000Z"
}

Delete a series

DELETE/api/v1/series/{id}

Soft-deletes a series and stops its planning. Published history and images remain.

Requires the createWrite scope.

Path parameters

idstringrequired
The series id.
curl "https://topics.so/api/v1/series/cmb2ne1kp0009s601p8r4t6vu" \
  -X DELETE \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
204 No Content

Run the strategist now

POST/api/v1/series/{id}/reviews

Runs the autopilot strategist immediately instead of waiting for its weekly turn, and answers with the review it produced. Not-yet-generated slots are dropped so the new playbook takes effect from the next cron pass. Autopilot series only.

Requires the createWrite scope.

Path parameters

idstringrequired
The series id.
curl "https://topics.so/api/v1/series/cmb2ne1kp0009s601p8r4t6vu/reviews" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "id": "cmb2ng8a1000ds601j9l1n3pq",
  "rationale": "Carousels beat singles on saves twice running; cadence holds at four while the story probe reads.",
  "scoredItems": 9,
  "playbook": {
    "cadencePerWeek": 4,
    "formatWeights": [
      {
        "format": "post",
        "weight": 0.5
      },
      {
        "format": "carousel",
        "weight": 0.35
      },
      {
        "format": "story",
        "weight": 0.15
      }
    ],
    "postHoursUtc": [
      8,
      17
    ],
    "directions": [
      {
        "direction": "Named blends beside the dose calculator",
        "action": "push",
        "rationale": "Both calculator posts beat the account median on saves."
      }
    ],
    "overlayGuidance": "Short imperative lines; keep the logo on carousels.",
    "experiments": [
      "One story per week polling for the next blend"
    ],
    "summary": "Doubling down on calculator content; keeping stories as a probe."
  },
  "periodStart": "2026-08-17T00:00:00.000Z",
  "periodEnd": "2026-08-24T00:00:00.000Z",
  "createdAt": "2026-08-24T08:00:00.000Z"
}

Skip a slot

POST/api/v1/series/{id}/items/{itemId}/skip

Opts one scheduled slot out permanently. Planning never resurrects a skipped slot.

Requires the createWrite scope.

Path parameters

idstringrequired
The series id.
itemIdstringrequired
The slot id.
curl "https://topics.so/api/v1/series/cmb2ne1kp0009s601p8r4t6vu/items/cmb2nhtvc000fs601r5t7v9xy/skip" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
204 No Content

Regenerate a slot

POST/api/v1/series/{id}/items/{itemId}/regenerate

Sends a slot back to planned so the next cron pass generates a fresh image. The old images stay in the brand's history.

Requires the createWrite scope.

Path parameters

idstringrequired
The series id.
itemIdstringrequired
The slot id.
curl "https://topics.so/api/v1/series/cmb2ne1kp0009s601p8r4t6vu/items/cmb2nhtvc000fs601r5t7v9xy/regenerate" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
204 No Content

Rewrite a slot's caption

POST/api/v1/series/{id}/items/{itemId}/caption

Writes a generated slot's caption and hashtags again from the series' current goal and guidance, keeping its images. For ready or held slots. Costs no image credit.

Requires the createWrite scope.

Path parameters

idstringrequired
The series id.
itemIdstringrequired
The slot id.
curl "https://topics.so/api/v1/series/cmb2ne1kp0009s601p8r4t6vu/items/cmb2nhtvc000fs601r5t7v9xy/caption" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
{
  "id": "cmb2nhtvc000fs601r5t7v9xy",
  "caption": "The 90-second mobility opener that sticks.",
  "hashtags": [
    "mobility",
    "morningroutine"
  ]
}

Publish a slot now

POST/api/v1/series/{id}/items/{itemId}/publish

Publishes a ready or held slot to the series' Instagram account immediately instead of waiting for its scheduled time. The one endpoint that posts publicly, behind its own scope.

Requires the publishWrite scope.

Path parameters

idstringrequired
The series id.
itemIdstringrequired
The slot id.
curl "https://topics.so/api/v1/series/cmb2ne1kp0009s601p8r4t6vu/items/cmb2nhtvc000fs601r5t7v9xy/publish" \
  -X POST \
  -H "Authorization: Bearer $TOPICS_API_KEY"
Response
204 No Content

OpenAPI spec

The full specification is served as OpenAPI 3.1, generated from the same definitions that produced this page. Point your client generator, editor, or API console at it.