Developer API

upload files programmatically with a per-account key

GETTING A KEY

Sign in with Google, then generate a key from your account page. It's shown once — copy it somewhere safe. Free tier only for now — pro/business are planned but not live yet.

LIMITS

tierfree (only tier currently available)
requests20 per minute, 500 per day
max file size5MB per file (per entry too, if it's a zip)
zip total uncompressed size50MB across all entries
zip entry count2000 max per archive

UPLOADING

One endpoint, works the same everywhere — POST the raw file bytes as the request body, with your key in the Authorization header.

macOS / Linux (curl)

curl -X POST https://rawpx.pages.dev/api/v1/upload \
  -H "Authorization: Bearer rk_your_key_here" \
  -H "X-Filename: photo.png" \
  -H "Content-Type: image/png" \
  --data-binary @photo.png

Windows (PowerShell)

Windows' built-in curl is actually an alias for Invoke-WebRequest, which uses different flags — use this instead:

Invoke-WebRequest -Method POST -Uri "https://rawpx.pages.dev/api/v1/upload" `
  -Headers @{ "Authorization" = "Bearer rk_your_key_here"; "X-Filename" = "photo.png" } `
  -ContentType "image/png" `
  -InFile "C:\path\to\photo.png"

(Or install real curl.exe and use the macOS/Linux command above as-is — Windows ships it too, just call it as curl.exe to bypass the alias.)

JavaScript — website or app backend

Only call this from server-side code (Node, a backend route, a Cloudflare Worker of your own, etc). Never from client-side browser JS — anyone could read your key out of the page.

const fileBuffer = await fs.readFile("photo.png"); // or however you have the bytes

const res = await fetch("https://rawpx.pages.dev/api/v1/upload", {
  method: "POST",
  headers: {
    "Authorization": "Bearer rk_your_key_here",
    "X-Filename": "photo.png",
    "Content-Type": "image/png",
  },
  body: fileBuffer,
});
const data = await res.json();
console.log(data.url);

Python

import requests

with open("photo.png", "rb") as f:
    res = requests.post(
        "https://rawpx.pages.dev/api/v1/upload",
        headers={
            "Authorization": "Bearer rk_your_key_here",
            "X-Filename": "photo.png",
            "Content-Type": "image/png",
        },
        data=f.read(),
    )
print(res.json()["url"])

response

{ "status": "uploaded", "url": "https://rawpx-files.pages.dev/f/<hash>.png", "delete_url": "..." }

If the exact file already exists (uploaded by anyone), you get "status": "exists" back instantly instead — no re-upload happens.

zips

Same endpoint, same headers — just point it at a .zip. It gets unzipped server-side, every file inside is hashed and deduped individually, and you get an archive link back instead:

{ "status": "uploaded", "archive_url": "https://rawpx-files.pages.dev/archive/<hash>", "entry_count": 12, "new_files_uploaded": 9 }

DOWNLOADING

There's no separate download API and no auth needed to download — that's the whole point of the raw links. Once you have a url from an upload response, it's just a normal public GET request, from anything: a browser, curl, an <img> tag, a CSS background, whatever.

curl -O https://rawpx-files.pages.dev/f/<hash>.png
Invoke-WebRequest -Uri "https://rawpx-files.pages.dev/f/<hash>.png" -OutFile "photo.png"

ERRORS

401missing, invalid, or revoked key
413file over 5MB
429rate limited — back off and retry after a bit
This is separate from the browser upload flow on the homepage, which uses short-lived presigned URLs and never touches your API key. Treat your key like a password — don't put it in code that ships to end users' browsers or apps.