Skip to content
UGC API

Posting tools for your own AI agent

Building an agent that runs without a person in the chat? Skip MCP and wrap three REST endpoints as tools. You get every platform's publishing behind one API key, with refunds when a platform rejects a post.

Updated

The three tools most agents need

ToolEndpointWhy
list_accountsGET /v1/accountsSo the agent posts to ids that exist
publish_postPOST /v1/postsVideo, slideshow or image to many accounts, now or scheduled
get_postGET /v1/posts/{id}Status and public URL per account, to report back or retry

Add POST /v1/campaigns when the agent plans many posts at once, and GET /v1/stats when it should learn from results. Full shapes are in create a post and campaigns.

OpenAI Agents SDK (Python)

python
import os, requests
from agents import Agent, Runner, function_tool

H = {"Authorization": f"Bearer {os.environ['UGCAPI_KEY']}"}

@function_tool
def list_accounts() -> list[dict]:
    """Connected social accounts with id, platform and username."""
    return requests.get("https://ugcapi.dev/v1/accounts", headers=H).json()["data"]

@function_tool
def publish_post(accounts: list[str], format: str, media: list[str], caption: str, scheduled_at: str | None = None) -> dict:
    """Publish or schedule a video, slideshow or image. format: video | slideshow | image."""
    body = {"accounts": accounts, "format": format, "media": media, "caption": caption, "scheduled_at": scheduled_at}
    return requests.post("https://ugcapi.dev/v1/posts", headers=H, json=body).json()

agent = Agent(name="UGC scheduler", instructions="Plan and schedule UGC. Never post sooner than 30 minutes from now.",
              tools=[list_accounts, publish_post])
print(Runner.run_sync(agent, "Schedule hook-3.mp4 to every TikTok account tomorrow 9am UTC").final_output)

Vercel AI SDK (TypeScript)

ts
import { tool } from "ai";
import { z } from "zod";

export const publishPost = tool({
  description: "Publish or schedule UGC to connected TikTok, Instagram, Facebook and YouTube accounts",
  inputSchema: z.object({
    accounts: z.array(z.string()),
    format: z.enum(["video", "slideshow", "image"]),
    media: z.array(z.string().url()),
    caption: z.string(),
    scheduled_at: z.string().optional(),
  }),
  execute: async (input) => {
    const r = await fetch("https://ugcapi.dev/v1/posts", {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.UGCAPI_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify(input),
    });
    return r.json();
  },
});

LangChain, CrewAI and the rest

Same idea: a function with a docstring and typed arguments that makes the HTTP call. LangChain's @tool decorator and CrewAI's BaseTool both take the Python function above with no changes beyond the decorator.

Design tips from teams running this in production

  • Let code pick accounts, let the model write. Agents are great at captions and matching hooks to audiences; deterministic code is better at cadence. Use POST /v1/campaigns for spacing.
  • Pass an `external_id`. Tag posts with your own job or user id so the agent can find its own work later.
  • Listen to webhooks, don't poll. post.published and post.failed tell your agent what happened. See webhooks.
  • Failed posts are refunded, so a retry loop on platform_rejected costs nothing extra until it succeeds.

FAQ

MCP or REST for my agent?

MCP when a person chats with the agent and approves access with OAuth. REST with an API key when the agent runs on its own, in a job, a queue or your backend.

Is there an OpenAPI spec?

The docs list every endpoint with request and response examples, and llms.txt at ugcapi.dev/llms.txt gives agents a compact summary.

Can my users' agents post to their own accounts?

Yes. Create a connect link per user with their id as external_id, then pass the account ids they connected.

Related