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 methods | Overview |
---|---|
app.bsky.video.getUploadLimits | Get upload limit information |
app.bsky.video.uploadVideo | Video upload |
app.bsky.video.getJobStatus | Get 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)
- Get service authorization token
The request destination is the same endpoint as many other APIs.
API method | Parameter name | Parameter value | Notes |
---|---|---|---|
com.atproto.server.getServiceAuth | aud | 'did:web:video.bsky.app' | Identifier indicating video service |
exp | Authorization expiration date | UNIX time indicating expiration date 60 seconds after default | |
lxm | 'app.bsky.video.getUploadLimits' | Lexicon of API method for which authorization is to be acquired |
- Obtaining upload limit information
The request destination is a video service endpoint.
API method | Parameter name | Parameter value | Notes |
---|---|---|---|
app.bsky.video.getUploadLimits | Bearer | Acquired service authorization token | Setting in HTTP Authorization header |
Video upload (app.bsky.video.uploadVideo)
- Get service authorization token
The request destination is the same endpoint as many other APIs.
API method | Parameter name | Parameter value | Notes |
---|---|---|---|
com.atproto.server.getServiceAuth | aud | 'did:web:<service endpoint domain>' | Belonging PDS server DID *1 |
exp | Authorization expiration date | UNIX 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
- Video upload
The request destination is a video service endpoint.
API method | Parameter name | Parameter value | Notes |
---|---|---|---|
app.bsky.video.uploadVideo | Bearer | Acquired service authorization token | Setting in HTTP Authorization header |
did | User DID (uploading video) | ||
name | Video name | Video file name, etc. | |
BODY | Video data | HTTP POST body |
Get video upload job status (app.bsky.video.getJobStatus)
- Get service authorization token
The request destination is the same endpoint as many other APIs.
API method | Parameter name | Parameter value | Notes |
---|---|---|---|
com.atproto.server.getServiceAuth | aud | 'did:web:video.bsky.app' | Identifier indicating video service |
exp | Authorization expiration date | UNIX 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 |
- Get video upload job status
The request destination is a video service endpoint.
API method | Parameter name | Parameter value | Notes |
---|---|---|---|
app.bsky.video.getJobStatus | Bearer | Acquired service authorization token | Setting in HTTP Authorization header |
jobId | Value 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.state | Summary | Notes |
---|---|---|
JOB_STATE_COMPLETED | Job completed | jobStatus.blob stores the JSON object specified for video under $type:app.bsky.embed.video in the post (createRecord ) |
JOB_STATE_FAILED | Job failed | |
JOB_STATE_ENCODING | Encoding in progress | jobStatus.progress stores the progress percentage (%) |
JOB_STATE_SCANNING | Scanning in progress | This 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)
- Detailed version (
getUploadLimits
implemented, various supplementary explanations added)
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
- Samuel's post
- Implementation example: Happy path (direct upload)
- Implementation example: Sad path (indirect upload)
Yamarten's information
API reference
- app.bsky.video.getJobStatus
- app.bsky.video.getUploadLimits
- app.bsky.video.uploadVideo
- com.atproto.server.getServiceAuth
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
andgetJobStatus
- Added a supplementary note that the
getServiceAuth
parameter inuploadVIdeo
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