Video upload with Bluesky API (XRPC)

@bills-appworks.blue

This is information on how to upload videos using the Bluesky API (XRPC). The information is current as of February 2025.

Most of the content is based on the information provided by Sugyan, Samuel and Yamarten, who are listed in the reference information. I would like to thank Sugyan, Samuel, and Yamarten. However, I am responsible for editing this article.

Overview

Use the APIs (XRPC) defined in app.bsky.video.* related to video services.

API methodsOverview
app.bsky.video.getUploadLimitsGet upload limit information
app.bsky.video.uploadVideoVideo upload
app.bsky.video.getJobStatusGet video upload job status

Unlike many other APIs, requests for video service-related APIs are made to the video service endpoint (https://video.bsky.app/xrpc/ for those provided by the official service).

Also, with many APIs, you specify a session JWT using com.atproto.server.createSession as the authorization token when making a request. However, when making a request to a video service, you must specify an authorization token acquired using com.atproto.server.getServiceAuth.

With the exception of uploadVideo, it is also possible to make requests via PDS using atproto-proxy (not explained in this article).

The usage of these is explained below. If necessary, you may find it to recognition helpful to refer to the process flow diagram in the latter half of this document.

API calls and parameters

Get upload limit information (app.bsky.video.getUploadLimits)

  1. Get service authorization token

The request destination is the same endpoint as many other APIs.

API methodParameter nameParameter valueNotes
com.atproto.server.getServiceAuthaud'did:web:video.bsky.app'Identifier indicating video service
expAuthorization expiration dateUNIX time indicating expiration date
60 seconds after default
lxm'app.bsky.video.getUploadLimits'Lexicon of API method for which authorization is to be acquired
  1. Obtaining upload limit information

The request destination is a video service endpoint.

API methodParameter nameParameter valueNotes
app.bsky.video.getUploadLimitsBearerAcquired service authorization tokenSetting in HTTP Authorization header

Video upload (app.bsky.video.uploadVideo)

  1. Get service authorization token

The request destination is the same endpoint as many other APIs.

API methodParameter nameParameter valueNotes
com.atproto.server.getServiceAuthaud'did:web:<service endpoint domain>'Belonging PDS server DID *1
expAuthorization expiration dateUNIX time indicating expiration date
60 seconds after if omitted
lxm'com.atproto.repo.uploadBlob'Lexicon of API method for which authorization is to be acquired *2

*1: The PDS server to which the user (who uploads the video) belongs, included in the response (service[].serviceEndpoint) to the HTTP request to https://plc.directory/<user DID> URL domain part

*2: Note that this is not app.bsky.video.uploadVideo

  1. Video upload

The request destination is a video service endpoint.

API methodParameter nameParameter valueNotes
app.bsky.video.uploadVideoBearerAcquired service authorization tokenSetting in HTTP Authorization header
didUser DID (uploading video)
nameVideo nameVideo file name, etc.
BODYVideo dataHTTP POST body

Get video upload job status (app.bsky.video.getJobStatus)

  1. Get service authorization token

The request destination is the same endpoint as many other APIs.

API methodParameter nameParameter valueNotes
com.atproto.server.getServiceAuthaud'did:web:video.bsky.app'Identifier indicating video service
expAuthorization expiration dateUNIX time indicating expiration date
60 seconds after if omitted
lxm'app.bsky.video.getJobStatus'Lexicon of API method for which authorization is to be obtained
  1. Get video upload job status

The request destination is a video service endpoint.

API methodParameter nameParameter valueNotes
app.bsky.video.getJobStatusBearerAcquired service authorization tokenSetting in HTTP Authorization header
jobIdValue of jobStats.jobId included in uploadVideo response

Details of each process

Details: Get upload limit information (app.bsky.video.getUploadLimits)

This process is possible to reduce server load and improve user experience, but it is not required for uploading.

First, as mentioned above, call getServiceAuth to acquire an authorization token. When calling getUploadLimits, specify that authorization token as the Bearer token in the HTTP Authorization header.

The response includes whether uploading is possible, the remaining upload size per day, the remaining number of uploads per day, etc., so use them as a reference.

Response example:

{
  "canUpload":true,
  "message":"User can upload",
  "remainingDailyBytes":40000000000,
  "remainingDailyVideos":100
}

If you request getUploadLimits via PDS using atproto-proxy, there is no need to call getServiceAuth.

Details: Video upload (app.bsky.video.uploadVideo)

First, as mentioned above, call getServiceAuth to acquire an authorization token. When calling uploadVideo, specify that authorization token as the Bearer token in the HTTP Authorization header.

Note that the service DID specified as the aud parameter when calling getServiceAuth is not 'did:web:video.bsky.app', but the DID of the PDS to which the user uploading the video belongs. The user's PDS URL is obtained as a response (service[].serviceEndpoint) to a request to https://plc.directory/<user's DID>. The service DID specified in the aud parameter is constructed by extracting the domain from the PDS URL and adding did:web: as a prefix (did:web:<PDS domain>).

Example:
(PDS URL)
https://verpa.us-west.host.bsky.network
(Service DID)
did:web:verpa.us-west.host.bsky.network

Also, note that the XRPC lexicon string specified as the lxm parameter is 'com.atproto.repo.uploadBlob', not 'app.bsky.video.uploadVideo'.

This is because the video service (video.bsky.app) that receives the uploadVideo request uploads the video to the PDS (uploadBlob). You can see the background of this process in the process flow diagram below.

The app.bsky.video.uploadVideo API reference does not specify any query parameters, but in fact, as mentioned above, the did and name query parameters must be specified. For did, specify the DID of the user uploading the video, and for name, specify the name of the video (the file name will do).

The API reference also lists JOB_STATE_COMPLETED and JOB_STATE_FAILED as possible values ​​for the jobStatus.state in the response, but in most normal cases it seems to be JOB_STATE_CREATED. In this case, you will need to wait for the processing to complete in the subsequent app.bsky.video.getJobStatus to obtain the value of jobStatus.blob.

If you upload the same video data, the response HTTP status code will be 409 (Conflict). In that case, the response payload's jobStatus.state will be JOB_STATE_COMPLETED, jobStatus.error will be "already_exists", and jobStatus.message will be "Video already processed". In this case, the jobId is still in jobStatus.jobId, and you can obtain the value of jobStatus.blob by specifying it in app.bsky.video.getJobStatus and calling it.

Unlike getUploadLimits and getJobStatus, uploadVideo cannot make a request via PDS using atproto-proxy.

Details: Get video upload job status (app.bsky.video.getJobStatus)

First, as mentioned above, call getServiceAuth to acquire an authorization token. When calling getJobStatus, specify that authorization token as the Bearer token in the HTTP Authorization header.

app.bsky.video.getJobStatus API Reference lists JOB_STATE_COMPLETED and JOB_STATE_FAILED as possible values ​​for jobStatus.state in the response, but there seem to be variations such as the following:

jobStatus.stateSummaryNotes
JOB_STATE_COMPLETEDJob completedjobStatus.blob stores the JSON object specified for video under $type:app.bsky.embed.video in the post (createRecord)
JOB_STATE_FAILEDJob failed
JOB_STATE_ENCODINGEncoding in progressjobStatus.progress stores the progress percentage (%)
JOB_STATE_SCANNINGScanning in progressThis state will be entered after the encoding process is completed, and the process will be completed after the scanning process

After uploadVideo, getJobStatus will be called at an appropriate frequency to wait for the process to complete.

The authorization token specified in getJobStatus depends on the authorization expiration date specified in getServiceAuth. Therefore, if you continue to use the same authorization token, the getJobStatus call will (probably) fail if the job exceeds the authorization expiration date. For this reason, you may want to extend the authorization expiration date or re-acquire the authorization token appropriately.

If you request getJobStatus via PDS using atproto-proxy, there is no need to call getServiceAuth.

Processing flow diagram

  • Simple version (getUploadLimits omitted)

image

  • Detailed version (getUploadLimits implemented, various supplementary explanations added)

image

Methods that do not use video services (app.bsky.video.*)

According to Samuel's information, which is linked from Sugyan's explanation, in addition to the methods using video services described above, there is also a method to upload using com.atproto.repo.uploadBlob.

However, while the method using getJobStatus allows you to control asynchronous processing, the method using uploadBlob is not recommended because it creates an uncontrollable time lag between the post processing.

Reference: aspectRatio when posting

In post (createRecord), there is an aspect ratio (aspectRatio) element under $type:app.bsky.embed.video. This element is optional, but depending on the client, for example, with vertical videos, black bars may be displayed on both sides, and the appearance may not be optimized.

Reference information

Sugyan's information

Samuel's information

Yamarten's information

API reference

lexicon


Change history

(2025-02-17)

  • Create new

(2025-02-21)

  • Added the path in the plc.directory response for the PDS URL
  • Added that the destination of requests for video service-related APIs is the video service endpoint (officially https://video.bsky.app/xrpc/<API name>), unlike many other APIs
  • Added that requests via PDS using atproto-proxy are also possible in the details of getUploadLimits and getJobStatus
  • Added a supplementary note that the getServiceAuth parameter in uploadVIdeo is different from other video service-related APIs
  • Added that requests via PDS using atproto-proxy are not possible in the details of uploadVideo
  • Added a processing flow diagram
  • Added information about Yamarten
  • Fixed other typos
bills-appworks.blue
びるず

@bills-appworks.blue

https://bills-appworks.net/
シェルスクリプト実装版Bluesky CLIクライアント bsky-sh-cli (Bluesky in the shell)を開発しています: https://github.com/bills-appworks/bsky-sh-cli
bsky-sh-cli広報アカウント: @bsky-sh-cli.bills-appworks.net
Linkat: https://linkat.blue/bills-appworks.blue

Post reaction in Bluesky

*To be shown as a reaction, include article link in the post or add link card

Reactions from everyone (0)