Skip to content
DikoAPI
API keys

Import a YouTube video into the library

Open as Markdown

Queues a YouTube video for transcription and adds it to your library.

POST/library/imports
Required scope
library:write
Authentication
Authorization: Bearer
Rate-limit policy
writeimportsaccount

The request never waits for the transcription: poll GET /library/imports/{import_id} (or subscribe to the library.import.* events) until status is succeeded, failed or canceled.

* A video already in your library, or already transcribed by Diko, answers 201 with reused: true and costs no credits. It is still an import: it counts toward the daily limit. * An import of the same video that is still running answers 200 with that import. * Otherwise 201 with a Location header. Credits are checked now and again before paid work; when too many imports are queued, or the daily limit is reached, the answer is 429 queue_limit_exceeded (error.details.limit says which limit). * A video without captions is transcribed by speech recognition in Diko's shared queue. Those jobs use the same per-user transcription slots as your own uploads in the app, but never the last free one (it stays available for your uploads and recordings), and may wait for a free slot; the import stays queued or processing meanwhile. * Revoking, disabling or deleting the API key that created an import, rotating it without an overlap period, or a revocation because of a leak cancels that key's imports that are still queued or running, and suspending API access does the same for the whole account: a queued import ends canceled at once, a running one becomes canceling and ends canceled when the work stops (event library.import.canceled). A rotation with a grace period leaves them running. Work already done is charged as usual.

Send an Idempotency-Key header to make retries safe.

Parameters

Parameters
NameInTypeRequiredDefaultConstraintsDescription
Idempotency-Keyheaderstringno–8–255 characters · pattern: ^[A-Za-z0-9_\-:.]{8,255}$Makes a retry safe: the same key with the same request returns the first answer (Idempotent-Replayed: true); a different request under the same key is rejected with 422 idempotency_key_reused. 8-255 characters from A-Z a-z 0-9 _ - : . Kept for 24 hours.

Request body

The video to import.

Content type: application/json

  • urlstringrequired

    A YouTube video link (youtube.com, www.youtube.com, m.youtube.com, music.youtube.com or youtu.be).

    1–2048 characters

  • languagestring | null

    Hint for the caption language and speech recognition. Omit to use your Diko default language.

    Allowed values: lten

Response

201

The import was created (queued), or the transcript already existed (succeeded with reused: true).

  • Idempotent-Replayed true when the response is a replay of an earlier request with the same Idempotency-Key.
  • Location URL of the created import.
  • RateLimit The most restrictive policy that applied: "name";r=<requests left>;t=<seconds>, where t is the number of seconds until one more request is allowed (0 when the quota is full). Absent when no rate limiter ran for the request.
  • RateLimit-Policy The rate-limit policies that applied to the request, one "name";q=<burst>;w=<seconds> item per policy, for example "read";q=10;w=2, "account";q=20;w=2: q is the burst quota and w the seconds in which an empty quota refills. Sent on every authenticated response; absent when no rate limiter ran for the request, for example when it was rejected before the key was checked.
  • Request-Id ID of this request (req_ prefix), the same as request_id in an error body. It is also sent as X-Request-ID. Quote it when you contact support.
  • X-Client-Request-Id Your own X-Client-Request-Id request header, echoed back. It is sent only when the value you sent has 1-128 characters from A-Z a-z 0-9 . _ : -.
  • X-RateLimit-Limit Burst quota of the most restrictive policy (q in RateLimit-Policy). Absent when no rate limiter ran.
  • X-RateLimit-Remaining Requests left in the most restrictive policy (r in RateLimit). Absent when no rate limiter ran.
  • X-RateLimit-Reset Seconds until one more request is allowed in the most restrictive policy (t in RateLimit; 0 when the quota is full). It is a number of seconds, not a timestamp. Absent when no rate limiter ran.

Content type: application/json

  • idstringrequired

    Public import ID (imp_ prefix).

    26–26 characters · pattern: ^imp_[0-9A-Za-z]{22}$

  • objectstringrequired

    Always library_import.

  • statusstringrequired

    Import status.

    Allowed values: queuedprocessingcancelingsucceededfailedcanceled

  • stagestring | nullrequired

    Current stage; only while processing.

    Allowed values: captionswaitingdownloadingtranscribingsummarizing

  • queue_positioninteger | nullrequired

    Position among your queued imports (1 = next); null when the import is not queued.

    1–2147483647 · format: int32

  • urlstringrequired

    Canonical YouTube URL.

    format: uri

  • youtube_video_idstringrequired

    YouTube video ID.

  • languagestring | nullrequired

    Language hint that was sent, or null.

    Allowed values: lten

  • reusedbooleanrequired

    True when the video was already in your library or in Diko's shared cache, so no credits were used.

  • transcript_idstring | nullrequired

    ID (trn_ prefix) of the library transcript once the import has succeeded; otherwise null.

    6–64 characters · pattern: ^trn_[A-Za-z0-9_-]+$

  • errorobject | nullrequired

    Error details when the import failed; otherwise null.

    2 fields
    • codestringrequired

      Stable error code. Unknown internal codes are reported as internal_error.

      Allowed values: video_unavailablevideo_privateage_restrictedvideo_too_longvideo_duration_unknowncaptions_unavailabledownload_failedtranscription_failedtranscription_timeoutprovider_limitinsufficient_creditsinternal_error

    • messagestringrequired

      Explanation in the language of Accept-Language.

  • created_atstringrequired

    When the import was created.

    ≤ 24 characters · format: date-time

  • updated_atstringrequired

    When the import last changed.

    ≤ 24 characters · format: date-time

  • completed_atstring | nullrequired

    When the import reached a final status; otherwise null.

    ≤ 24 characters · format: date-time

200

An import of this video is already queued or running; that import is returned.

  • Content-Location URL of the existing import that this answer describes.
  • Idempotent-Replayed true when the response is a replay of an earlier request with the same Idempotency-Key.
  • RateLimit The most restrictive policy that applied: "name";r=<requests left>;t=<seconds>, where t is the number of seconds until one more request is allowed (0 when the quota is full). Absent when no rate limiter ran for the request.
  • RateLimit-Policy The rate-limit policies that applied to the request, one "name";q=<burst>;w=<seconds> item per policy, for example "read";q=10;w=2, "account";q=20;w=2: q is the burst quota and w the seconds in which an empty quota refills. Sent on every authenticated response; absent when no rate limiter ran for the request, for example when it was rejected before the key was checked.
  • Request-Id ID of this request (req_ prefix), the same as request_id in an error body. It is also sent as X-Request-ID. Quote it when you contact support.
  • X-Client-Request-Id Your own X-Client-Request-Id request header, echoed back. It is sent only when the value you sent has 1-128 characters from A-Z a-z 0-9 . _ : -.
  • X-RateLimit-Limit Burst quota of the most restrictive policy (q in RateLimit-Policy). Absent when no rate limiter ran.
  • X-RateLimit-Remaining Requests left in the most restrictive policy (r in RateLimit). Absent when no rate limiter ran.
  • X-RateLimit-Reset Seconds until one more request is allowed in the most restrictive policy (t in RateLimit; 0 when the quota is full). It is a number of seconds, not a timestamp. Absent when no rate limiter ran.

Content type: application/json

  • idstringrequired

    Public import ID (imp_ prefix).

    26–26 characters · pattern: ^imp_[0-9A-Za-z]{22}$

  • objectstringrequired

    Always library_import.

  • statusstringrequired

    Import status.

    Allowed values: queuedprocessingcancelingsucceededfailedcanceled

  • stagestring | nullrequired

    Current stage; only while processing.

    Allowed values: captionswaitingdownloadingtranscribingsummarizing

  • queue_positioninteger | nullrequired

    Position among your queued imports (1 = next); null when the import is not queued.

    1–2147483647 · format: int32

  • urlstringrequired

    Canonical YouTube URL.

    format: uri

  • youtube_video_idstringrequired

    YouTube video ID.

  • languagestring | nullrequired

    Language hint that was sent, or null.

    Allowed values: lten

  • reusedbooleanrequired

    True when the video was already in your library or in Diko's shared cache, so no credits were used.

  • transcript_idstring | nullrequired

    ID (trn_ prefix) of the library transcript once the import has succeeded; otherwise null.

    6–64 characters · pattern: ^trn_[A-Za-z0-9_-]+$

  • errorobject | nullrequired

    Error details when the import failed; otherwise null.

    2 fields
    • codestringrequired

      Stable error code. Unknown internal codes are reported as internal_error.

      Allowed values: video_unavailablevideo_privateage_restrictedvideo_too_longvideo_duration_unknowncaptions_unavailabledownload_failedtranscription_failedtranscription_timeoutprovider_limitinsufficient_creditsinternal_error

    • messagestringrequired

      Explanation in the language of Accept-Language.

  • created_atstringrequired

    When the import was created.

    ≤ 24 characters · format: date-time

  • updated_atstringrequired

    When the import last changed.

    ≤ 24 characters · format: date-time

  • completed_atstring | nullrequired

    When the import reached a final status; otherwise null.

    ≤ 24 characters · format: date-time

Examples

Request

cURL
curl -X POST "https://api.diko.app/api/v1/library/imports" \
  -H "Authorization: Bearer $DIKO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","language":"lt"}'

Example response · 201 · application/json

JSON
{
  "id": "imp_6Mfj7Oxm4MfxGGmZRhiUQf",
  "object": "library_import",
  "status": "queued",
  "stage": null,
  "queue_position": 1,
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "youtube_video_id": "dQw4w9WgXcQ",
  "language": "lt",
  "reused": false,
  "transcript_id": null,
  "error": null,
  "created_at": "2026-10-11T09:30:00.000Z",
  "updated_at": "2026-10-11T09:30:00.100Z",
  "completed_at": null
}

Example response · 200 · application/json

JSON
{
  "id": "imp_6Mfj7Oxm4MfxGGmZRhiUQf",
  "object": "library_import",
  "status": "queued",
  "stage": null,
  "queue_position": 1,
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "youtube_video_id": "dQw4w9WgXcQ",
  "language": "lt",
  "reused": false,
  "transcript_id": null,
  "error": null,
  "created_at": "2026-10-11T09:30:00.000Z",
  "updated_at": "2026-10-11T09:30:00.100Z",
  "completed_at": null
}

Errors

This endpoint can return these errors. Every code is described in the error catalog.

HTTPCodeMeaningRetry?
400invalid_requestThe request is malformed: the body is not valid JSON, is missing, is not a JSON object, or `Content-Length` is not a number. A missing required field or an unknown field is `invalid_parameter` instead.no
400invalid_parameterA path, query or body parameter has an invalid value, a required field is missing or a field is unknown (unknown fields are rejected). `error.param` names the parameter.no
400invalid_youtube_urlThe URL is not a supported address of a single YouTube video.no
400idempotency_key_invalidThe `Idempotency-Key` is invalid: it needs 8 to 255 characters from `A-Z a-z 0-9 _ - : .`.no
401missing_api_keyThe request has no `Authorization` header.no
401invalid_api_keyThe header is unusable (a scheme other than `Bearer`, an empty key or more than one `Authorization` header) or the key is unknown or malformed (wrong structure or checksum). The response is identical in all these cases.no
401api_key_expiredThe key has expired.no
401api_key_revokedThe key was revoked: manually, after a rotation grace period ended, because of a leak or because the account was disabled.no
401api_key_disabledThe key is temporarily disabled.no
402subscription_requiredThe account has no active paid access.no
402insufficient_creditsThere are not enough credits for the import.no
402team_member_limit_reachedYour credit limit in the team has been reached.no
403ip_not_allowedThe key has an IP allowlist and the request IP is not on it, or it cannot be determined reliably.no
403account_disabledThe account is disabled.no
403email_not_verifiedThe account e-mail address is not verified.no
403api_access_suspendedA Diko administrator suspended API access for this account.no
403plan_requiredThe plan does not include the API. `error.details.required_plans` lists `verslas`.no
403insufficient_scopeThe key lacks the scope this endpoint needs. `error.details` contains `required_scopes` and `granted_scopes`.no
403team_member_disabledYour team membership is disabled.no
408request_timeoutThe request body was not received in time (10 s by default). This happens when a client sends the body too slowly or stalls while sending. Nothing was created.yes
409idempotency_in_progressThe same request with the same `Idempotency-Key` is still being processed.yes, after Retry-After
413request_too_largeThe request body is larger than 64 KB.no
415unsupported_media_typeA POST body must be `application/json`.no
422idempotency_key_reusedThe same `Idempotency-Key` was used with a different method, path or body.no
429queue_limit_exceededThe import capacity is reached: up to 10 active per user, 200 overall or 500 per day.yes, after Retry-After
429rate_limit_exceededA rate-limit policy was exceeded. `error.details.policy` names it.yes, after Retry-After
500internal_errorAn internal server error.conditionally
503credit_balance_unavailableThe credit balance could not be determined right now, so the import was not started.yes, after Retry-After
503writes_disabledWrite operations are temporarily switched off on the platform side. Reading still works.yes, after Retry-After
503service_unavailableThe service is temporarily busy, for example because of database load.yes, after Retry-After

Search for a topic, an endpoint or an error code.

Search across all documentation pagesesc close