Clipboard API
Every board is an HTTP endpoint. There is no API key, no SDK and no JSON envelope around the
text - a board is a URL you can curl. That makes it a practical bridge between
a machine you only reach over SSH and the browser on your desk.
All examples use the board myboard on
www.clipboard2.com. Replace it with your own name; a
board is created the first time it is used, so no setup call is needed.
-L follows the redirect from the bare domain and is worth keeping in every
command.
Read the text
curl -L www.clipboard2.com/myboard/text
Invoke-RestMethod https://www.clipboard2.com/myboard/text
wget -qO- www.clipboard2.com/myboard/text
python3 -c "import urllib.request as u;print(u.urlopen('https://www.clipboard2.com/myboard/text').read().decode())"
Returns the current text as-is, with no quoting and no trailing newline. An empty board
returns an empty body. Add -w '\n' if you want your shell prompt on its own
line afterwards.
Write the text
curl -L -X POST --data "text sample" www.clipboard2.com/myboard/text
Invoke-RestMethod -Method Post -ContentType 'text/plain' -Body 'text sample' https://www.clipboard2.com/myboard/text
wget -qO- --post-data="text sample" www.clipboard2.com/myboard/text
python3 -c "import urllib.request as u;u.urlopen(u.Request('https://www.clipboard2.com/myboard/text',b'text sample',{'Content-Type':'text/plain'}))"
A POST replaces the whole content of the board; it does not append. Every browser that has the board open sees the new text immediately.
With curl, --data sends application/x-www-form-urlencoded, which the
server decodes for you - convenient for a quick line of text. For content that must arrive
byte for byte, send it as plain text instead:
curl -L -X POST -H "Content-Type: text/plain" --data-binary @notes.txt www.clipboard2.com/myboard/text
Piping works the same way with --data-binary @-, which is the usual way to get
command output onto a board:
tail -n 200 /var/log/app.log | curl -L -X POST -H "Content-Type: text/plain" --data-binary @- www.clipboard2.com/myboard/text
Upload files
curl -L -F "files=@myfile.pdf" www.clipboard2.com/myboard/files
Invoke-RestMethod -Method Post -Form @{files = Get-Item myfile.pdf} https://www.clipboard2.com/myboard/files
wget cannot send a file upload - use curl or PowerShell
the standard library has no multipart helper - use curl or PowerShell
The field name is files and it may be repeated to upload several files in one
request. The response is the board's complete file list as JSON:
[{"id":"a1b2c3d4","fileName":"report.pdf","size":13067,"expiryDate":"2026-09-11T10:37:18.605+00:00"}]
Take the id from there for downloads and deletes. expiryDate is
when the file is removed automatically.
Download a file
curl -L -OJ www.clipboard2.com/myboard/file/a1b2c3d4
Invoke-WebRequest -OutFile myfile.pdf https://www.clipboard2.com/myboard/file/a1b2c3d4
wget --content-disposition www.clipboard2.com/myboard/file/a1b2c3d4
python3 -c "import urllib.request as u;u.urlretrieve('https://www.clipboard2.com/myboard/file/a1b2c3d4','myfile.pdf')"
-OJ stores the file under its original name, which the server sends in the
Content-Disposition header. Without -J, curl would name the file
after the ID. Every file row on the board has a button that copies this exact command,
including the ID.
Delete a file
curl -L -X DELETE www.clipboard2.com/myboard/file/a1b2c3d4
Invoke-RestMethod -Method Delete https://www.clipboard2.com/myboard/file/a1b2c3d4
wget --method=DELETE -qO- www.clipboard2.com/myboard/file/a1b2c3d4
python3 -c "import urllib.request as u;u.urlopen(u.Request('https://www.clipboard2.com/myboard/file/a1b2c3d4',method='DELETE'))"
Answers 204 No Content and removes the file from the server right away, on all
connected devices.
Endpoints at a glance
| Method | Path | Purpose |
|---|---|---|
| GET | /{board}/text | Read the board text |
| POST | /{board}/text | Replace the board text |
| POST | /{board}/files | Upload one or more files, returns the file list |
| GET | /{board}/file/{id} | Download one file |
| DELETE | /{board}/file/{id} | Delete one file |
Errors
Upload problems come back as JSON with a stable reason code, so a script can
react to them:
| Status | reason | Meaning |
|---|---|---|
| 413 | TOTAL_SIZE_EXCEEDED | The board would exceed 100 MB in total |
| 422 | TOO_MANY_FILES | More than 10 files on one board |
| 422 | EMPTY_FILE | An empty file was sent |
| 429 | RATE_LIMITED | Too many uploads from your network, wait and retry |
| 404 | - | The file ID does not exist, or it has expired |
Rules worth knowing
- Board names are up to 32 characters of letters, digits, hyphens and underscores, and they are case sensitive.
- Content expires after 1 minutes - text and files together. A board that is only read through the API expires just the same, so a script that needs the content should fetch it, not park it.
- There is no authentication. The board name is the only secret. Generate a long random name for anything automated, the way you would a token.
- Be gentle. The service is free and shared; poll a board every few seconds at most, and use the browser if you need true real-time updates.
A shortcut worth keeping
Sending the output of a command to your desk is a one-liner, but it is a long one. Put it in
your shell profile - ~/.bashrc, ~/.zshrc, or the file
$PROFILE points at in PowerShell - so it is defined everywhere you work:
clip() { curl -sL -X POST -H "Content-Type: text/plain" --data-binary @- www.clipboard2.com/myboard/text; }
function Send-Clip { $input | Out-String | ForEach-Object { Invoke-RestMethod -Method Post -ContentType 'text/plain' -Body $_ https://www.clipboard2.com/myboard/text } }
clip() { wget -qO- --post-data="$(cat)" www.clipboard2.com/myboard/text >/dev/null; }
clip() { python3 -c "import sys,urllib.request as u;u.urlopen(u.Request('https://www.clipboard2.com/myboard/text',sys.stdin.buffer.read(),{'Content-Type':'text/plain'}))"; }
From then on, sending anything to the board is one word at the end of a pipe:
dmesg | tail -50 | clip
Get-Content app.log -Tail 50 | Send-Clip
dmesg | tail -50 | clip
dmesg | tail -50 | clip