# Importing videos

The import API pulls a video from a URL you control — an S3 object, a signed link, any
direct HTTPS file — into your Livepush video library. It runs on its own host:
`https://vod-web-import-api.livepush.io`.

Imports are a two-step flow. You cannot skip the first step.

## 1. Inspect the source

Inspection reads the remote file's headers and container metadata and returns an `id`
that represents the inspected source.

```bash
curl -X POST 'https://vod-web-import-api.livepush.io/inspector/get_metadata' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"url": "https://example-bucket.s3.amazonaws.com/keynote.mp4"}'
```

The response reports duration, size, container, and the video and audio codecs. Check
these before transferring — a file the inspector cannot read is a file the import will
fail on, and you find out in milliseconds instead of after a multi-gigabyte transfer.

## 2. Start the transfer

Pass the `id` from step 1 along with the zone the video should land in and the name it
should carry in the library.

```bash
curl -X POST 'https://vod-web-import-api.livepush.io/transfers/new' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "meta_xxxxxxxxxxxx",
    "zone": "eu-central",
    "videoName": "Keynote 2026"
  }'
```

The call returns immediately. The transfer itself is asynchronous.

## 3. Poll until it disappears

```bash
curl -X GET 'https://vod-web-import-api.livepush.io/transfers/queued' \
  -H 'Authorization: Bearer YOUR_API_TOKEN'
```

This returns imports that are queued or processing, each with a `progress` value and a
`status`. **Completion is signalled by absence**: when your video ID is no longer in the
list, the import has finished and the asset is in the VOD library, where
[Get user videos](/videos/get-user-videos/) will return it.

Poll on a sensible interval — every 5 to 10 seconds is plenty for a transfer measured in
minutes.

```javascript
async function waitForImport(videoId, { intervalMs = 8000, timeoutMs = 3_600_000 } = {}) {
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    const queued = await getQueuedTransfers();
    const entry = queued.find((item) => item.id === videoId);

    if (!entry) return 'complete';
    if (entry.status === 'error') throw new Error(`Import failed for ${videoId}`);

    await new Promise((resolve) => setTimeout(resolve, intervalMs));
  }

  throw new Error(`Import timed out for ${videoId}`);
}
```

Guard the loop with a deadline. Absence means success, so a bug that drops the entry for
any other reason would otherwise leave you polling forever.

## Using the imported video

Once the video is in the library it behaves like any other asset — attach it to a
[pre-recorded stream playlist](/guides/pre-recorded-streams/) and schedule it.
