Upload API

Send a file over plain HTTP and get back an encrypted share link and a deletion link. No SDK, no session, one header.

Quick start

1

Grab your key

It is the AUTH-Key under Settings › Security in your dashboard.

2

POST the file

Multipart form data to /api/v1/upload.

3

Read the JSON

You get url and deletionUrl.

Endpoint

POST https://imageserver.pw/api/v1/upload
Content-Type multipart/form-data
Auth header X-IMAGESERVER-AUTH-KEY
Rate limit 30 uploads per minute
Encryption happens server side, always

Request fields

Field Required Description
file yes The file itself. The extension of the sent filename decides the detected MIME type, which is then checked against your plan. Text files (.txt, .log, .json, .md and common source code) become pastes on every plan, up to 1 MB of UTF-8, with an optional language field for highlighting.
userKey no Your user key as a body field, for clients that cannot set headers. The header wins if both are present.
urlStyle no embed, raw or private. Decides which URL you get back, see URL styles. Leave it out to get the URL style set in your dashboard, which is private for a new account.
domains no A JSON array as a string. ["own"] serves the file from one of your verified domains, ["default"] from whichever domain the server is set to, and the slug of a domain category from that group, for example ["short"]. The config generator lists the categories that exist. Anything unknown, including a hostname, falls back to a random public domain, and with several entries one is chosen at random.
title no Embed title shown by Discord and friends. Defaults to imageserver.pw.
color no Embed accent color as hex, for example #ef7c3b, which is also the default.
expires no How long the upload is kept: never, 1h, 6h, 12h, 24h, 7d or 30d, counted from the upload. Sending this beats the "Expire after" setting in your dashboard, including never, so a client can decide per file. Leave it out to use the setting. An unknown value is ignored rather than rejected, and the setting applies. Expired uploads stop resolving immediately and are deleted by an hourly job; once deleted, the file is gone and cannot be restored.
maxDownloads no How often the upload may be fetched: unlimited, 1, 5, 10, 25 or 100. Works the same way as expires and alongside it: sending it beats the "Download limit" setting in your dashboard, including unlimited, and whichever of the two runs out first ends the link. Counted per visitor rather than per request, so a video player asking for byte ranges spends one download, and a request with the wrong key spends none. Once the last one is used the link stops working immediately and the file is queued for deletion. See making a link stop working.
folder no Name of the folder the upload is filed into. The folder is created on first use, so no setup in the dashboard is needed, and the name is matched without regard to case: Clips and clips are the same folder. Leave it out and the upload stays uncategorised. Names longer than 60 characters are cut to 60. An account holds at most 200 folders; past that, a new name is not created and the upload stays uncategorised rather than failing.
tags no Comma-separated tags, at most 10 per upload and 32 characters each. They are lowercased, spaces become hyphens and anything else is dropped, so CS:GO, Game Clips is stored as csgo and game-clips. Unlike the folder, a file can carry several.

Account settings can override your request

If "Use these settings for ShareX too" is on in your dashboard's upload settings, the server ignores urlStyle, domains, title and color from the request and uses your saved preferences instead. If your parameters seem to have no effect, this switch is the first thing to check.

With it off, your request decides, and anything it leaves out falls back to the same saved preferences. So an uploader config that never sets urlStyle follows your dashboard rather than a fixed default, and a config that does set it is obeyed. The switch only matters when the two disagree.

expires, maxDownloads, folder and tags are the exceptions and are never overridden. Retention and filing are per-file decisions, so a client that offers them before each upload stays in control regardless of this switch.

URL styles

embed

https://imageserver.pw/aB3dK9.png?key=…

A preview page with Open Graph tags, so Discord, Twitter and Slack render a card.

raw

https://imageserver.pw/raw/aB3dK9.png?key=…

The decrypted bytes with the right content type and nothing around them. Use it for <img src> and for anything that expects a real file.

private (new accounts)

https://imageserver.pw/p/aB3dK9.png#key=…

The key sits behind #, so browsers never transmit it and the file is decrypted locally. A crawler cannot open the file, so the link preview in Discord and elsewhere comes from a small separate image made at upload time: blurred by default, a full thumbnail, or none, as set under "Preview Image" in your dashboard. That image is the one part our server can read, see what we can see.

Examples

Linux, macOS, WSL
curl -X POST https://imageserver.pw/api/v1/upload \
  -H "X-IMAGESERVER-AUTH-KEY: your-user-key" \
  -F "file=@/path/to/image.png" \
  -F "urlStyle=embed" \
  -F "color=#ef7c3b"

Response

200 OK, from /api/v1/upload

{
  "url": "https://imageserver.pw/aB3dK9.png?key=Xq2…",
  "deletionUrl": "https://imageserver.pw/delete/aB3dK9?key=9f4…"
}
  • Store deletionUrl. It is the only way to remove the file without logging in, and it is never shown again.
  • The host in both URLs is whichever domain was picked, so it is not always imageserver.pw. Never build the link yourself, use the one you get back.

Deleting an upload

GET /delete/:key?key=deletion-key

Open the deletionUrl from the upload response in a browser. It shows a page that names the file and asks you to confirm; the file is deleted, immediately and for good, only when you press the button there. No auth key is involved, the deletion key in the query string is the credential, so keep the link to yourself.

Opening the link deletes nothing on its own. Chat apps, link scanners and browsers fetch links ahead of time, and a link that deleted on sight would let any of them remove your file. For the same reason a plain GET from a script only gets that confirmation page back, as HTML: there is no API endpoint that deletes with the auth key. Requests are limited to 10 per minute.

Signed in, you can delete the same files from the dashboard without the key.

Errors

Status Message What to do
400 Missing auth key or file. Check the header name and that the file part is called file.
400 Unsupported file type for your plan. The type is taken from the filename extension. See the allowed list on plans.
401 Invalid user key. Copy the key again from the dashboard, it may have been regenerated.
403 Your account is suspended. Uploads stay disabled until the suspension is lifted. Contact us on Discord.
400 File content does not match its extension. The bytes are checked against the extension. Send the file under its real extension.
403 This file cannot be uploaded. The file matches one that was removed from this site. It cannot be uploaded again.
413 File size exceeds your plan limit of X. The message names your actual limit. Compress the file or upgrade.
413 Storage quota exceeded. Remaining: X. Delete old uploads in the dashboard or raise the quota.
413 File size exceeds limit (500MB) Plain text, not JSON: the request was cut off before the upload was read. 500 MB is the most any request can carry.
429 Too many uploads. Please wait a moment before trying again. 30 per minute, counted per user key and shared with the Paste API. Back off and retry.
429 New accounts can upload 30 files a day during their first week. … Confirming an email address lifts it at once; otherwise it ends when the account is a week old.
429 Too many requests with an invalid user key. Try again in a few minutes. Several wrong keys in a row from one client. Fix the key, then wait a few minutes.
500 Server error occurred during file upload. Plain text, not JSON. Retry once, then tell us on Discord.

Except for the 500 above, errors are JSON in the shape { "error": "..." }.

ShareX

You do not have to build the preset by hand.

  1. Generate it in the Config Creator, or copy the ready-made one from the homepage
  2. ShareX, Destinations › Custom Uploader Settings › Import › From Clipboard
  3. Under Destinations, select it as image, text and file uploader

The generated preset posts to /api/v1/upload, so it always gets the JSON body and can fill in DeletionURL.

Postman

  1. New request, POST https://imageserver.pw/api/v1/upload
  2. Headers, add X-IMAGESERVER-AUTH-KEY
  3. Body, choose form-data
  4. Key file, type File, then pick your file
  5. Optionally add urlStyle, color, title, domains, folder, tags

Leave Content-Type unset, Postman writes the multipart boundary itself.

Everything you upload is encrypted

The server generates a random password per file, encrypts with AES-256, and hands the password back to you inside the URL. We keep no copy we can read, so unless a key pair of yours sealed one, a lost link is a lost file.

How the encryption works

Do not want to write code?

The desktop client does the same thing behind a hotkey: capture, upload, link in your clipboard. Windows, macOS and Linux.