▼
Everything the forms on this site do, for scripts. Every reply is JSON, including errors, so you never have to guess whether a body is a result or an explanation.
POST to the endpoint with either form parameters or a JSON body. Both are equivalent; pick whichever your tooling makes easier. The action parameter selects what to do. Use --data-urlencode for anything a person typed — paste text, a URL, a translation — because -d sends the value raw and it will be cut at the first &. Plain -d is fine for fixed values like action itself.
curl -X POST https://www.0wx.cat/api.cgi \
-d action=paste --data-urlencode 'content=hello & world'
curl -X POST https://www.0wx.cat/api.cgi \
-H 'Content-Type: application/json' \
-d '{"action":"paste","content":"hello & world"}'
In a form body & separates parameters, and curl -d does not encode what you give it. A URL with a query string is therefore cut at its first &, and the rest arrives as separate parameters. Use --data-urlencode, or send JSON. Stray parameters are refused rather than ignored, so this fails loudly instead of storing half a link.
# wrong -- the value is cut at the &
curl -X POST https://www.0wx.cat/api.cgi \
-d action=tinyurl -d 'url=https://example.com/?a=1&b=2'
# right
curl -X POST https://www.0wx.cat/api.cgi \
-d action=tinyurl --data-urlencode 'url=https://example.com/?a=1&b=2'
# also right -- JSON needs no escaping
curl -X POST https://www.0wx.cat/api.cgi -H 'Content-Type: application/json' \
-d '{"action":"tinyurl","url":"https://example.com/?a=1&b=2"}'
Optional. Without a key a request is anonymous and behaves exactly like an anonymous visitor — the result is public either way, the only difference is whether it lands in your file list. Generate a key on your account page and send it as a bearer token; passing it as an apikey parameter also works, but a key in a query string ends up in server logs and browser history.
curl -X POST https://www.0wx.cat/api.cgi \
-H 'Authorization: Bearer YOUR_KEY' \
-d action=whoami
| action | Parameters | Description |
|---|---|---|
whoami | — | Confirms a key works and reports the current limits. Costs nothing; call it first. |
upload | file (repeatable), resize, lifetime | Uploads one or more files. Must be multipart/form-data. |
tinyurl | url, lifetime | Shortens a URL. |
hugeurl | url, lifetime | Lengthens a URL, absurdly, on purpose. |
paste | content, lifetime | Stores a block of text and returns a link to it. |
translate | content, to | Translates text. to is an ISO 639 code — note these are not always country codes: Danish is da, not dk. |
Uploads cannot use the JSON body, because a file has to be sent as multipart. Repeat the file part to send several at once. Each file is reported separately: the reply carries a files list of those that were accepted and a rejected list of those that were not, so a partial success is still a success.
curl -X POST https://www.0wx.cat/api.cgi \
-H 'Authorization: Bearer YOUR_KEY' \
-F action=upload \
-F file=@photo.jpg \
-F file=@notes.txt \
-F lifetime=7d
Optional on every action that stores something. Accepts bare seconds, or a number followed by m, h or d — the same forms the web forms take. Omit it or pass 0 to keep the item indefinitely.
Every reply is a JSON object with an ok field. On success it carries the result; on failure it carries a stable error code and a human-readable message. Branch on the code, log the message.
{"ok":true,"type":"paste","share":"Ab3xK9pQ",
"url":"https://www.0wx.cat/p/Ab3xK9pQ","bytes":11}
{"ok":false,"error":"url_too_long",
"message":"URL exceeds the maximum length."}
| error | HTTP | Description |
|---|---|---|
unknown_action | 400 | No such action. |
bad_json | 400 | The body was not valid JSON, or a field held a nested structure. |
unexpected_parameter | 400 | A parameter arrived that this action does not take — usually an unencoded & in a value. The message names the parameters. |
bad_key | 401 | The key was not recognised. A wrong key is refused rather than silently treated as anonymous. |
bad_url | 400 | The URL is malformed, or uses a scheme other than http(s), ftp or gopher. |
bad_lifetime | 400 | The lifetime was not a number or a number followed by m, h or d. |
no_content | 400 | Nothing was sent to store. |
url_too_long | 413 | The URL is longer than the limit. |
paste_too_long | 413 | The text is larger than the paste limit. Limits are measured in bytes of UTF-8, so non-Latin text counts more than its character count suggests. |
too_many_files | 413 | More files than one call accepts. |
no_usable_files | 415 | Every file was rejected. Check the rejected list for why. |
backend_unavailable | 502 | The translation backend did not answer. |
internal | 500 | Something failed unexpectedly. It has been logged. |
Call whoami to read the current values rather than hardcoding them — they are set by the server operator and can change. Sizes are bytes of UTF-8.
There is deliberately no remote-fetch action. It would make the server retrieve a URL of the caller's choosing, which is a request-forgery surface, and a script can drive that far harder than a form. Upload the file yourself instead.