> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mavera.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart: Video Analysis

> Upload a video, run AI-powered analysis for emotional and engagement metrics, and chat about results in under 20 minutes

## What You'll Learn

In this quickstart you will:

* **Upload a video** to Mavera using the Files API (presigned URL flow) so it can be used as input for analysis.
* **Create a video analysis** job with a title, goal, brand/product context, and analysis settings (chunk duration, frames per chunk).
* **Poll for completion** and then read **full-video metrics** (overall score, emotional impact, attention, CTA effectiveness) and **chunk-level** breakdowns.
* **Optionally** use the analysis chat endpoint to ask follow-up questions (e.g. "What are the weakest moments and how can I improve them?").

Video Analysis is built for ad creatives, product videos, and any content where you want measurable engagement and emotional signals.

<Info>
  **Time:** About 20 minutes (upload is quick; analysis can take 2–10 minutes depending on video length). **Credits:** Approximately 100–500 credits depending on duration; see table below.
</Info>

***

## Prerequisites

<Check>**Mavera account** with an active subscription and enough credits (video analysis is credit-heavy).</Check>
<Check>**API key** from [Developer Settings](https://app.mavera.io/settings/developer).</Check>
<Check>**Workspace ID** where the video file and analysis will live. Find it in the dashboard or via the workspaces API.</Check>
<Check>**A video file** on your machine (e.g. MP4, MOV). Short clips (e.g. 15–60 seconds) are best for a first run; max size is 2 GB for video files.</Check>

***

## Overview of the Flow

<Steps>
  <Step title="Upload the video">
    Use the Files API: request a presigned upload URL, upload the file to that URL, then create a file record. You receive a **file ID** (used as `asset_id` for video analysis).
  </Step>

  <Step title="Create the analysis">
    Call `POST /video-analyses` with `asset_id` (your file ID), plus title, goal, brand, product, intent, and analysis options. You receive an **analysis ID** and initial status (e.g. `PENDING`).
  </Step>

  <Step title="Poll until completed">
    Call `GET /video-analyses/{id}` periodically until `status` is `COMPLETED`. Analysis typically takes a few minutes.
  </Step>

  <Step title="Read results">
    From the completed analysis, use `results.full_video_metrics` for overall scores and `results.chunks` for segment-by-segment breakdowns. Optionally use `POST /video-analyses/{id}/chat` to ask questions about the analysis.
  </Step>
</Steps>

***

## Step 1: Upload the Video (Files API)

Videos must be in Mavera's storage before you can analyze them. The Files API uses a **presigned URL** flow: you never send the file bytes to the Mavera API server; you upload directly to storage.

### Step 1a: Request a presigned upload URL

Send file metadata (name, type, size, workspace\_id) to get an `upload_url` and `public_url`.

<CodeGroup>
  ```python Python theme={"dark"}
  import requests

  API_KEY = "mvra_live_your_key_here"
  HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
  WORKSPACE_ID = "ws_your_workspace_id"
  BASE = "https://app.mavera.io/api/v1"

  # Path to your video file
  VIDEO_PATH = "path/to/your/clip.mp4"

  with open(VIDEO_PATH, "rb") as f:
      content = f.read()

  file_size = len(content)
  file_name = VIDEO_PATH.split("/")[-1]
  file_type = "video/mp4"  # or video/quicktime for MOV

  upload_resp = requests.post(
      f"{BASE}/files/upload-url",
      headers=HEADERS,
      json={
          "file_name": file_name,
          "file_type": file_type,
          "file_size": file_size,
          "workspace_id": WORKSPACE_ID,
      },
  ).json()

  if "error" in upload_resp:
      raise Exception(upload_resp["error"]["message"])

  upload_url = upload_resp["upload_url"]
  public_url = upload_resp["public_url"]
  print(f"Upload URL expires in {upload_resp.get('expires_in', '?')}s")
  ```

  ```javascript JavaScript theme={"dark"}
  const API_KEY = "mvra_live_your_key_here";
  const HEADERS = {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  };
  const WORKSPACE_ID = "ws_your_workspace_id";
  const BASE = "https://app.mavera.io/api/v1";

  // In Node you might read the file from disk or get it from a multipart upload
  const fs = await import("fs");
  const path = "path/to/your/clip.mp4";
  const content = fs.readFileSync(path);
  const fileSize = content.length;
  const fileName = path.split("/").pop();
  const fileType = "video/mp4";

  const uploadResp = await fetch(`${BASE}/files/upload-url`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
      file_name: fileName,
      file_type: fileType,
      file_size: fileSize,
      workspace_id: WORKSPACE_ID,
    }),
  }).then((r) => r.json());

  if (uploadResp.error) throw new Error(uploadResp.error.message);

  const { upload_url: uploadUrl, public_url: publicUrl } = uploadResp;
  console.log("Got presigned URL");
  ```
</CodeGroup>

### Step 1b: Upload the file to the presigned URL

Use a `PUT` request with the file body and the correct `Content-Type`.

<CodeGroup>
  ```python Python theme={"dark"}
  put_resp = requests.put(
      upload_url,
      data=content,
      headers={"Content-Type": file_type},
  )
  put_resp.raise_for_status()
  ```

  ```javascript JavaScript theme={"dark"}
  await fetch(uploadUrl, {
    method: "PUT",
    body: content,
    headers: { "Content-Type": fileType },
  });
  ```
</CodeGroup>

### Step 1c: Create the file record

Register the file with Mavera so you get a stable **file ID** (this is the `asset_id` for video analysis).

<CodeGroup>
  ```python Python theme={"dark"}
  file_resp = requests.post(
      f"{BASE}/files",
      headers=HEADERS,
      json={
          "name": file_name,
          "type": file_type,
          "url": public_url,
          "workspace_id": WORKSPACE_ID,
          "file_size": file_size,
      },
  ).json()

  if "error" in file_resp:
      raise Exception(file_resp["error"]["message"])

  asset_id = file_resp["id"]
  print(f"File created; asset_id for video analysis: {asset_id}")
  ```

  ```javascript JavaScript theme={"dark"}
  const fileResp = await fetch(`${BASE}/files`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
      name: fileName,
      type: fileType,
      url: publicUrl,
      workspace_id: WORKSPACE_ID,
      file_size: fileSize,
    }),
  }).then((r) => r.json());

  if (fileResp.error) throw new Error(fileResp.error.message);

  const assetId = fileResp.id;
  console.log("File created; asset_id for video analysis:", assetId);
  ```
</CodeGroup>

<Warning>
  Presigned URLs expire (often within an hour). Upload the file and create the record soon after requesting the URL. If upload fails, request a new URL and retry.
</Warning>

***

## Step 2: Create the Video Analysis

Pass the **asset\_id** (your file ID) plus metadata and analysis options. The API returns an analysis ID and status (e.g. `PENDING` or `RUNNING`).

<CodeGroup>
  ```python Python theme={"dark"}
  analysis_payload = {
      "title": "Quickstart: Ad Clip Analysis",
      "asset_id": asset_id,
      "goal": "Analyze viewer engagement and emotional response",
      "brand": "Your Brand",
      "product": "Product Name",
      "primary_intent": "Drive product awareness",
      "chunk_duration": 5,
      "frames_per_chunk": 3,
      "workspace_id": WORKSPACE_ID,
  }

  create_resp = requests.post(
      f"{BASE}/video-analyses",
      headers=HEADERS,
      json=analysis_payload,
  ).json()

  if "error" in create_resp:
      raise Exception(create_resp["error"]["message"])

  analysis_id = create_resp["id"]
  status = create_resp["status"]
  print(f"Analysis created: {analysis_id}")
  print(f"Status: {status}")
  ```

  ```javascript JavaScript theme={"dark"}
  const analysisPayload = {
    title: "Quickstart: Ad Clip Analysis",
    asset_id: assetId,
    goal: "Analyze viewer engagement and emotional response",
    brand: "Your Brand",
    product: "Product Name",
    primary_intent: "Drive product awareness",
    chunk_duration: 5,
    frames_per_chunk: 3,
    workspace_id: WORKSPACE_ID,
  };

  const createResp = await fetch(`${BASE}/video-analyses`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify(analysisPayload),
  }).then((r) => r.json());

  if (createResp.error) throw new Error(createResp.error.message);

  const analysisId = createResp.id;
  console.log("Analysis created:", analysisId);
  console.log("Status:", createResp.status);
  ```

  ```bash cURL theme={"dark"}
  curl -X POST https://app.mavera.io/api/v1/video-analyses \
    -H "Authorization: Bearer mvra_live_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "title": "Quickstart: Ad Clip Analysis",
      "asset_id": "file_abc123",
      "goal": "Analyze viewer engagement and emotional response",
      "brand": "Your Brand",
      "product": "Product Name",
      "primary_intent": "Drive product awareness",
      "chunk_duration": 5,
      "frames_per_chunk": 3,
      "workspace_id": "ws_your_workspace_id"
    }'
  ```
</CodeGroup>

| Parameter                                    | Description                                                       |
| -------------------------------------------- | ----------------------------------------------------------------- |
| `asset_id`                                   | The file ID from the Files API (your uploaded video).             |
| `chunk_duration`                             | Length of each analyzed segment in seconds (e.g. 5).              |
| `frames_per_chunk`                           | Number of frames analyzed per chunk (e.g. 3).                     |
| `goal`, `brand`, `product`, `primary_intent` | Context used to improve relevance of metrics and recommendations. |

***

## Step 3: Poll Until Completed

Analysis runs asynchronously. Poll `GET /video-analyses/{id}` every 15–30 seconds until `status` is `COMPLETED`.

<CodeGroup>
  ```python Python theme={"dark"}
  import time

  def get_analysis(aid):
      r = requests.get(f"{BASE}/video-analyses/{aid}", headers=HEADERS)
      return r.json()

  for _ in range(40):
      analysis = get_analysis(analysis_id)
      if "error" in analysis:
          raise Exception(analysis["error"]["message"])
      if analysis.get("status") == "COMPLETED":
          break
      print(f"Status: {analysis.get('status')}; waiting 15s...")
      time.sleep(15)
  else:
      raise Exception("Analysis did not complete in time")
  ```

  ```javascript JavaScript theme={"dark"}
  async function getAnalysis(id) {
    const r = await fetch(`${BASE}/video-analyses/${id}`, { headers: HEADERS });
    return r.json();
  }

  for (let i = 0; i < 40; i++) {
    const analysis = await getAnalysis(analysisId);
    if (analysis.error) throw new Error(analysis.error.message);
    if (analysis.status === "COMPLETED") break;
    console.log(`Status: ${analysis.status}; waiting 15s...`);
    await new Promise((r) => setTimeout(r, 15000));
  }
  ```

  ```bash cURL theme={"dark"}
  # Poll (replace va_abc123 with your analysis id)
  curl -s "https://app.mavera.io/api/v1/video-analyses/va_abc123" \
    -H "Authorization: Bearer mvra_live_your_key_here" | jq '{id, status}'
  ```
</CodeGroup>

***

## Step 4: Read the Results

Once `status` is `COMPLETED`, the response includes a `results` object with **full-video metrics** and **chunks** (segment-level data).

<CodeGroup>
  ```python Python theme={"dark"}
  metrics = analysis["results"]["full_video_metrics"]
  print("Overall score:", metrics.get("overall_score"))
  print("Emotional impact:", metrics.get("emotional_impact"))
  print("Attention score:", metrics.get("attention_score"))
  print("CTA effectiveness:", metrics.get("cta_effectiveness"))
  print("Brand recall likelihood:", metrics.get("brand_recall_likelihood"))

  print("\nChunk breakdown:")
  for chunk in analysis["results"].get("chunks", []):
      print(f"  {chunk['start_time']}s–{chunk['end_time']}s: engagement={chunk.get('engagement_score')}, valence={chunk.get('emotional_valence')}")

  print("\nRecommendations:", analysis["results"].get("recommendations", []))
  print("Credits used:", analysis.get("usage", {}).get("credits_used"))
  ```

  ```javascript JavaScript theme={"dark"}
  const metrics = analysis.results.full_video_metrics;
  console.log("Overall score:", metrics.overall_score);
  console.log("Emotional impact:", metrics.emotional_impact);
  console.log("Attention score:", metrics.attention_score);
  console.log("CTA effectiveness:", metrics.cta_effectiveness);

  console.log("\nChunk breakdown:");
  analysis.results.chunks?.forEach((chunk) => {
    console.log(`  ${chunk.start_time}s–${chunk.end_time}s: engagement=${chunk.engagement_score}`);
  });

  console.log("Recommendations:", analysis.results.recommendations);
  console.log("Credits used:", analysis.usage?.credits_used);
  ```
</CodeGroup>

Typical **full\_video\_metrics** fields:

| Field                     | Description                                 |
| ------------------------- | ------------------------------------------- |
| `overall_score`           | Aggregate score (e.g. 0–100).               |
| `emotional_impact`        | Strength of emotional response (e.g. 1–10). |
| `attention_score`         | How well the video holds attention.         |
| `cta_effectiveness`       | Effectiveness of call-to-action.            |
| `brand_recall_likelihood` | Estimated likelihood of brand recall.       |

**Chunks** give you segment-level engagement, emotional valence, key moments, and optional recommendations per segment.

***

## Step 5: Chat About the Analysis (Optional)

You can ask natural-language questions about the completed analysis (e.g. weak spots, how to improve). Use `POST /video-analyses/{id}/chat`.

<CodeGroup>
  ```python Python theme={"dark"}
  chat_resp = requests.post(
      f"{BASE}/video-analyses/{analysis_id}/chat",
      headers=HEADERS,
      json={
          "message": "What are the weakest moments in this video and how can I improve them?"
      },
  ).json()

  if "error" not in chat_resp:
      print(chat_resp.get("content", ""))
  ```

  ```javascript JavaScript theme={"dark"}
  const chatResp = await fetch(`${BASE}/video-analyses/${analysisId}/chat`, {
    method: "POST",
    headers: HEADERS,
    body: JSON.stringify({
      message: "What are the weakest moments in this video and how can I improve them?",
    }),
  }).then((r) => r.json());

  if (!chatResp.error) console.log(chatResp.content);
  ```

  ```bash cURL theme={"dark"}
  curl -X POST "https://app.mavera.io/api/v1/video-analyses/va_abc123/chat" \
    -H "Authorization: Bearer mvra_live_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{"message": "What are the weakest moments and how can I improve them?"}'
  ```
</CodeGroup>

***

## Credit Costs

| Video length  | Approximate credits |
| ------------- | ------------------- |
| \< 30 seconds | 100–150             |
| 30 s – 1 min  | 150–250             |
| 1–3 minutes   | 250–400             |
| 3+ minutes    | 400+                |

File upload and chat about results use additional credits; see [Credits](/guides/credits) and the API reference.

***

## Common Issues

<AccordionGroup>
  <Accordion title="Presigned upload fails or expires">
    Request a new upload URL and retry the PUT and file record creation. Don't delay between getting the URL and uploading.
  </Accordion>

  <Accordion title="asset_id invalid or 404">
    Use the `id` returned from `POST /files` after uploading. Ensure the file is in the same workspace you use in the analysis request.
  </Accordion>

  <Accordion title="Analysis stuck in PENDING/RUNNING">
    Longer videos take longer. Poll for several minutes; if it never completes, check [status](https://status.mavera.io) or contact support.
  </Accordion>

  <Accordion title="File size or type rejected">
    Video files are supported up to 2 GB. Use a supported type (e.g. video/mp4, video/quicktime). See [Files](/features/files#file-size-limits).
  </Accordion>

  <Accordion title="402 credits_exhausted">
    Video analysis is credit-intensive. Refill credits or use a shorter clip; see [Credits](/guides/credits).
  </Accordion>
</AccordionGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Video Analysis" icon="video" href="/features/video-analysis">
    All metrics, chunk options, and response shapes
  </Card>

  <Card title="Files & Folders" icon="cloud-arrow-up" href="/features/files">
    Upload flow, folders, and using files with other APIs
  </Card>

  <Card title="Workspaces" icon="folder" href="/features/workspaces">
    Organize files and analyses by workspace
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/video-analysis/create-a-video-analysis">
    Full video analysis request/response specification
  </Card>
</CardGroup>

Use video analysis to compare creatives, optimize ad length, or improve CTA placement—then iterate with new uploads and analyses.
