# Check a video

Send a video to a check, follow its job, then read the video's verdict and each frame's reading.

## How a video runs

A video check is a job. `POST /audit` answers 202 at once with its `id` and `media: "video"`, and you poll [`GET /audit/{id}`](/docs/api-reference/get-check) until its `status` is `succeeded` or `failed`.

The job moves from `queued` to `running`, then ends as `succeeded` or `failed`. Poll it every few seconds. Each poll counts toward your [requests a minute](/docs/credits-and-limits#requests-a-minute), so on 429 `rate_limited` wait the seconds in `Retry-After`, then poll again.

Send exactly one of `video_url` or `video_upload_id`. A video on disk goes through [an upload](/docs/guides/upload-a-local-file) with its video `content_type`. A large video is faster that way than by URL.

## Send the length

Pass `seconds`, the video's length, when you know it. Credits are held for that length and a video that runs longer is refused. Without it, credits for the longest video are held, and the real length is charged once the job finishes. [Video limits](/docs/credits-and-limits#video-limits) has the numbers.

## Check a video

**Check a video by URL**

```bash title="cURL"
curl -X POST "https://realpng.com/api/v1/audit" \
  -H "X-API-Key: $REALPNG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://videos.example.com/drone.mp4",
    "seconds": 12
  }'
```

```javascript title="JavaScript"
const res = await fetch("https://realpng.com/api/v1/audit", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.REALPNG_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "video_url": "https://videos.example.com/drone.mp4",
    "seconds": 12
  }),
});
const data = await res.json();
```

```python title="Python"
import os

import requests

res = requests.post(
    "https://realpng.com/api/v1/audit",
    headers={"X-API-Key": os.environ["REALPNG_API_KEY"]},
    json={
        "video_url": "https://videos.example.com/drone.mp4",
        "seconds": 12,
    },
)
data = res.json()
```

Response `202 Accepted`

```json
{
  "id": "b8f2d6a4-3c1e-4f7a-9d5b-2e6c8a0f4b13",
  "status": "queued",
  "media": "video"
}
```

Then poll the check until it finishes.

**Get the video's check**

```bash title="cURL"
curl "https://realpng.com/api/v1/audit/b8f2d6a4-3c1e-4f7a-9d5b-2e6c8a0f4b13" \
  -H "X-API-Key: $REALPNG_API_KEY"
```

```javascript title="JavaScript"
const res = await fetch("https://realpng.com/api/v1/audit/b8f2d6a4-3c1e-4f7a-9d5b-2e6c8a0f4b13", {
  headers: {
    "X-API-Key": process.env.REALPNG_API_KEY,
  },
});
const data = await res.json();
```

```python title="Python"
import os

import requests

res = requests.get(
    "https://realpng.com/api/v1/audit/b8f2d6a4-3c1e-4f7a-9d5b-2e6c8a0f4b13",
    headers={"X-API-Key": os.environ["REALPNG_API_KEY"]},
)
data = res.json()
```

Response `200 OK`

```json
{
  "id": "b8f2d6a4-3c1e-4f7a-9d5b-2e6c8a0f4b13",
  "status": "succeeded",
  "verdict": "ai",
  "score": 88,
  "decided_by": "pixels",
  "lanes": {
    "pixels": {
      "verdict": "ai",
      "score": 88
    },
    "watermark": {
      "verdict": "none",
      "score": 4
    },
    "provenance": {
      "verdict": "nothing",
      "issuer": null
    }
  },
  "media": "video",
  "video": {
    "seconds": 12,
    "fps": 30,
    "frames": 360,
    "width": 1280,
    "height": 720,
    "audio": "copied",
    "readings": [
      {
        "t": 0.75,
        "verdict": "ai",
        "score": 84
      },
      {
        "t": 2.25,
        "verdict": "ai",
        "score": 86
      },
      {
        "t": 3.75,
        "verdict": "ai",
        "score": 88
      },
      {
        "t": 5.25,
        "verdict": "ai",
        "score": 91
      },
      {
        "t": 6.75,
        "verdict": "ai",
        "score": 87
      },
      {
        "t": 8.25,
        "verdict": "ai",
        "score": 90
      },
      {
        "t": 9.75,
        "verdict": "ai",
        "score": 89
      },
      {
        "t": 11.25,
        "verdict": "ai",
        "score": 85
      }
    ]
  }
}
```

A video is read on 8 frames, one at the middle of each of 8 equal slices of the video. Each frame is checked as an image is, both detectors and the watermark check, and the video's reading is the median of the frames'. One scene cut or black frame does not decide it.

`verdict`, `score`, `decided_by` and `lanes` mean what they mean for an image, as [Read the verdict](/docs/guides/check-an-image#read-the-verdict) describes. The provenance lane reads the video file's own C2PA manifest. `video` adds the file's facts and each frame's reading.

| Field            | Description                                                      |
| ---------------- | ---------------------------------------------------------------- |
| `video.seconds`  | The video's length in seconds.                                   |
| `video.fps`      | Its frame rate.                                                  |
| `video.frames`   | Its frame count.                                                 |
| `video.width`    | Its width in pixels, as it plays.                                |
| `video.height`   | Its height in pixels, as it plays.                               |
| `video.readings` | Each frame's time `t` in seconds, its `verdict` and its `score`. |

The frames of one video can read far apart, so branch on the video's `verdict`, not on one frame's.

## From an agent

The MCP tool `audit_video` takes the same fields and waits for the job, as [MCP tools](/docs/mcp/tools) describes. `get_processing_result` resumes it.

## Next steps

- [Credits and limits](/docs/credits-and-limits#video-limits): What a video costs, and its length, size and type limits.

- [Upload a local file](/docs/guides/upload-a-local-file): Send a video from your disk by its upload id.

- [Errors](/docs/errors): Every error code, including a video that runs too long.
