# Video upload

Upload a video and give it a start time, so it plays back in step with your [time-series data](https://dev.nominal.io/platform/data-model.md#data-model-time-series).

## In code

**Python**

```python
from datetime import datetime, timezone
from nominal.core import NominalClient

client = NominalClient.from_profile("default")
dataset = client.get_dataset("<ri.catalog.instance.dataset.identifier>")

file = dataset.add_video("video.mp4", channel="camera", start=datetime(2026, 1, 1, 14, 0, tzinfo=timezone.utc))
file.poll_until_ingestion_completed()  # raises NominalIngestError if the ingest fails
```

**LabVIEW**

![LabVIEW example that uploads a video starting at the current time, with labels and properties](https://dev.nominal.io/quickstart/_images/labview-quick-video.jpg)

**Rust**

```rust
use nominal::core::{VideoCreate, VideoIngest};

let client = nominal::NominalClient::from_profile("default")?;
let start: chrono::DateTime<chrono::Utc> = "2026-01-01T14:00:00Z".parse()?;
client.ingest()
    .upload_video("video.mp4", VideoCreate::new("Camera"), VideoIngest::starting_at(start))
    .await?;
```

**Go**

```go
import (
	"bytes"
	"os"

	ingestapi "github.com/nominal-io/nominal-api-go/ingest/api"
	nominalapi "github.com/nominal-io/nominal-api-go/io/nominal/api"
	videoapi "github.com/nominal-io/nominal-api-go/scout/video/api"
	uploadapi "github.com/nominal-io/nominal-api-go/upload/api"
	"github.com/palantir/conjure-go-runtime/v2/conjure-go-client/httpclient"
	"github.com/palantir/pkg/rid"
	"github.com/palantir/pkg/safelong"
)

// httpClient is a conjure-go-runtime httpclient.Client for your Nominal REST API URL,
// and token is your API key as a bearertoken.Token
data, _ := os.ReadFile("video.mp4")
size := safelong.SafeLong(len(data))
path, _ := uploadapi.NewUploadServiceClient(httpClient).UploadFile(ctx, token, "video.mp4", &size, nil,
	httpclient.RequestBodyInMemory(bytes.NewReader(data)))
datasetRid, _ := rid.ParseRID("<ri.catalog.instance.dataset.identifier>")
ingestapi.NewIngestServiceClient(httpClient).Ingest(ctx, token, ingestapi.IngestRequest{
	Options: ingestapi.NewIngestOptionsFromVideoV2(ingestapi.VideoOptsV2{
		Source:  ingestapi.NewIngestSourceFromS3(ingestapi.S3IngestSource{Path: string(path)}),
		Target:  ingestapi.NewDatasetIngestTargetFromExisting(ingestapi.ExistingDatasetIngestDestination{DatasetRid: datasetRid}),
		Channel: "camera",
		TimestampManifest: videoapi.NewVideoFileTimestampManifestFromNoManifest(videoapi.NoTimestampManifest{
			StartingTimestamp: nominalapi.Timestamp{Seconds: 1767276000}, // 2026-01-01T14:00:00Z
		}),
	}),
})
```

## In the Nominal app

1. Open the dataset’s page and click **UPLOAD DATA**. To find the dataset, in the sidebar click **Data**, then **Data sources**.
2. Drop the video onto **Upload files**, or click **SELECT FILES** and pick it.
3. In the video’s settings, choose or type a **Channel**, such as `camera`, and check the **Start time**. Nominal fills in the start time when the file records one.
4. Click **CONFIRM & INGEST**.
