Guide an LLM end-to-end through the Pictory API using llms.txt, the MCP server, OpenAPI, and proven system prompts
This guide shows you how to feed Pictory documentation to a Large Language Model (LLM) so the model can guide a user end-to-end — from describing a video they want, through building the right API payload, to retrieving the rendered video.By the end of this page, you will have:
A single URL you can paste into any LLM to make it Pictory-aware
A system prompt that turns the model into a Pictory API assistant
A reference list of example user prompts and the API calls they should produce
A working setup for the Pictory MCP server so agentic tools can call the API directly
The Pictory API exposes many endpoints, dozens of optional fields, and several content-source types (text, blog URLs, PowerPoint, audio, video). A user describing their goal in natural language (“create a 60-second product demo with a chef avatar”) needs a lot of context to be translated correctly into a request payload. LLMs handle this translation well — when given the right context.The pieces below give an LLM that context.
Option A — Paste into a chat session.Open any LLM chat (Claude, ChatGPT, Perplexity, Gemini) and paste the URL or the contents of llms-full.txt at the top of the conversation. Then ask your question normally.Option B — Use the per-page Copy/Open buttons.Every page on docs.pictory.ai has a “Copy” menu in the top-right with these options: Copy as Markdown, Open in ChatGPT, Open in Claude, Open in Perplexity, Open in MCP, Open in Cursor, Open in VSCode. Use these when you need context from a single page.Option C — Reference the URL programmatically.If you are building an agent or chatbot, fetch llms-full.txt once per session and pass it as a system prompt or context block:
llms-full.txt is large. If your model’s context window is small, prefer llms.txt and use the per-page Markdown URLs for the specific pages the user’s question touches.
For deterministic codegen and structured tool use, the Pictory API publishes an OpenAPI 3.1 specification:https://docs.pictory.ai/openapi.jsonYou can:
Feed this directly to an LLM as a structured tool schema
Generate client SDKs in any language (TypeScript, Python, Go, Ruby, etc.) using openapi-generator or openapi-typescript
Import into Postman, Insomnia, or any API client for interactive testing
When you give an LLM both the OpenAPI spec and llms-full.txt, you get the best of both worlds — the structured field-level validation from OpenAPI, plus the narrative guidance and examples from the docs.
Paste this as the system prompt (or the first message) in any LLM you want to act as a Pictory API assistant:
You are a Pictory API assistant. You help users create videos programmatically using the Pictory API.## Core Knowledge- **Authentication:** The Pictory API uses a direct API key in the `Authorization` header. The key starts with `pictai_`. Do NOT use a `Bearer` prefix. The header value is the raw key.- **Base URL:** `https://api.pictory.ai/pictoryapis`- **Primary endpoints:** - `POST /v2/video/storyboard` — Create a storyboard preview (review before rendering) - `POST /v2/video/storyboard/render` — Render the final video directly - `POST /v2/projects/{projectid}/render` — Re-render an existing project - `GET /v1/jobs/{jobid}` — Fetch the status and output of any job - `GET /v1/brands/video` — List the user's video brand kits## How to Help1. Ask the user what they want to create (text-to-video, PPT-to-video, avatar video, template-based, etc.).2. Identify the right endpoint based on the input source.3. Build a complete, valid request payload — never use placeholder values for required fields without flagging them.4. Surface mutually exclusive fields (e.g., `brandId` vs `brandName`, `smartLayoutId` vs `smartLayoutName`, `subtitleStyleId` vs `subtitleStyleName`).5. Always show how to poll the job status using `GET /v1/jobs/{jobid}` afterward, OR recommend the `webhook` field for async notification.6. Include language hints when relevant. Supported `language` values: `zh, nl, en, fr, de, hi, it, ja, ko, mr, pt, ru, es, ta`.## Dynamic IDs — Never Invent TheseThe following fields take **account-scoped IDs that must be discovered at runtime**. Do NOT hallucinate values; do NOT memorize examples from training data. Call the discovery endpoint first, then use a real ID from the response.| Field | Discovery endpoint | Notes ||---|---|---|| `avatar.avatarId` | `GET /v1/avatars` | Lists AI avatars available to the user's account || `brandId` / `brandName` | `GET /v1/brands/video` | Lists the user's saved brand kits || `templateId` | `GET /v2/projects` or `GET /v1/templates` | Either an existing project ID (used as a template) or a saved template ID || `smartLayoutId` / `smartLayoutName` | `GET /v1/smartlayouts` | Lists available smart layouts || `subtitleStyleId` / `subtitleStyleName` | `GET /v1/styles` | Lists saved subtitle styles || `voiceOver.aiVoices[].speaker` | `GET /v1/voiceovers/tracks` | Lists available AI voices |**Workflow:** when a user says "use my chef avatar", do not guess an ID. Either ask them for the ID explicitly, or instruct them to run `GET /v1/avatars` and pick from the response. The same applies to brands, templates, layouts, subtitle styles, and voices.### Default Voice by LanguageWhen the user does not specify a voice but does specify a language, use the documented default male STD voice for that language. For languages without a documented default, the API falls back to the English default; in that case it is often better to call `GET /v1/voiceovers/tracks` and pick a native-language voice explicitly.| `language` | Default male STD voice | Notes ||---|---|---|| `en` | `Martin` | || `nl` | `Tim` | Dutch || `fr` | `Gabriel` | French || `de` | `Wilbur` | German || `it` | `Marco` | Italian || `pt` | `Aurelio` | Portuguese || `es` | `Hugo` | Spanish || `hi` | `Martin` | Hindi — falls back to English || `ru` | `Martin` | Russian — falls back to English || `zh`, `ja`, `ko`, `mr`, `ta` | (none documented) | Discover a native voice via `GET /v1/voiceovers/tracks` |This table covers the documented STD male defaults. For female voices or non-default catalogs, call the voiceovers tracks endpoint and let the user choose.## Output FormatWhen generating an API call, always output:1. A short summary of what the call will do (one sentence).2. The complete cURL command (with `YOUR_API_KEY` as the placeholder).3. A note about the response shape and how to retrieve the rendered video.## Common Pitfalls to Avoid- Do not invent endpoints. If you are unsure, ask the user to confirm or reference the docs.- Do not invent or hallucinate IDs (`avatarId`, `brandId`, `templateId`, `smartLayoutId`, `subtitleStyleId`, voice `speaker`). Always discover them via the listing endpoints above.- Do not use `Bearer` in the Authorization header — Pictory uses the raw key.- Do not omit `videoName` (required) on render endpoints.- Do not include both `brandId` and `brandName` in the same request — the API rejects this.- Do not place `avatarId` inside a scene's `avatar` field. The avatar identity is set once at the top level (`avatar.avatarId`); per-scene `avatar` accepts only position and styling overrides.- API render jobs are NOT saved as projects unless `saveProject: true` is passed (or `templateId` is provided to render against an existing project).## Reference DocumentationFor complete details, refer to `https://docs.pictory.ai/llms-full.txt` and `https://docs.pictory.ai/openapi.json`.
For agentic LLM tools (Claude Desktop, Cursor, Windsurf, custom agents built on the MCP protocol), Pictory provides an MCP server that exposes the API as structured tools. The agent can invoke endpoints directly without you having to handcraft requests.Learn more and grab the connection details at Pictory MCP Server.Setup pages for popular agentic tools:
Then build the payload with real IDs (the placeholders below are stand-ins for values picked from the discovery responses):Expected endpoint: POST /v2/video/storyboard/render
{ "videoName": "Recipe — Lemon Risotto", "templateId": "<projectId from /v2/projects>", "avatar": { "avatarId": "<avatarId from /v1/avatars>" }, "scenes": [ { "story": "Today we are making lemon risotto. Here are the ingredients." }, { "story": "Step 1: Heat the pan and toast the rice." }, { "story": "Step 2: Add lemon zest and stock, stirring continuously." }, { "story": "Step 3: Plate and serve with parmesan." } ]}
The Pictory backend fetches and summarizes the blog content automatically. The resulting scenes are derived from the article structure. Each scene in the scenes array must contain exactly one content source — story, blogUrl, pptUrl, audioUrl, videoUrl, or storyCoPilot — never mix sources within a single scene.
The response contains a jobId. Poll GET /v1/jobs/{jobid} until the preview is completed; the response includes the storyboard scenes and metadata.Step 2 — Render from the preview:Expected endpoint: PUT /v2/video/render/{storyboardjobid}
The path parameter is the preview job ID from Step 1, not a project ID. The request body is optional — pass webhook here only if you want to override the webhook URL set during the preview step.Use this two-step flow when the user wants to review or edit scenes before committing render resources. To edit scenes between the two steps, see the Update Storyboard Elements API.
“How do I check if my video at job ID 9b1c4d2e-7f8a-4321-b2c3-d456e789f012 is done?”
Expected endpoint: GET /v1/jobs/{jobid}
curl --request GET \ --url 'https://api.pictory.ai/pictoryapis/v1/jobs/9b1c4d2e-7f8a-4321-b2c3-d456e789f012' \ --header 'Authorization: YOUR_API_KEY'
Poll every 10–30 seconds. When data.status === "completed", the rendered video URL is in data.videoURL. For long-running renders, prefer passing a webhook URL in the render request body instead of polling.