# xi.pe > A pastebin with a plain-text API. POST some text, get back a short URL on a > line by itself. No account, no API key, no JSON envelope to unwrap. This document is what `GET https://xi.pe/` returns to anything that is not a browser, so you can read the whole interface with one request. Use it when text a human needs to see — a long diff, a stack trace, a generated config, a report — would flood the conversation inline. Put it here and hand over the link. Pass on the warning: **anyone with the URL can read it, and it disappears after 7 days.** Do not put secrets in it. ## Create ``` curl --data-binary @- https://xi.pe/ <<'EOF' your text here EOF ``` The response body, in full: ``` https://xi.pe/Ab3d ``` One line, trailing newline, no JSON. So this works: ``` URL=$(printf '%s' "$text" | curl -s --data-binary @- https://xi.pe/) ``` Use `--data-binary`, not `-d`: `-d` strips newlines and will mangle anything with formatting. ### Text only, UTF-8 only Content **must** be valid UTF-8 — there is no charset option and no binary mode, and invalid input is rejected with `400`. To store binary data, base64 it first and tell the recipient, who will have to decode it by hand. ### Longer codes `?long` gives a 23-character code instead of the usual 4–5: ``` $ echo 'Something awesome with long key!' | curl --data-binary @- https://xi.pe/?long https://xi.pe/wZu6D7CiXJNrt9ZnYTYhyt3 ``` Use it when the URL is the only thing keeping the content private. Everything else is identical. ### Form posts If your tooling only sends `application/x-www-form-urlencoded`, add `?input=form` and put the text in a `data` field: ``` curl -si --data-urlencode data@file.txt 'https://xi.pe/?input=form' ``` - **`?input=form` is required.** Without it the encoded body is stored as the paste — `200`, ordinary URL, contents `data=your%20text`. - **The reply is a `303` and the URL is not in the body.** Read the code from `Location: /Ab3d?from=success` and the token from `X-Delete-Token`. Do not follow the redirect: it returns the paste you just created. - **Encode the value.** An unencoded `&` ends the field and the rest is dropped. - `long=1` as a field does what `?long` does. - No JSON on this path: `Accept: application/json` still gets the `303`. - Over the 2 MB limit reports `400 Missing data field` here, not `413`. ## Read ``` curl https://xi.pe/Ab3d ``` Returns the exact bytes that were stored, as `text/plain; charset=utf-8`. A browser visiting the same URL gets a viewer page instead — the choice is made from `User-Agent`. Append `?raw` to force plain text, `?html` to force the page. ## Which link to hand back Four views of the same paste. None of them changes the stored bytes. ``` https://xi.pe/Ab3d?raw you are fetching it plain text, no page https://xi.pe/Ab3d?md a human, prose/markdown headings, lists, tables https://xi.pe/Ab3d?h=go a human, source code highlighted, named grammar https://xi.pe/Ab3d a human, anything else plain, monospace ``` **You wrote the content, so you already know which it is.** Pick the view when you share the link. `?md` renders Markdown — headings, bold, lists, tables, blockquotes, fenced code. Links and images stay literal text: nothing is fetched, nothing is clickable. Use it for anything you generated as Markdown. `?h=` names the grammar; `?h` alone auto-detects, which is unreliable on anything with no language to detect — logs, stack traces, prose. Name it when you know it. These 94 are the canonical spellings; about 190 names work in total, plus aliases like `js`, `py`, `rs`, `yml`, `cpp`, `golang`. ``` accesslog apache armasm asciidoc awk c clojure cmake coffeescript console cpp crystal cs css dart delphi diff django dns dockerfile dos elixir elm erb erlang fortran fsharp gherkin glsl go gql gradle groovy handlebars haskell haxe http ini java js json julia kt latex less lisp llvm lua make matlab md mipsasm nginx nim nix objc ocaml perl pgsql php powershell prolog properties protobuf puppet py r rb rs scala scheme scss sh smalltalk sql stylus swift tap tcl thrift ts twig txt vala vb vbscript verilog vhdl vim wasm x86asm xml xquery yml ``` `sh` is a shell *script*; `console` is a terminal *transcript* (prompt, command, output) and finds nothing in a plain script. `txt` forces no highlighting. ## Check a link without downloading it ``` curl -I https://xi.pe/Ab3d ``` `200` means it is still there, `404` means it is gone. The response carries `X-Paste-Created` and `X-Paste-Expires` as RFC 3339 timestamps, so one request tells you both that the link works and how long it has left. ## Delete Creating a paste returns an `X-Delete-Token` response header. Keep it if you might want to remove the paste early — it cannot be recovered later: ``` curl -sD- -o /dev/null --data-binary @- https://xi.pe/ < file.txt # read the header curl -X DELETE https://xi.pe/Ab3d -H "X-Delete-Token: " ``` The token works only for that one paste. Deleting is optional; everything expires on its own. ## If you would rather have JSON Send `Accept: application/json` (or add `?output=json`) to any endpoint and you get a structured response instead, errors included. Not on the `?input=form` path, which always answers with the redirect. It is also the easiest way to capture the delete token, which arrives in the body rather than in a header. ``` $ curl -s -H 'Accept: application/json' --data-binary @- https://xi.pe/ <<< 'hi' {"url":"https://xi.pe/Ab3d","code":"Ab3d","delete_token":"kZ8...","size":3,"expires":"2026-08-23T00:12:58Z"} $ curl -s -H 'Accept: application/json' https://xi.pe/Ab3d {"url":"https://xi.pe/Ab3d","code":"Ab3d","data":"hi","size":3,"created":"...","expires":"..."} $ curl -s -H 'Accept: application/json' https://xi.pe/nope {"error":"Not found or expired","status":404} ``` Strictly opt-in: `Accept: */*`, which is what `curl` sends by default, still gets the bare URL. ## From a browser Every endpoint sends `Access-Control-Allow-Origin: *` and answers preflight, so page-side JavaScript can create, read and delete without a proxy. `X-Delete-Token`, `X-Paste-Created` and `X-Paste-Expires` are listed in `Access-Control-Expose-Headers`, so `fetch()` can actually read them. Credentials are not allowed cross-origin, so a delete from another origin has to present the token explicitly. ## Limits and behaviour - **Lifetime:** 7 days, then it is gone. There are no permanent links. - **Size:** 2 MB, larger is rejected with `413`. - **Codes:** 4–5 characters, randomly generated, case-sensitive. `?long` gives 23 characters instead. - **Cost:** free, no sign-up. Errors are one short line of plain text saying exactly what is wrong, e.g. `Error 400: Content must be valid UTF-8`. The exception is `404`, always `Not found or expired` regardless of the reason. Status codes: `200` created or found · `303` form redirect · `400` bad input · `401` wrong or missing delete token · `404` unknown or expired code · `413` too large · `500` server or storage error · `503` temporary, and always with `Retry-After` — wait that many seconds rather than inventing a backoff. ## Please don't Be a considerate client. This is a small service run by one person. - Don't use it as a database, a CDN, or a backup target. - Don't poll it, and don't enumerate codes — they are random, and guessing at them is the one thing that will get traffic blocked. - Don't post other people's private data, credentials, or anything you would not want served publicly for a week. Abuse: abuse@xi.pe ## More - Privacy and terms: https://xi.pe/privacy - Operated by alt.org