Autoblow API V1
Selecting A Cluster
Section titled “Selecting A Cluster”The Autoblow API is hosted on multiple clusters around the world.
When a device goes online it will connect to the lowest latency server.
To control the device you must connect to the same cluster as the device.
You can find the cluster your device is connected to by checking the cluster property in the /autoblow/connected response.
https://us-east-1.autoblowapi.comhttps://us-east-2.autoblowapi.comhttps://us-west-1.autoblowapi.comhttps://us-west-2.autoblowapi.comhttps://ca-central-1.autoblowapi.comhttps://ap-southeast-2.autoblowapi.comhttps://eu-west-2.autoblowapi.comhttps://eu-central-1.autoblowapi.comEvery request below takes the device token in the x-device-token header. Requests with a body use Content-Type: application/json; send requests without a body without a Content-Type header, because the API rejects an empty JSON body. Most commands respond with the device state; the ones that return something else say so. The examples use the eu-central-1 cluster and YOUR_DEVICE_TOKEN.
Basic Requests
Section titled “Basic Requests”GET /autoblow/connected
Section titled “GET /autoblow/connected”Check if a device is connected and get the cluster it is connected to.
This request can be performed on any cluster but for reliability purposes we recommend using https://latency.autoblowapi.com which will connect you to the cluster with the lowest latency.
All the other requests can only be performed on the cluster the device is connected to; on any other cluster they fail with 502.
deviceType is autoblow-ultra for the Ultra. For a vacuglide or vacupump use the Vacuglide API or the VacuPump API instead. A device that is offline returns { "connected": false }.
curl --request GET \ --url https://latency.autoblowapi.com/autoblow/connected \ --header 'x-device-token: YOUR_DEVICE_TOKEN'{ "connected": true, "cluster": "eu-central-1.autoblowapi.com", "deviceType": "autoblow-ultra" }GET /autoblow/info
Section titled “GET /autoblow/info”Get the firmware and hardware version and the device MAC. firmwareStatus is UP_TO_DATE, UPDATE_AVAILABLE or UPDATE_REQUIRED.
curl --request GET \ --url https://eu-central-1.autoblowapi.com/autoblow/info \ --header 'x-device-token: YOUR_DEVICE_TOKEN'{ "firmwareStatus": "UP_TO_DATE", "firmwareVersion": 1.01, "firmwareBranch": "prod", "hardwareVersion": "ultra", "mac": "aabbccddeeff", "deviceType": "autoblow-ultra"}GET /autoblow/state
Section titled “GET /autoblow/state”Get the current device state.
curl --request GET \ --url https://eu-central-1.autoblowapi.com/autoblow/state \ --header 'x-device-token: YOUR_DEVICE_TOKEN'Oscillate Commands
Section titled “Oscillate Commands”Positions and speeds are percentages from 0 to 100.
PUT /autoblow/oscillate
Section titled “PUT /autoblow/oscillate”Set the oscillator and start it: the stroker moves between minY and maxY at speed. minY and maxY must be at least 10 apart.
| name | required | type | description |
|---|---|---|---|
| speed (body) | yes | number | Oscillator speed (0 to 100) |
| minY (body) | yes | number | Oscillator low point (0 to 100) |
| maxY (body) | yes | number | Oscillator high point (0 to 100) |
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/oscillate \ --header 'Content-Type: application/json' \ --header 'x-device-token: YOUR_DEVICE_TOKEN' \ --data '{"speed": 50, "minY": 5, "maxY": 100}'PUT /autoblow/oscillate/start, /autoblow/oscillate/stop
Section titled “PUT /autoblow/oscillate/start, /autoblow/oscillate/stop”Start the oscillator with the last settings, or pause it (OSCILLATOR_PAUSED). No body.
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/oscillate/start \ --header 'x-device-token: YOUR_DEVICE_TOKEN'Sync Script Commands
Section titled “Sync Script Commands”Sync scripts are funscripts played in sync with a video. Upload a script or load one by token, call start with the video position whenever the video starts playing or the user seeks, and stop when it pauses.
Script formats
Section titled “Script formats”The uploads accept a funscript or a CSV file. The backend converts the script to the binary format the Ultra plays and stores it for 9 days under a new token, which you can later pass to load-token. Positions are percentages from 0 to 100 and times are milliseconds from the start of the video.
A funscript is JSON with an actions array:
{ "actions": [ { "at": 0, "pos": 0 }, { "at": 500, "pos": 100 }, { "at": 1000, "pos": 0 } ]}A CSV file has one time,position pair per line and no header. End the file with a newline; the last line is dropped otherwise.
0,0500,1001000,0Limits: at most 100,000 actions, and the last one at most 16,777,214 ms (about 4.6 hours).
PUT /autoblow/sync-script/upload-funscript, /autoblow/sync-script/upload-csv
Section titled “PUT /autoblow/sync-script/upload-funscript, /autoblow/sync-script/upload-csv”Upload a script file, store it and load it on the device. The body is multipart/form-data with the file in one part, up to 60 MB; curl’s --form sets the header. The device downloads the script before the request responds, so allow a request timeout of at least 60 seconds. The response is the device state: syncScriptToken holds the new token and operationalMode is SYNC_SCRIPT_PAUSED, as after load-token.
| name | required | type | description |
|---|---|---|---|
| file (multipart) | yes | file | The .funscript or .csv file |
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/sync-script/upload-funscript \ --header 'x-device-token: YOUR_DEVICE_TOKEN' \ --form 'file=@/path/to/script.funscript'PUT /autoblow/sync-script/upload-funscript-url, /autoblow/sync-script/upload-csv-url
Section titled “PUT /autoblow/sync-script/upload-funscript-url, /autoblow/sync-script/upload-csv-url”The same as the file uploads, but the API downloads the script from url. The URL must serve the file directly; redirects are not followed.
| name | required | type | description |
|---|---|---|---|
| url (body) | yes | string | http or https URL of the script file |
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/sync-script/upload-funscript-url \ --header 'Content-Type: application/json' \ --header 'x-device-token: YOUR_DEVICE_TOKEN' \ --data '{"url": "https://example.com/scripts/test.funscript"}'PUT /autoblow/sync-script/load-token
Section titled “PUT /autoblow/sync-script/load-token”Load a previously uploaded script by its token. The device downloads the script before responding, so allow a request timeout of at least 60 seconds. Loading never starts playback: whatever the device was playing, it switches to SYNC_SCRIPT_PAUSED and stops moving, and looping is turned off.
| name | required | type | description |
|---|---|---|---|
| scriptToken (body) | yes | string | Token of an uploaded script |
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/sync-script/load-token \ --header 'Content-Type: application/json' \ --header 'x-device-token: YOUR_DEVICE_TOKEN' \ --data '{"scriptToken": "7a93bfc5-4043-4ade-aee5-bec8012b0e12"}'PUT /autoblow/sync-script/start
Section titled “PUT /autoblow/sync-script/start”Start playing the loaded script from a video position, or seek while it plays. Without a loaded script it fails with 400 (SYNC_SCRIPT_NOT_LOADED).
| name | required | type | description |
|---|---|---|---|
| startTimeMs (body) | yes | number | Video position in ms |
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/sync-script/start \ --header 'Content-Type: application/json' \ --header 'x-device-token: YOUR_DEVICE_TOKEN' \ --data '{"startTimeMs": 24000}'PUT /autoblow/sync-script/stop
Section titled “PUT /autoblow/sync-script/stop”Pause playback (SYNC_SCRIPT_PAUSED). The script stays loaded, so a later start resumes or seeks without loading it again. No body.
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/sync-script/stop \ --header 'x-device-token: YOUR_DEVICE_TOKEN'PUT /autoblow/sync-script/offset
Section titled “PUT /autoblow/sync-script/offset”Compensate the latency between your player and the device. The offset stays set across script loads until the device restarts.
| name | required | type | description |
|---|---|---|---|
| offsetTimeMs (body) | yes | number | Offset in whole ms, positive or negative; positive means the device runs ahead of the video |
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/sync-script/offset \ --header 'Content-Type: application/json' \ --header 'x-device-token: YOUR_DEVICE_TOKEN' \ --data '{"offsetTimeMs": 100}'PUT /autoblow/sync-script/loop
Section titled “PUT /autoblow/sync-script/loop”Restart the script from the beginning when it ends. Without looping the device pauses at the end (SYNC_SCRIPT_PAUSED). Loading a script turns looping off, so send this after loading.
| name | required | type | description |
|---|---|---|---|
| loop (body) | yes | bool | Whether to restart the script at the end |
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/sync-script/loop \ --header 'Content-Type: application/json' \ --header 'x-device-token: YOUR_DEVICE_TOKEN' \ --data '{"loop": true}'Local Script Commands
Section titled “Local Script Commands”Local scripts are the stroke patterns stored on the device.
PUT /autoblow/local-script
Section titled “PUT /autoblow/local-script”Play a local script at a speed.
| name | required | type | description |
|---|---|---|---|
| localScriptIndex (body) | yes | number | Local script to play (0 to 9) |
| speedIndex (body) | yes | number | Speed level (0 to 9) |
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/local-script \ --header 'Content-Type: application/json' \ --header 'x-device-token: YOUR_DEVICE_TOKEN' \ --data '{"localScriptIndex": 5, "speedIndex": 2}'PUT /autoblow/local-script/start, /autoblow/local-script/stop
Section titled “PUT /autoblow/local-script/start, /autoblow/local-script/stop”stop pauses the playing local script (LOCAL_SCRIPT_PAUSED) and start resumes it. In any other mode they change nothing; use /autoblow/local-script to play a script. No body.
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/local-script/stop \ --header 'x-device-token: YOUR_DEVICE_TOKEN'Go To Commands
Section titled “Go To Commands”PUT /autoblow/goto
Section titled “PUT /autoblow/goto”Move the stroker to a position at a speed. The command is sent to the device without waiting for it, so the request responds right away with { "success": true }, which means the command was sent, not that the move finished. The device switches to GO_TO; poll /autoblow/state to follow it.
| name | required | type | description |
|---|---|---|---|
| position (body) | yes | number | Target position in percent (0 to 100) |
| speed (body) | yes | number | Speed in percent (0 to 100) |
curl --request PUT \ --url https://eu-central-1.autoblowapi.com/autoblow/goto \ --header 'Content-Type: application/json' \ --header 'x-device-token: YOUR_DEVICE_TOKEN' \ --data '{"position": 100, "speed": 10}'{ "success": true }Device Events
Section titled “Device Events”GET /events/stream?deviceToken=...
Section titled “GET /events/stream?deviceToken=...”Server-sent events for one device. Open the stream on the cluster the device is connected to; this request takes the token in the deviceToken query parameter instead of the header. The stream starts with a connected event and sends a heartbeat every 30 seconds.
While the Ultra is online its mode, pause and speed buttons do nothing on the device; each press is sent as an event instead, so your app decides what it does:
mode-button-pressedpause-button-pressedspeed-up-button-pressedspeed-down-button-pressed
| name | required | type | description |
|---|---|---|---|
| deviceToken (query) | yes | string | The device token |
curl --no-buffer \ --url 'https://eu-central-1.autoblowapi.com/events/stream?deviceToken=YOUR_DEVICE_TOKEN'event: connecteddata: {"connectionId":"bedffe18-f62e-4c9c-8e9b-7082f88d5f0c","subscribedDevice":"YOUR_DEVICE_TOKEN"}
event: mode-button-presseddata: {"deviceToken":"YOUR_DEVICE_TOKEN","payload":{"type":"mode-button-pressed","rawData":null,"args":[]},"timestamp":1752685746276}
event: heartbeatdata: {"timestamp":1752685776276}Device State
Section titled “Device State”| name | type | description |
|---|---|---|
| operationalMode | enum: POWERED_OFF, CALIBRATION, REST_AT_TOP, INIT, ONLINE_CONNECTED, SYNC_SCRIPT_PLAYING, SYNC_SCRIPT_PAUSED, LOCAL_SCRIPT_PLAYING, LOCAL_SCRIPT_PAUSED, OSCILLATOR_PLAYING, OSCILLATOR_PAUSED, GO_TO, SETUP, ERROR, LOADING, FIRMWARE_UPDATING, ERROR_MOTOR_STUCK, ERROR_CALIBRATION, ERROR_OVERHEATING | The current operational mode (see below) |
| localScript | number (0 to 9) | Selected local script |
| localScriptSpeed | number (0 to 9) | Local script speed level |
| motorTemperature | number | Motor temperature in °C |
| oscillatorTargetSpeed | number (0 to 100) | Oscillator speed in percent |
| oscillatorLowPoint | number (0 to 100) | Oscillator low point in percent (minY) |
| oscillatorHighPoint | number (0 to 100) | Oscillator high point in percent (maxY) |
| syncScriptCurrentTime | number | Current position of the sync script in ms |
| syncScriptOffsetTime | number | Applied sync offset in ms |
| syncScriptToken | string | Token of the loaded sync script; empty when none is loaded |
| syncScriptLoop | bool | Whether the sync script restarts when it ends |
| operationalMode | description |
|---|---|
| ONLINE_CONNECTED | Connected and idle, ready for commands |
| OSCILLATOR_PLAYING, OSCILLATOR_PAUSED | The oscillator is running or paused |
| SYNC_SCRIPT_PLAYING, SYNC_SCRIPT_PAUSED | The sync script is playing or paused |
| LOCAL_SCRIPT_PLAYING, LOCAL_SCRIPT_PAUSED | A local script is playing or paused |
| GO_TO | Moving after a goto command |
| FIRMWARE_UPDATING | Updating its firmware |
| ERROR_MOTOR_STUCK, ERROR_CALIBRATION, ERROR_OVERHEATING, ERROR | The motor is stuck, calibration failed, the motor overheated, or another error |
| POWERED_OFF, CALIBRATION, REST_AT_TOP, INIT, SETUP, LOADING | Start-up, Wi-Fi setup and connecting states before the device is online |
Example (a sync script playing from the start):
{ "operationalMode": "SYNC_SCRIPT_PLAYING", "localScript": 0, "localScriptSpeed": 0, "motorTemperature": 30, "oscillatorTargetSpeed": 50, "oscillatorLowPoint": 0, "oscillatorHighPoint": 100, "syncScriptCurrentTime": 15, "syncScriptOffsetTime": 0, "syncScriptToken": "7a93bfc5-4043-4ade-aee5-bec8012b0e12", "syncScriptLoop": false}Rate Limiting
Section titled “Rate Limiting”Requests are limited per device token. Each response carries x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset headers. A 429 response means the limit was hit; wait before retrying.