Skip to main content

Video Generation API

The Opennote Video API enables you to create high-quality educational videos from text prompts, scripts, or conversational message formats. Our AI-powered video generation system transforms your content into engaging visual presentations with narration, graphics, and educational elements. You can create videos from text prompts, direct scripts, or conversational messages.
1

Submit Video Job Request

Send your content with model preferences and configuration options.
2

Check Status While Processing

Our system processes your content, generates visuals, and creates narration. You can view the status of your video job by polling the status endpoint, or wait for the webhook to be triggered.
3

Deliver Your Video

Get your completed video via S3 URL or direct base64 encoded video.

Quick Start Example

from opennote import OpennoteClient
import time

client = OpennoteClient(api_key="your_editable_api_key")

# Create a video from a simple prompt
response = client.video.create(
    model="picasso",
    messages=[
        {"role": "system", "content": "Create educational content for high school students"},
        {"role": "user", "content": "Explain photosynthesis in plants with visual examples"}
    ],
    title="Photosynthesis in Plants",
    upload_to_s3=True,
    include_sources=True,
    search_for="plant photosynthesis biology",
    source_count=3
)

if response.success:
    print(f"Video creation started! ID: {response.video_id}")
    
    # Poll for completion
    while True:
        status = client.video.status(response.video_id)
        print(f"Status: {status.status} ({status.progress}%)")
        
        if status.status == "completed":
            print(f"Video ready: {status.response.s3_url}!")
            break
        elif status.status == "failed":
            print(f"ERROR, Video creation failed: {status.message}")
            break
        
        time.sleep(15)

Configuration Options

The only model available as of right now is picasso. We are working on more models in the future, as well as dated compatability for models.

Message Formats

System Messages (Optional)

Provide context about the audience, style, or educational goals:
{
  "role": "system", 
  "content": "You are creating content for college-level chemistry students. Use technical terms but explain them clearly."
}

User and Assistant Messages

With the messages parameter, you can provide a list of messages, with the first message being the system message, and the last message being a user message. This can simulate a conversation between an assistant and a user, which can be useful for creating more natural videos that work towards the conversation. The user message will be the last message in the video, and ideally is where the user “requests” the video. The last message will also be the primary message that the video will be about, and the rest of the conversation is used as context for the video. Here is an example of a good user message, though it does not always have to be this verbose:
{
  "role": "user",
  "content": "Create a video explaining chemical bonding, covering ionic, covalent, and metallic bonds with real-world examples."
}
Keep in mind that user messages do NOT determine the length of the video. The length is determined by the length parameter. Stating things like “Make a 10-minute video” will not change anything about the video.

Using Scripts instead of Messages

The script parameter is a more verbose way to create a video, and is more useful for creating detailed videos. It is also cheaper than using the messages parameter, as no script is generated. Scripts are spoken exactly as is, so write them fully, such as f of x for f(x), otherwise the text-to-speech model will not understand the special formatting of characters. You can seperate a script into different sections by using the ----- separator. An example of this is shown in the Extra Examples section.

Advanced Options

response = client.video.create(
    # ... other params
    include_sources=True,
    search_for="<specific topic or keywords>",
    source_count=5,  # Number of sources to include
)
Enable automatic web search to enhance your video with reliable sources and citations. These will be returned to you in the final response as well.
response = client.video.create(
    # ... other params
    webhook_url="https://your-domain.com/video-webhook"
)
Get notified when your video is ready instead of polling the status endpoint. When the video completes (or fails), we will send a POST request to the provided webhook URL, with the same schema as the status endpoint.
response = client.video.create(
    # ... other params
    upload_to_s3=True,  # Store in S3 for reliable access
)
If the above is False, then we will return a base64 encoded video in the response. Otherwise, we return the s3_url of the video, which is uploaded to our S3 bucket for you. These videos are stored permanently for you to access at your conveinece, and will always come back with the following base URL: https://videofs-opennote-us-east-1.s3.us-east-1.amazonaws.com...

Webhooks

When using webhooks, you’ll receive an update about your video once it has been completed, with the same schema as the status endpoint, which you can view in the API Reference. Keep in mind that even though the status endpoint is usually a GET request, the webhook will deliver a POST request to your webhook URL.

Security

To verify that your webhook is coming from the right location, you can expect the following headers to be returned alongside your video:
{
    "Content-Type": "application/json",
    "X-Request-Id": <video_id>,
    "X-Webhook-Platform": "opennote-api",
    "X-Origin": "https://video.api.opennote.com",
    "X-Organization-Id": <your_api_org_id>,
    "X-API-Key": <api_key_used_to_create_the_video>
}
A function such as this one below can be used to verify the webhook.
def verify_webhook(request):
    required_headers = {
        'X-Webhook-Platform': 'opennote-api',
        'X-Origin': 'https://video.api.opennote.com',
        'X-Organization-Id': 'your_api_org_id',
        'X-API-Key': 'your_api_key'
    }
    
    for header, expected in required_headers.items():
        if request.headers.get(header) != expected:
            return False
    return True

Error Handling

Common Error Responses

{
  "error": "Invalid API key or unauthorized access",
  "message": "API key is missing or invalid"
}
Solution: Verify your API key is correct and has editable permissions.
{
  "error": "Insufficient credits", 
  "message": "...",
}
This occurs when your credit balance has dropped below $0 and will block future requests.Solution: Add more credits to your account via the dashboard.
You will get a Retry-After header in the response, which you can use to wait before making another request.
{
  "error": "Video generation failed",
  "message": "...",
  "video_id": "abc123"
}
Solution: Check the content format and try again. Contact support if the issue persists.

Best Practices

Creating Effective Videos

  • Be specific: Include target audience, duration, and key concepts in the system prompt.
  • Provide context: Use system messages to set educational goals
  • Structure content: Break complex topics into clear sections
  • Include examples: Mention specific examples or case studies to include
  • Use Web Search: Enable web search to bring more context to the video model.
  • Literal Scripts: If you are using the script parameter, note that what you write will be spoken exactly as is.
    • Math, formulas, etc. will be spoken exactly as you write them, so write them fully, such as f of x for f(x).
    • The more literal you are in your script for what you want it to say, the better the video will be.

Optimizing Performance

  • Use focused prompts: Specific requests generate faster than broad topics
  • Enable webhooks: Avoid polling overhead with real-time notifications
  • Cache results: Store completed videos to avoid regenerating similar content

Cost Management

  • Preview content: Use shorter videos with the length parameter for testing before creating full-length content
  • Optimize usage: Fewer sources and no web search means lower costs, as well as no title or script generation.
    • Using the script parameter will always be cheaper than using the messages parameter, as no script is generated.
    • Using the title parameter will be cheaper than not, as no title is generated.
    • Mark upload_to_s3 as False to avoid storage costs.
  • Reuse content: Leverage existing videos for similar topics
  • Monitor usage: Track credit consumption in your dashboard

Example Use Cases

Course Materials

Create lecture videos from course outlines or textbook chapters for flipped classroom models.

Training Content

Transform training manuals into engaging video content for employee onboarding and skills development.

Educational Summaries

Convert research papers or long-form content into digestible video summaries.

Interactive Tutorials

Build step-by-step tutorial videos from written instructions or documentation.

Support

Need help with video generation?
  • Technical Issues: devtools@opennote.me
  • Content Guidelines: Check our best practices above
  • API Questions: See our API Reference for detailed documentation
  • Account Issues: Visit your dashboard for billing and usage
Ready to create your first video? Try the API Playground or dive into our SDK documentation.

Extra Examples

These are some extra examples to show different usages of the API.

Using the Script Parameter

This example shows how we used the ----- seperator to break up a script into different sections for a video.
from opennote import OpennoteClient

client = OpennoteClient(api_key="sk_opennote_...")

video_create_response = client.video.create(
    script="""
        Today, we're going to go over scalars and vectors. A Scalar is a quantity that is fully described by a single value, called a Magnitude, which is 
        the size or numerical value of a vector quantity, and has no Direction, i.e. The line or path along which something points or moves. 
        These include such quantities such as mass, time, and speed.
        -----
        A Vector is a quantity that has both Magnitude and Direction. Vectors are used to represent physical quantities that require both of these components 
        for a complete description. Vectors can be seen as an arrow in space, in which its length represents its magnitude and the direction the arrow is pointing is its direction. 
        A vector is also commonly described using the tail to head method, where the direction of the vector originates from the tail and goes towards the head of that vector. 
        These include such quantities such as displacement, velocity, and force. Vectors can be denoted with an arrow on top of the variable that is a vector. 
        A noteworthy example is that speed, which is a scalar, shows the same magnitude as velocity, which is a vector, but that speed has no direction, while velocity does.
        -----
        Sometimes, in a problem with many vectors, the vectors will not be aligned with each other, making the problem more tricky to figure out. 
        In physics, Trigonometry is frequently used with vectors to analyze their Components and break them into different directions. This is especially 
        useful when vectors are not aligned with the Standard Axes, x-axis and y-axis, in a coordinate system. The relationship between vectors and trigonometry comes 
        from the need to break a vector into its components or to calculate the magnitude and direction from its components.
        -----
        Let's say we have a vector "a" that is some angle "theta" above the x-axis. The magnitude of this vector is denoted as |a|. Using trigonometry, the vector's horizontal component 
        can be calculated using |a|cos(theta) and the vertical component can be calculated using |y|cos(theta). Using the Pythagorean Theorem among side lengths in a right triangle, 
        it can be derived that the magnitude of a equals the square root of the x component squared plus the y component squared. To find the angle, trigonometric identities are used, 
        which gives theta equals the inverse tangent of the y component over the x component.
    """, 
    upload_to_s3=True, 
    title="1.1 - Scalars and Vectors"
)


response = client.video.status(video_create_response.video_id)
print(response)
Here is the video that was generated with this script above: As you can see, verbose scripts are very useful for creating detailed videos.
I