Docs
Every computer is a virtual machine running Ubuntu 24.04 with root access and the internet, and can come with a desktop for computer use. Your agent creates one, runs commands, moves files, and deletes it when it is done. Everything goes through one REST API at https://disposable.computer/api/v1, or the MCP server built on it.
Quickstart
Create an API key in the console and add funds. Then create a computer:
curl https://disposable.computer/api/v1/computers \
-H "Authorization: Bearer $DC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"size": "medium", "ttl_seconds": 3600}'It comes back with "status": "provisioning". Check on it with a GET until it says running, then run a command:
curl https://disposable.computer/api/v1/computers/$COMPUTER_ID/exec \
-H "Authorization: Bearer $DC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command": "uname -a"}'Delete it when you are done. Billing stops right away.
curl -X DELETE https://disposable.computer/api/v1/computers/$COMPUTER_ID \
-H "Authorization: Bearer $DC_API_KEY"Authentication
Send your API key as a bearer token on every request: Authorization: Bearer dc_.... Keys start with dc_ and are shown once when you create them. Revoke a key in the console and it stops working right away. Bodies are JSON with Content-Type: application/json.
Your account
GET/api/v1/account
Returns { "email", "first_name", "last_name", "balance", "email_low_balance", "email_timers" }, the names null until set, the balance in US dollars, and whether we email you when the balance runs low and about computer timers.
Account changes work only from the console, so an API key gets 403 forbidden: PATCH /api/v1/account with any of first_name and last_name (up to 50 characters each, or null to clear), email_low_balance and email_timers (true or false) returns the account, GET /api/v1/account/sessions lists every browser signed in, each with id, browser, device (phone, tablet, computer or null), place (the country, or null), created_at, last_seen_at and current for the one asking, DELETE /api/v1/account/sessions/:id signs out one and returns { "signed_out": true }, DELETE /api/v1/account/sessions signs out every other browser and returns { "signed_out" } with how many, and changing the sign-in email takes two steps: POST /api/v1/account/email with email sends a 6-digit code to the current address and another to the new one, and returns { "email", "code_length" } (409 email_taken if another account uses it), then POST /api/v1/account/email/confirm with current_code and new_code makes the switch, signs out every other browser, tells the old address and returns { "email" }. Deleting the account works the same way: POST /api/v1/account/delete emails a code to the account and returns { "email" }, and POST /api/v1/account/delete/confirm with that code deletes every computer, revokes every key, signs out every browser, closes the account and returns { "deleted": true }; any balance left is forfeited. Codes expire after 15 minutes and allow 5 wrong tries, then code_used_up. Up to 5 codes per account every 15 minutes, 5 an hour to the same new address, and 10 tries per 15 minutes or 20 a day.
Computers
A computer looks like this:
{
"id": "7f3c2a9e-1b4d-4c8e-9a51-0d2e6f8b4c11",
"name": "computer-4f2a1c",
"status": "running",
"image": "ubuntu-24.04",
"vcpus": 2,
"memory_gb": 4,
"disk_gb": 40,
"hourly_rate": 0.2,
"stopped_hourly_rate": 0.02,
"cost": 0.01,
"created_at": "2026-11-13T12:00:00.000Z",
"ready_at": "2026-11-13T12:00:31.000Z",
"status_changed_at": "2026-11-13T12:00:31.000Z",
"expires_at": "2026-11-13T13:00:00.000Z",
"error": null,
"source_id": null
}status is one of provisioning, starting, running, stopping, stopped, resizing, deleting, deleted or failed. status_changed_at is when the computer reached that status, or null while it has yet to act on a create, start, stop or delete you just asked for. expires_at is when it deletes itself, or null if it never does. source_id is the computer it was duplicated from, or null. hourly_rate is what it costs an hour while it runs and stopped_hourly_rate while it is stopped, when only its disk is billed. Both and cost are in US dollars, counted to the minute.
Create a computer
POST/api/v1/computers
imagestringubuntu-24.04 by default. ubuntu-24.04-desktop adds a desktop your agent can see and control, and ubuntu-24.04-coding comes with coding agents installed.
sizestringsmall (1 vCPU, 2 GB, 20 GB), medium (2 vCPU, 4 GB, 40 GB), large (4 vCPU, 8 GB, 80 GB). Default small, or medium for a desktop.
vcpusinteger1 to 4. Overrides the size.
memory_gbinteger1 to 8. Overrides the size.
disk_gbinteger10 to 100. Overrides the size.
ttl_secondsintegerDelete it this many seconds from now, from 60 up to 30 days. 0 keeps it until you delete it. Default 3600. For a computer set to last two days or more, we email you a day before it deletes and when it does, unless you turn that off in Settings.
namestringUp to 63 letters, numbers, spaces, dots, dashes or underscores, starting with a letter or number. Default computer- and six random characters.
Returns the computer with status 201. It starts right away.
List computers
GET/api/v1/computers
Returns { "computers": [...] }, the newest 100 first, without deleted ones.
Get a computer
GET/api/v1/computers/:id
Update a computer
PATCH/api/v1/computers/:id
Send any of name, ttl_seconds (counted from now, 0 for never), size, vcpus, memory_gb and disk_gb. Changing the size restarts a running computer. Disks can grow but never shrink.
Check capacity
GET/api/v1/capacity
Room for new computers is limited. This returns available (whether the smallest computer fits right now), online (false while no new computers can start at all) and the free vcpus, memory_gb, disk_gb and computers. When something doesn't fit, creating or starting it fails with no_capacity; space frees up as other computers are deleted.
Stop and start
POST/api/v1/computers/:id/stop
POST/api/v1/computers/:id/start
A stopped computer keeps its disk and only pays for that. Starting it boots it again with everything still on disk. Both return the computer.
Duplicate a computer
POST/api/v1/computers/:id/duplicate
Creates a new computer with a full copy of this one's disk, at the same image and size, and returns it with status 201 and source_id set to the original. Send name (default the original's name with -copy) and ttl_seconds (default 3600), both optional; the time spent copying doesn't count toward the timer. The original must be running or stopped with no start, stop or resize in progress. If it is running, the copy gets its disk as it is when copying finishes, as if the power was cut, so running programs don't carry over: stop it first for a clean copy. Copying takes longer the more is on the disk, each account copies one disk at a time, and a copy can wait its turn while others copy. While the disk copies, the original's start, stop, resize and delete wait until it is done, and a running original pauses for a moment at the end. Growing the original's disk before copying begins fails the copy, and so does stopping or deleting the copy before its disk is copied. A copy that fails is refunded in full. The copy counts against your limits and bills like any new computer.
Delete a computer
DELETE/api/v1/computers/:id
Billing stops right away. It shuts down, its disk is erased, and it comes back with status deleting. This can't be undone.
Commands
POST/api/v1/computers/:id/exec
Runs a command with bash on a running computer and waits for it to finish: as root, or as ubuntu on a coding computer.
commandstringRequired. Up to 65536 characters. Runs as bash -lc, so pipes, && and globs work.
timeoutintegerSeconds before it is stopped, 1 to 90. Default 30.
cwdstringAbsolute working directory. Default /root, or /home/ubuntu/workspace on a coding computer.
envobjectExtra environment variables, up to 64. Names are letters, digits and underscores; values up to 32768 characters.
run_idstringOptional. 8 to 64 lowercase hex characters or dashes, like a UUID, so you can stop the command before it finishes.
ttybooleanOptional. Runs the command in a terminal, so programs print colors and progress bars. stdout then holds everything, with its escape codes, and stderr is empty. Default false.
colsintegerThe terminal's width, 20 to 500. Default 80. Only with tty: true.
rowsintegerThe terminal's height, 5 to 200. Default 24. Only with tty: true.
{
"exit_code": 0,
"stdout": "Linux computer-4f2a1c 6.8.0 x86_64 GNU/Linux\n",
"stderr": "",
"timed_out": false,
"stopped": false,
"truncated": false,
"duration_ms": 41
}stdout and stderr are each cut at 256 KB, and truncated says when that happened. A command that runs out of time is stopped with everything it started, and comes back with timed_out: true and exit_code: null. For longer jobs, start them in the background and check on them later:
nohup ./build.sh > build.log 2>&1 &Each call is its own shell. Use cwd and env, or files, to carry state between calls. Up to 8 commands, file and screen calls can run at once on your account.
Restart
POST/api/v1/computers/:id/restart
Reboots a running computer from the inside. Everything running on it stops and it answers again in about half a minute; files and the disk stay. Returns { "restarting": true }. Until it is back, commands, files and the screen answer with agent_offline.
Open a shell
GET/api/v1/computers/:id/shell
Returns { "url", "expires_at" }: a WebSocket address to open within 2 minutes, with cols and rows added to the query if you know them. It is a real login shell in a terminal, as root or as ubuntu in /home/ubuntu/workspace on a coding computer: binary messages carry its output to you and your keystrokes to it, and a text message of { "cols", "rows" } resizes it. The first message is text, { "resume" }. The shell ends when you close the connection, and when it exits on its own the connection closes with code 1000. If the connection drops instead, as when a phone puts the browser to sleep, or you close it with code 1001 or 4000, the shell keeps running until the computer stops: open a new address with resume added to its query to get it back, starting with the last 256 KB it printed. A waiting shell still counts against the limits, and the one waiting longest ends when a new shell needs its place. Up to 4 at once per computer and 8 per account, 16 across everyone, and 60 addresses a minute per account. A refused connection fails its WebSocket upgrade with an HTTP status and a body of { "error" }: 401 unauthorized when the address expired, 404 no_session when there is no shell to resume, 409 not_running or agent_offline, 429 too_many_shells, or 503 busy.
Stop a command
POST/api/v1/computers/:id/exec/stop
Send { "run_id" } with the id you gave the command. It gets an interrupt, like ctrl+c, and a kill two seconds later if it is still running. The original call then returns what the command printed so far with stopped: true and exit_code: null. Returns { "stopped" }, false when nothing with that id was running.
Usage
GET/api/v1/computers/:id/stats
Returns { "cpu_percent", "memory_used_gb", "memory_total_gb", "disk_used_gb", "disk_total_gb", "download_bytes_per_second", "upload_bytes_per_second" } as measured inside the running computer over half a second, the disk being its root filesystem.
Files
Read a file
GET/api/v1/computers/:id/files?path=/etc/os-release
Returns { "path", "size", "encoding", "content" }. Text comes back as utf-8, anything else as base64. Add &encoding=base64 to always get base64, or &encoding=utf-8 to get an error instead of base64. Files are limited to 8 MB; paths to 4096 characters.
Write a file
PUT/api/v1/computers/:id/files
pathstringRequired. Absolute path. Missing folders are created.
contentstringRequired. Up to 8 MB once decoded.
encodingstringutf-8 or base64. Default utf-8.
modestringOctal permissions such as 0755. Default keeps the old file's, or 0644.
overwritebooleanfalse refuses to replace a file that already exists, with 409 already_exists. Default true.
Returns { "path", "size" }. The write is atomic: readers see the old file or the new one, never half of it.
List a folder
GET/api/v1/computers/:id/directory?path=/root
Returns { "path", "entries", "truncated" }. Each entry has a name, link (true for a symbolic link), a type (directory, file or other), a size in bytes for files (null otherwise) and a modified_at time (null when it can't be read). Links show as what they point to. Folders come first, and a listing stops at 1,000 entries with truncated: true.
Watch a folder
GET/api/v1/computers/:id/directory/watch
Returns { "url", "expires_at" }: a WebSocket address to open within 2 minutes. Send a text message of { "path" } with an absolute folder to watch it, and another to switch. A text message of { "path" } comes back at once, then again whenever something directly in that folder is added, removed, renamed, written or has its permissions or times changed, or the folder itself is removed or moved, at most once a second; list the folder to see what changed. A path that isn't absolute or isn't a folder is never watched. Messages are up to 8 KB each; a larger one closes the connection. Up to 4 at once per computer and 8 per account, 32 across everyone, and 60 addresses a minute per account. A refused connection fails its WebSocket upgrade with an HTTP status and a body of { "error" }: 401 unauthorized when the address expired, 409 not_running, agent_offline or agent_outdated, 429 too_many_watchers, or 503 busy.
Create a folder
POST/api/v1/computers/:id/directory
Send { "path" }. Missing parent folders are created too. Returns { "path" } with status 201, or already_exists when something has that path.
Move or rename
PATCH/api/v1/computers/:id/files
Send { "path", "new_path" }. Works for files and folders, and never replaces anything: if new_path exists you get already_exists. Missing folders in new_path are created. The root folder can't be moved, and nothing moves into itself. Returns { "path" }, the new one.
Delete
DELETE/api/v1/computers/:id/files?path=/root/old
Deletes a file, or a folder with everything in it. A link is removed, never what it points to. The root folder can't be deleted. Returns { "path", "deleted" }.
Desktop
Create a computer with "image": "ubuntu-24.04-desktop" and it also runs a 1280x800 desktop with Firefox that your agent sees through screenshots and drives with the mouse and keyboard, the way computer use models work. It needs at least 2 vCPU, 4 GB of memory and a 20 GB disk. Commands you run see the screen through DISPLAY=:1, so firefox https://example.com & opens a window on it.
Take a screenshot
GET/api/v1/computers/:id/screenshot?format=png
format is png (the default) or jpeg, which is several times smaller. cursor is where the mouse pointer is, or null when it can't be read.
{
"width": 1280,
"height": 800,
"format": "png",
"content_base64": "iVBORw0KGgo...",
"cursor": { "x": 640, "y": 400 }
}Watch it live
GET/api/v1/computers/:id/stream
Returns { "url", "control_url", "expires_at" }: two WebSocket addresses to open within 2 minutes. Each binary message is one video frame: a first byte of 1 for a keyframe or 0 otherwise, then H.264 (constrained baseline, Annex B) at up to 60 frames a second. When an API key sends input, a text message of { "type": "input", "by", "x", "y" } names the key and where the pointer is headed (null for keyboard-only input), so a viewer can show who is acting, and a text message of { "type": "pointer", "x", "y", "shape" } follows the pointer as it moves, up to 30 times a second, with shape a CSS cursor name such as default, text or pointer. The video leaves the pointer out, so draw it from these messages. Computers created before November 14, 2026 draw the pointer into the video instead, and their pointer messages, if any, carry no shape. The stream starts when the first viewer connects and stops shortly after the last one leaves.
control_url carries the same video and also takes the mouse and keyboard: send each input as a text message holding one JSON object of up to 256 bytes; a larger one closes the connection and an invalid one is ignored. Coordinates are pixels from the top left of the screen. Up to 500 messages a second; past that, everything except letting go of a key or button and control is dropped. Every key and button still held is let go when the connection closes.
movex, yMove the pointer, 0 to 9999.
buttonbutton, downPress (down true) or let go (down false) of a button: 1 left, 2 middle, 3 right, 4 and 5 scroll up and down, 6 and 7 scroll left and right, 8 and 9 back and forward.
keykeysym, downPress or let go of a key, given as an X11 keysym: 65293 for Return, 97 for a, or 0x01000000 plus the code point for any other Unicode character. A character the keyboard lacks is typed through a spare key. For a character, the keysym decides whether Shift is pressed; other keys keep the modifiers you hold.
releaseLet go of every key and button still held.
controlactiveSay you are driving (active true) or done (active false). While any viewer is driving, input calls from the API get 409 screen_in_use. It ends when the connection closes.
{"type":"move","x":640,"y":400}
{"type":"button","button":1,"down":true}
{"type":"button","button":1,"down":false}
{"type":"key","keysym":97,"down":true}
{"type":"key","keysym":97,"down":false}Up to 2 viewers per computer and 4 per account, 16 across everyone, and up to 60 stream addresses a minute per account. A refused connection fails its WebSocket upgrade with an HTTP status and a body of { "error" }: 401 unauthorized when the address expired, 409 not_running, no_desktop or agent_offline, 429 too_many_viewers, or 503 busy. Screenshots keep working either way.
Use the mouse and keyboard
POST/api/v1/computers/:id/input
actionsarrayRequired. 1 to 20 actions, run in order. Coordinates are pixels from the top left of the screen, 0 to 9999.
screenshotbooleanAlso return a screenshot taken a second after the last action. Default false.
movex, yMove the pointer.
clickx, y, button, countClick at a point. button is left, middle or right (default left), count 1 to 3 (default 1).
dragx, y, to_x, to_yDrag from x, y to to_x, to_y.
scrollx, y, direction, amountScroll up, down, left or right (default down), 1 to 20 steps (default 3).
typetextType text, up to 4000 characters in one call.
keykeysPress keys, for example ctrl+l, Return or ctrl+shift+t, several separated by spaces. Up to 100 characters.
curl https://disposable.computer/api/v1/computers/$COMPUTER_ID/input \
-H "Authorization: Bearer $DC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"actions": [{"type": "click", "x": 640, "y": 400}], "screenshot": true}'Returns { "actions" }, the number of actions run, plus screenshot when you asked for one (null if it failed). Input calls to one computer run one at a time, so actions from two agents never interleave; a call waits up to 10 seconds for its turn, then gets 429 agent_busy. While someone controls the screen, input gets 409 screen_in_use; screenshots still work.
Coding
Create a computer with "image": "ubuntu-24.04-coding" and it comes with Claude Code, Codex, Gemini CLI and opencode installed, along with Node.js 22, Python with uv, git, the GitHub CLI and build tools. Commands and the terminal run as the ubuntu user, which has passwordless sudo, starting in /home/ubuntu/workspace. Files you upload into its home belong to that user.
Sign in to each tool with your own account or API key inside the computer, for example by running claude or codex login --device-auth in its terminal, or pass a key in env on each command. We never provide or store model access. It costs the same as a headless computer.
MCP
The MCP server at /api/mcp speaks streamable HTTP and takes the same API key.
Add it to
Put your key in DC_API_KEY, then add the MCP server.
claude mcp add --transport http disposable \
https://disposable.computer/api/mcp \
--header "Authorization: Bearer $DC_API_KEY"Tools: create_computer (waits until it is ready), duplicate_computer (waits until it is ready), list_computers, get_computer, run_command, read_file, write_file, take_screenshot, send_input (returns a screenshot after the actions) and delete_computer.
Pricing and limits
Prices are in US dollars, paid from a balance you top up in the console from $5. A computer costs $0.05 per vCPU, $0.02 per GB of memory and $0.0005 per GB of disk, every hour, counted by the minute from the moment it starts provisioning until you delete it. A desktop adds $0.05 an hour. A stopped computer only pays for its disk.
Creating, starting or growing a running computer needs enough balance for an hour of it. If your balance runs out, your computers stop. Stopped disks keep costing, so the balance can go below zero; your next purchase covers that first. Three days after the balance runs out, the computers are deleted. We email you when they stop and the day before they are deleted, and, unless you turn it off in Settings, when the balance has less than a day left.
Up to 5 computers and 150 GB of disk across them per account. Up to 30 new computers, 60 changes (start, stop, resize, delete), 60 account changes and 600 commands, file and screen calls a minute. Request bodies can be up to 4 MB for commands, 24 MB for file writes and 64 KB for input. Each computer downloads at up to 100 Mbit/s and uploads at up to 30 Mbit/s. To test your app on a slow or flaky network, slow the computer itself down with sudo tc qdisc add dev $(ip route | awk '/default/ {print $5; exit}') root netem delay 200ms loss 1% and undo it with sudo tc qdisc del dev $(ip route | awk '/default/ {print $5; exit}') root. Outbound mail ports are closed.
Errors
Every error comes back with an HTTP status and a body like this:
{
"error": "insufficient_balance",
"message": "Your balance can't cover an hour of this computer. Add funds to keep going."
}invalid_request400A field is missing or out of range. The message says which.
invalid_json400The body isn't valid JSON.
disk_shrink400Disks can grow but never shrink.
is_directory400The path is a folder, not a file.
not_directory400The path, or a folder along it, is a file.
input_failed400An action couldn't run, usually because of an unknown key name. The message says why.
unauthorized401The API key is missing, wrong or revoked.
insufficient_balance402Your balance can't cover an hour of this computer.
forbidden403API keys can't manage keys, billing or account settings. Use the console.
invalid_email400The new email isn't a valid address.
same_email400The new email is already the account's email.
email_taken409Another account already uses that email.
wrong_code400The code is wrong or expired.
code_used_up400Too many wrong tries for that code. Send a new one.
account_disabled403The account is disabled. Contact support.
permission_denied403The computer refused to read or write that path.
not_found404No computer, key or session with that id on your account.
file_not_found404No file or folder at that path.
no_session404That shell has ended or waited too long to be resumed. Open a new one.
method_not_allowed405That endpoint doesn't take this HTTP method.
not_running409The computer has to be running for commands, files and the screen.
agent_offline409The computer isn't answering: it is still booting, restarting, or something on it stopped the agent.
computer_limit409You have reached your limit of computers. Delete one first.
disk_limit409Your computers' disks add up to your limit. Delete one or pick a smaller disk.
invalid_state409The computer can't do that in its current state.
no_desktop409The computer has no desktop. Create one with the ubuntu-24.04-desktop image.
run_id_in_use409A command with that run_id is still running. Send a new id.
already_exists409Something already exists at that path.
body_too_large413The request body is over the limit for that endpoint.
file_too_large413Files are limited to 8 MB.
unsupported_content_type415Send the body as JSON with Content-Type: application/json.
rate_limited429Too many requests, or too many running at once. Slow down and retry.
screen_in_use409Someone is controlling the screen. Wait until they release it.
agent_outdated409The computer was created before this feature existed. Create a new one to use it.
too_many_shells429The computer or your account already has as many open shells as it allows. Close one first.
too_many_watchers429The computer or your account already has as many folder watchers as it allows. Close one first.
too_many_viewers429The computer or your account already has as many live screen viewers as it allows. Close one, or use screenshots.
agent_busy429That computer already has 8 commands, file or screen calls waiting. Wait for one to finish.
internal_error500Something went wrong on our side. Retry.
agent_error502The computer couldn't carry out the command, file or screen call.
unavailable502, 503The computer could not be reached. Retry shortly.
busy503Too many live connections are open on our side. Retry in a few seconds.
no_capacity503No room for a computer of that size right now. Retry in a few minutes.
desktop_unavailable503The desktop is still starting. Retry shortly.
timeout504The computer did not answer in time.