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

# Transcribe Online Video (Async)

> Queue a speech-to-text transcription and return a `task_id` immediately.

**Use this instead of `POST /transcribe` for anything longer than ~10 minutes.** A synchronous transcription holds the HTTP connection open for the whole download and speech-to-text pass; past roughly ten minutes of audio that starts failing against client timeouts, reverse proxies and marketplace gateways. This endpoint has no duration cap.

Accepts the same body as `POST /transcribe`, plus `webhook_url`.

**Billing** is identical to the synchronous endpoint and happens in the background worker. Because the `202` fires before any charge is made, `include_usage` is not accepted here — pass it on `GET /transcribe/{task_id}` instead, which replays the final charges once `task_status=completed`. A failed job has all of its charges reverted.

See https://docs.vidnavigator.com/guides/async-jobs

**Submit-time credit gate:** the submit endpoint returns `402` instead of a `task_id` when the account has less than 60 seconds of transcription credit left. This is a spam gate, not a quote — the real, duration-accurate charge happens when the job runs, and a job whose video exceeds the remaining balance still fails on the same credit check a synchronous call would hit. An invalid or deactivated API key is rejected with `401`, and a key without the endpoint's permission with `403`; no task is created in either case.

Transcribe an online video of **any length**. The job runs in the background: you get a `task_id` immediately, then collect the transcript by polling or through a [webhook](/webhooks).

<Info>
  Use this endpoint instead of [`POST /transcribe`](/api-reference/endpoint/transcribe) for media longer than **10 minutes**, which the synchronous endpoint rejects with `video_too_long`. It also works for short videos, so it is the safe default when you don't know the duration.
</Info>

## How It Works

<Steps>
  <Step title="Submit the job">
    `POST /transcribe/async` with the **same body** as [`POST /transcribe`](/api-reference/endpoint/transcribe), plus an optional `webhook_url`. You get `202 Accepted` with a `task_id` and a `check_status_url`.
  </Step>

  <Step title="Wait for the result">
    Poll `GET /transcribe/{task_id}` every few seconds while `task_status` is `processing` — polling is free — or receive a [webhook](/webhooks) when the job finishes.
  </Step>

  <Step title="Read the result">
    On `completed`, `data.result` is identical to the synchronous response's `data` block, so the same parsing code works for both. On `failed`, `data.error` carries the same error code the synchronous endpoint would have returned.
  </Step>
</Steps>

## Billing

Billed **exactly like** [`POST /transcribe`](/api-reference/endpoint/transcribe) — the async mode costs nothing extra. Charges are made in the background worker, so pass `include_usage=true` on the result request (not on the submit) to see them. A failed job has all of its charges reverted.

## Submit the job

`POST https://api.vidnavigator.com/v1/transcribe/async`

| Parameter         | Type    | Required | Description                                                                                                                                                                                                                                      |
| ----------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `video_url`       | string  | Yes      | URL of the video to transcribe. For Instagram carousel posts, append `?img_index=N` to select a specific video.                                                                                                                                  |
| `transcript_text` | boolean | No       | When `true`, returns the transcript as a single plain-text string.                                                                                                                                                                               |
| `all_videos`      | boolean | No       | For Instagram carousel posts only. When `true`, transcribes all videos in the post.                                                                                                                                                              |
| `webhook_url`     | string  | No       | Where to POST the result when the job finishes. Overrides the account default set in [Studio → API](https://vidnavigator.com/studio/api); pass `""` to opt this job out of the default. Must be a public `https` URL. See [Webhooks](/webhooks). |

<Note>
  `include_usage` is not accepted on the submit request — pass it on the poll request instead.
</Note>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.vidnavigator.com/v1/transcribe/async" \
    -H "X-API-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "video_url": "https://www.instagram.com/reel/C86ZvEaqRmo/",
      "webhook_url": "https://example.com/hooks/vidnavigator"
    }'
  ```

  ```python Python theme={null}
  import time
  import requests

  BASE = "https://api.vidnavigator.com"
  HEADERS = {"X-API-Key": "YOUR_API_KEY"}

  task = requests.post(
      f"{BASE}/v1/transcribe/async",
      headers=HEADERS,
      json={"video_url": "https://www.instagram.com/reel/C86ZvEaqRmo/"},
  ).json()["data"]

  while True:
      job = requests.get(BASE + task["check_status_url"], headers=HEADERS).json()["data"]
      if job["task_status"] != "processing":
          break
      time.sleep(5)

  if job["task_status"] == "completed":
      for segment in job["result"]["transcript"]:
          print(f'[{segment["start"]:.2f}s - {segment["end"]:.2f}s] {segment["text"]}')
  else:
      print("Failed:", job["error"]["error"], job["error"]["message"])
  ```

  ```javascript JavaScript theme={null}
  const BASE = 'https://api.vidnavigator.com';
  const HEADERS = { 'X-API-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' };

  const { data: task } = await (await fetch(`${BASE}/v1/transcribe/async`, {
    method: 'POST',
    headers: HEADERS,
    body: JSON.stringify({ video_url: 'https://www.instagram.com/reel/C86ZvEaqRmo/' })
  })).json();

  let job;
  do {
    await new Promise(r => setTimeout(r, 5000));
    ({ data: job } = await (await fetch(BASE + task.check_status_url, { headers: HEADERS })).json());
  } while (job.task_status === 'processing');

  if (job.task_status === 'completed') {
    job.result.transcript.forEach(s => console.log(`[${s.start}s] ${s.text}`));
  } else {
    console.error('Failed:', job.error.error, job.error.message);
  }
  ```
</CodeGroup>

### Response (202 Accepted)

```json theme={null}
{
  "status": "success",
  "data": {
    "task_id": "550e8400-e29b-41d4-a716-446655440000",
    "task_status": "processing",
    "job_type": "transcribe",
    "expires_at": "2026-09-23T13:00:00Z",
    "check_status_url": "/v1/transcribe/550e8400-e29b-41d4-a716-446655440000",
    "webhook_url": "https://example.com/hooks/vidnavigator",
    "message": "Transcription job accepted."
  }
}
```

| Status | `error`                                     | Description                                                            |
| ------ | ------------------------------------------- | ---------------------------------------------------------------------- |
| `400`  | `missing_parameter`, `invalid_parameter`, … | Invalid request.                                                       |
| `401`  | —                                           | Invalid or deactivated API key.                                        |
| `402`  | `limit_exceeded`                            | Less than 60 seconds of transcription credit left. No task is created. |
| `403`  | —                                           | The API key lacks permission for this endpoint.                        |
| `429`  | `too_many_active_jobs`                      | Too many jobs already running for this account.                        |

## Get the result

<Note>
  The playground above only covers the submit request. Call the result endpoint with cURL or your HTTP client.
</Note>

`GET https://api.vidnavigator.com/v1/transcribe/{task_id}`

| Parameter       | In    | Required | Description                                                                               |
| --------------- | ----- | -------- | ----------------------------------------------------------------------------------------- |
| `task_id`       | path  | Yes      | The `task_id` returned by `POST /transcribe/async`.                                       |
| `include_usage` | query | No       | When `true` and `task_status=completed`, attaches a `usage` block with the final charges. |

Polling is **free**. Poll every few seconds while `task_status` is `processing`. Results are kept for **1 hour after the job finishes**, and reading a task doesn't delete it.

```bash cURL theme={null}
curl "https://api.vidnavigator.com/v1/transcribe/550e8400-e29b-41d4-a716-446655440000?include_usage=true" \
  -H "X-API-Key: YOUR_API_KEY"
```

On `completed`, `data.result` is **identical** to the `data` block of the [synchronous response](/api-reference/endpoint/transcribe) (`video_info` + `transcript`, or `carousel_info` + `videos` when `all_videos=true`).

```json theme={null}
{
  "status": "success",
  "data": {
    "task_id": "550e8400-e29b-41d4-a716-446655440000",
    "task_status": "completed",
    "job_type": "transcribe",
    "request": { "video_url": "https://www.instagram.com/reel/C86ZvEaqRmo/", "transcript_text": false, "all_videos": false },
    "created_at": "2026-09-23T12:00:00Z",
    "started_at": "2026-09-23T12:00:01Z",
    "completed_at": "2026-09-23T12:00:40Z",
    "expires_at": "2026-09-23T13:00:40Z",
    "check_status_url": "/v1/transcribe/550e8400-e29b-41d4-a716-446655440000",
    "webhook": { "status": "delivered", "attempts": 1, "response_status": 200, "last_error": null, "delivered_at": "2026-09-23T12:00:41Z" },
    "result": {
      "video_info": { "title": "A Reel Interesting Video", "duration": 58.5, "url": "https://www.instagram.com/reel/C86ZvEaqRmo/" },
      "transcript": [
        { "text": "This is the first sentence of the video.", "start": 0.5, "end": 3.2 }
      ]
    },
    "error": null
  }
}
```

On `failed`, `data.result` is `null` and `data.error` carries the same code the synchronous endpoint would have returned, e.g. `{ "error": "transcript_not_available", "message": "...", "http_status": 404 }`. All charges are reverted.

| Status | `error`          | Description                                                                        |
| ------ | ---------------- | ---------------------------------------------------------------------------------- |
| `404`  | `task_not_found` | Unknown `task_id`, expired, owned by another account, or not a transcription task. |

## Webhook

Pass `webhook_url` on the submit request, or configure a default endpoint in [Studio → API](https://vidnavigator.com/studio/api), to be called back when the job finishes. See [Webhooks](/webhooks) for the payload and signature verification, and [Async Jobs](/guides/async-jobs) for the full workflow.


## OpenAPI

````yaml POST /transcribe/async
openapi: 3.0.3
info:
  title: VidNavigator Developer API
  description: >-
    The VidNavigator Developer API provides programmatic access to video
    analysis, transcription, and search capabilities.


    ## Authentication

    All endpoints require API key authentication via the `X-API-Key` header:

    ```

    X-API-Key: YOUR_API_KEY

    ```


    ## Rate Limits

    Check the documentation for the rate limits for each endpoint.


    ## Long videos and async jobs

    The speech-to-text endpoints (`/transcribe`, `/extract/video` with
    `transcribe=true`, `/tweet/statement`) run for as long as the media is long.
    A synchronous request holds the HTTP connection open for the whole download
    and transcription, which is reliable for short clips and not for long ones —
    clients, reverse proxies and marketplace gateways all time out.


    **For media longer than ~10 minutes, use the async twin of the endpoint:**


    | Synchronous | Async submit | Poll |

    |---|---|---|

    | `POST /transcribe` | `POST /transcribe/async` | `GET
    /transcribe/{task_id}` |

    | `POST /extract/video` | `POST /extract/video/async` | `GET
    /extract/video/{task_id}` |

    | `POST /tweet/statement` | `POST /tweet/statement/async` | `GET
    /tweet/statement/{task_id}` |


    The async endpoint returns `202` with a `task_id` immediately. Poll the
    `check_status_url` until `task_status` is `completed` or `failed`; results
    are retained for 1 hour after the job finishes. Polling is free.


    The synchronous endpoints enforce a **10-minute** limit, rejecting longer
    media with `video_too_long` (HTTP 400). The metadata fetch needed to read
    the video's duration is billed (`standard_request` or `residential_request`)
    and is **not** refunded: the limit is documented, so the proxy call was
    spent answering a request that could not be served. No `transcription_hour`
    is charged — no audio is processed.


    Past ten minutes a single HTTP connection is not a reliable way to deliver a
    transcription, so send long media to the async endpoints — they have no
    duration cap. See https://docs.vidnavigator.com/guides/async-jobs.


    ## Webhooks

    Rather than polling, an async job can call you back. Pass `webhook_url` on
    the submit request, or configure an account-level default in Studio → API;
    the per-request value wins, and passing `"webhook_url": ""` opts a single
    job out of the default.


    Deliveries are signed with HMAC-SHA256 in the `X-VidNavigator-Signature`
    header (`t=<unix_ts>,v1=<hex>`, computed over `"{t}.{raw_body}"` using your
    signing secret). Delivery is at-least-once and best effort — five attempts
    backing off over ~13 minutes — so make your receiver idempotent on
    `X-VidNavigator-Delivery` and treat polling as the source of truth.


    **Auto-disable:** an account-level default endpoint that fails 20 deliveries
    in a row over more than 72 hours is switched off automatically, and the
    owner is notified in Studio. Both conditions must hold, so a busy afternoon
    of failures will not disable a healthy endpoint and neither will two
    failures a week apart; a single successful delivery resets the run. While
    disabled, jobs still run and results are still available by polling — only
    the callback stops. Re-enable it in Studio → API once the receiver is fixed.
    A per-request `webhook_url` is never auto-disabled, because it is not
    stored. Full contract: https://docs.vidnavigator.com/guides/webhooks


    ## Error Handling

    The API uses standard HTTP status codes and returns error responses in JSON
    format:

    ```json

    {
      "status": "error",
      "error": "error_type",
      "message": "Human readable error message"
    }

    ```

    Actionable errors also carry a `docs_url`. Error reference:
    https://docs.vidnavigator.com/reference/errors
  version: 1.0.0
  contact:
    name: VidNavigator Support
    url: https://vidnavigator.com/support
    email: support@vidnavigator.com
  license:
    name: Proprietary
    url: https://vidnavigator.com/terms
servers:
  - url: https://api.vidnavigator.com/v1
    description: Production server
security:
  - ApiKeyAuth: []
tags:
  - name: Transcripts
    description: Extract transcripts from online videos
  - name: TikTok
    description: Scrape TikTok profile metadata and per-video stats
  - name: Files
    description: Manage uploaded audio/video files
  - name: Analysis
    description: AI-powered content analysis
  - name: Extraction
    description: >-
      Extract structured data from video and file transcripts using custom
      schemas
  - name: Namespaces
    description: Organize uploaded files into namespaces (folders)
  - name: Search
    description: Search videos and files using AI
  - name: System
    description: System health and information
  - name: Tweet Analysis
    description: Extract structured claims and metadata from X/Twitter tweets
  - name: Webhooks
    description: >-
      Callbacks for async jobs. Configure an account-level default endpoint and
      signing secret in Studio → API, or pass `webhook_url` per request. The
      `WebhookEvent` schema documents the payload and signature. Full contract:
      https://docs.vidnavigator.com/guides/webhooks A default endpoint that
      fails 20 deliveries in a row over more than 72 hours is disabled
      automatically and the owner notified; polling is unaffected.
paths:
  /transcribe/async:
    post:
      tags:
        - Transcripts
      summary: Transcribe a video asynchronously (long videos)
      description: >-
        Queue a speech-to-text transcription and return a `task_id` immediately.


        **Use this instead of `POST /transcribe` for anything longer than ~10
        minutes.** A synchronous transcription holds the HTTP connection open
        for the whole download and speech-to-text pass; past roughly ten minutes
        of audio that starts failing against client timeouts, reverse proxies
        and marketplace gateways. This endpoint has no duration cap.


        Accepts the same body as `POST /transcribe`, plus `webhook_url`.


        **Billing** is identical to the synchronous endpoint and happens in the
        background worker. Because the `202` fires before any charge is made,
        `include_usage` is not accepted here — pass it on `GET
        /transcribe/{task_id}` instead, which replays the final charges once
        `task_status=completed`. A failed job has all of its charges reverted.


        See https://docs.vidnavigator.com/guides/async-jobs


        **Submit-time credit gate:** the submit endpoint returns `402` instead
        of a `task_id` when the account has less than 60 seconds of
        transcription credit left. This is a spam gate, not a quote — the real,
        duration-accurate charge happens when the job runs, and a job whose
        video exceeds the remaining balance still fails on the same credit check
        a synchronous call would hit. An invalid or deactivated API key is
        rejected with `401`, and a key without the endpoint's permission with
        `403`; no task is created in either case.
      operationId: transcribeVideoAsync
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - video_url
              properties:
                video_url:
                  type: string
                  format: uri
                  description: >-
                    URL of the video to transcribe. For Instagram carousel
                    posts, append ?img_index=N to select a specific video.
                  example: https://www.youtube.com/watch?v=long-podcast
                transcript_text:
                  type: boolean
                  default: false
                  description: >-
                    When true, returns the transcript as a single plain-text
                    string instead of an array of segments.
                all_videos:
                  type: boolean
                  default: false
                  description: >-
                    For Instagram carousel posts only. When true, transcribes
                    ALL videos in the post.
                webhook_url:
                  type: string
                  format: uri
                  description: >-
                    Where to POST the result when the job finishes. Overrides
                    the account-level default configured in Studio → API. Pass
                    an empty string to opt this job out of that default. Must be
                    a publicly reachable https URL — private, loopback and
                    link-local hosts are rejected. See
                    https://docs.vidnavigator.com/guides/webhooks
                  example: https://example.com/hooks/vidnavigator
      responses:
        '202':
          description: Job accepted and started.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AsyncJobAccepted'
        '400':
          $ref: '#/components/responses/BadRequest'
        '402':
          description: >-
            Insufficient credits to queue the job. Returned when the account has
            less than 60 seconds of transcription credit remaining; no task is
            created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - error
                  error:
                    type: string
                    enum:
                      - limit_exceeded
                  error_code:
                    type: string
                    enum:
                      - limit_exceeded
                  message:
                    type: string
        '429':
          description: Too many jobs already running for this account.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - error
                  error:
                    type: string
                    enum:
                      - too_many_active_jobs
                  message:
                    type: string
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  schemas:
    AsyncJobAccepted:
      type: object
      description: 202 response from an async submit endpoint.
      properties:
        status:
          type: string
          enum:
            - success
        data:
          type: object
          properties:
            task_id:
              type: string
              description: Identifier to poll with.
              example: 550e8400-e29b-41d4-a716-446655440000
            task_status:
              type: string
              enum:
                - processing
            job_type:
              type: string
              enum:
                - transcribe
                - extract_video
                - tweet_statement
            expires_at:
              type: string
              format: date-time
              description: >-
                Results are deleted after this time. Until the job finishes this
                is a provisional submit-time bound; on completion it is pushed
                out to finish time + 1 hour.
            check_status_url:
              type: string
              description: Relative URL to poll for status and the result.
              example: /v1/transcribe/550e8400-e29b-41d4-a716-446655440000
            webhook_url:
              type: string
              nullable: true
              description: >-
                The URL this job's result will be POSTed to, or null if no
                webhook applies.
            message:
              type: string
            docs_url:
              type: string
  responses:
    BadRequest:
      description: >-
        Bad request - invalid parameters. On /transcribe the `error` field may
        be `missing_parameter`, `unsupported_platform`, `unsupported_url`,
        `invalid_url`, `request_body_required`, `video_too_long`, or
        `no_videos_found` (the post carries no video).
      content:
        application/json:
          schema:
            type: object
            properties:
              status:
                type: string
                enum:
                  - error
              error:
                type: string
              message:
                type: string
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            type: object
            properties:
              status:
                type: string
                enum:
                  - error
              error:
                type: string
                enum:
                  - metadata_fetch_failed
                  - audio_extraction_failed
                  - transcription_failed
                  - internal_server_error
              message:
                type: string
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        API key authentication. Include your VidNavigator API key in the
        X-API-Key header.

````