#HTTP API Reference

#GET /

Return the index page.

#GET /<name>[.<ext>] or /<name>/<filename>

Fetch the paste with name <name>. By default, it will return the raw content of the paste.

The Content-Type header is set to the mime type inferred from the filename of the paste, or text/plain;charset=UTF-8 if no filename is present. If <ext> is given, the worker will infer mime-type from <ext> and change Content-Type. If the paste is uploaded with a filename, the worker will infer mime-type from the filename. This method accepts the following query string parameters:

The Content-Disposition header is set to inline by default. But can be overriden by ?a query string. If the paste is uploaded with filename, or <filename> is set in given request URL, Content-Disposition is appended with filename* indicating the filename. If the paste is encrypted, the filename is appended with .encrypted suffix.

If the paste is encrypted, an X-PB-Encryption-Scheme header will be set to the encryption scheme. An X-PB-Decrypted-Content-Type header is also set to the mime type that the decrypted content would have (inferred from the same sources as Content-Type but ignoring the encryption-induced application/octet-stream fallback), so clients can decide how to render the plaintext without an extra round trip.

If the paste is uploaded with a lang parameter, an X-PB-Highlight-Language header will be set to the highlight language.

Examples: GET /abcd?lang=js, GET /abcd?mime=application/json.

If error occurs, the worker returns status code different from 200:

#GET /<name>:<passwd>

Return the web page to edit the paste of name <name> and password <passwd>.

If error occurs, the worker returns status code different from 200:

#GET /u/<name>

Redirect to the URL recorded in the paste of name <name>.

If error occurs, the worker returns status code different from 302:

#GET /d/<name>[.<ext>] or /d/<name>/<filename>

Return the web page that will display the content of the paste of name <name>. If the paste is encrypted, a key can be appended to the URL to decrypt the paste of name <name> in browser.

If error occurs, the worker returns status code different from 200:

#GET /m/<name>

Get the metadata of the paste of name <name>.

If error occurs, the worker returns status code different from 200:

The response body is a JSON object, for example:

{
  "lastModifiedAt": "2025-05-05T10:33:06.114Z",
  "createdAt": "2025-05-01T10:33:06.114Z",
  "expireAt": "2025-05-08T10:33:06.114Z",
  "sizeBytes": 4096,
  "location": "KV",
  "filename": "a.jpg",
  "highlightLanguage": "rust",
  "encryptionScheme": "AES-GCM"
}

Explanation of the fields:

#GET /a/<name>

Return the HTML converted from the markdown file stored in the paste of name <name>. The markdown conversion follows GitHub Flavored Markdown (GFM) Spec, supported by remark-gfm.

Syntax highlighting is supported by prism.js. LaTeX mathematics is supported by MathJax.

If the paste is encrypted, the server cannot read it, so it returns a page without content, and the browser fetches the paste, decrypts it with the key in the URL fragment, and renders it. Open it as /a/<name>#<key>, or as /a/<name>#<key>:<section> to jump to the heading with id <section> (percent-encoded). Links inside the rendered page keep the key in the same way. See https://pastebin.doesthis.help/doc/paste-crypto.

If error occurs, the worker returns status code different from 200:

Usage example:

# Header 1

This is the content of `test.md`

<script>
alert("Script should be removed")
</script>

## Header 2

| abc | defghi |
| :-: | -----: |
| bar |    baz |

**Bold**, `Monospace`, _Italics_, ~~Strikethrough~~, [URL](https://github.com)

- A
- A1
- A2
- B

![Panty](https://shz.al/~panty.jpg)

1. first
2. second

> Quotation

$$
\int_{-\infty}^{\infty} e^{-x^2} = \sqrt{\pi}
$$

#HEAD /*

Request a paste without returning the body. It accepts same parameters as all GET requests, and returns the same Content-Type, Content-Disposition, Content-Length and cache control headers with the corresponding GET request. Note that the Content-Length with /a/<name>, ?lang=<lang> is the length of the paste instead of the length of the actual HTML page.

#POST /

Upload your paste. It accept parameters in form-data:

POST method returns a JSON string by default, if no error occurs, for example:

{
  "url": "https://shz.al/abcd",
  "manageUrl": "https://shz.al/abcd:w2eHqyZGc@CQzWLN=BiJiQxZ",
  "expirationSeconds": 1209600,
  "lastModifiedAt": "2025-05-01T10:33:06.114Z",
  "createdAt": "2025-05-01T10:33:06.114Z",
  "expireAt": "2025-05-05T10:33:06.114Z",
  "sizeBytes": 4096,
  "location": "KV",
  "filename": "a.jpg",
  "highlightLanguage": "rust",
  "encryptionScheme": "AES-GCM"
}

Explanation of the fields:

The remaining fields mirror the GET /m/<name> metadata response: lastModifiedAt, createdAt, expireAt, sizeBytes, location, and the optional filename, highlightLanguage, encryptionScheme.

If error occurs, the worker returns status code different from 200:

#PUT /<name>:<passwd>

Update your paste of the name <name> and password <passwd>. It accepts all the same form-data fields as POST (c, e, s, lang, encryption-scheme) except n (the name cannot be changed; supplying it returns 400) and p (silently ignored). When e is supplied, the expiration is recalculated from the update time.

The returning of PUT method is the same as POST method.

If error occurs, the worker returns status code different from 200:

#DELETE /<name>:<passwd>

Delete the paste of name <name> and password <passwd>. It may take seconds to synchronize the deletion globally.

If error occurs, the worker returns status code different from 200:

#GET /live

Return the live pastebin page. Everyone who opens the same /live#<key> URL joins one channel, where posts (text with an optional filename and highlight language, or files up to 10 MiB) show up on every open page immediately. <key> is 32 random bytes in base64 (with / replaced by _ and padding removed); a fresh one is generated if the fragment is empty.

The key never reaches the server. Each open page makes an ephemeral key pair, exchanges public keys with the other pages in the channel, and encrypts every post separately for the pages that are open at the time, so a recorded post cannot be decrypted later even with the link. Nothing is stored anywhere: a page only shows posts made while it is open. See https://pastebin.doesthis.help/doc/live-crypto for the cryptographic design and its analysis.

Nicknames are not authenticated: anyone with the link can post under any nickname and timestamp. Since posts come from other people, the page treats them as untrusted: markdown is sanitized more strictly than on /a/<name> (no custom classes, ids, form controls or remote images), text over 512 KiB and images other than PNG, JPEG, GIF, WebP, AVIF and BMP (including SVG) are only offered for download.

/live takes precedence over a paste named live, which can still be read through /d/live or /m/live. Random paste names never collide with it. If BASIC_AUTH is configured, both /live and /live/ws/<channel-id> require it.

#GET /live/ws/<channel-id>

WebSocket endpoint that relays posts between peers in the channel <channel-id>, which must be 32 lowercase hex characters.

A post body is at most 10 MiB, and frames larger than that plus 64 KiB close the connection with code 1009. Frames are rate limited by size, both per connection (24 MiB burst, then 1 MiB/s) and per channel (48 MiB burst, then 4 MiB/s); frames over the limit are dropped with an error message.

If error occurs, the worker returns status code different from 101: