snipbud API Dashboard →

Snipbud API

Send an image to Snipbud from your own code and get a clean, shareable link back. One request per upload. No SDK required.

This is a plain HTTP (REST) API that speaks JSON. If you can make an HTTP request, whether with curl, Node.js, Python, or anything else, you can use it. The examples below use curl and Node.js, but the shape is the same everywhere.

You'll need the Max plan. The API is included with Max. On Free or Pro you can keep using the website and apps, but you won't be able to create API keys.

On this page

Quick start Getting a key Authentication Workspaces Upload an image List images Get one Delete Rate limits Errors Keeping keys safe

Quick start

Three steps to your first shared link:

  1. On the Max plan, open the dashboard, click your account menu, and choose API keys. Create one and copy it (it starts with sk_live_).
  2. Send an image to the upload endpoint with your key.
  3. Read the url from the response, that's your shareable link.
# upload photo.png and get a public link back
curl -X POST "https://snipbud.apoiolabs.com/api/v1/images?visibility=public" \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "Content-Type: image/png" \
  --data-binary @photo.png

Getting an API key

An API key is a long secret string that proves a request is really coming from you, instead of logging in with an email and password each time.

  1. Sign in and make sure the workspace you want is on the Max plan (your personal space, or a team you own or admin).
  2. Open the account menu (top right) and choose API keys.
  3. Optionally give the key a name (so you remember what it's for) and choose an expiry: never, 30 days, 90 days, or 1 year.
  4. Click Create key. The full key is shown once. Copy it now and store it somewhere safe, we only keep a scrambled (hashed) copy and can never show it again.

You can create several keys (up to 25 active at a time), see when each was last used, and revoke any of them instantly if one leaks. Revoking takes effect immediately.

Authentication

Every request must include your key in a header called Authorization, in this exact format (the word Bearer, a space, then your key):

Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

A few important rules:

The base URL for every endpoint is:

https://snipbud.apoiolabs.com/api/v1

Workspaces: personal vs team keys

A key always belongs to the workspace it was created in, and everything it does happens there:

You can't change a key's workspace later, create a new key in the workspace you want instead.

Upload an image

POST /api/v1/images

Put the raw image file as the request body and set the Content-Type header to the file's format. Content-Type is just a label that tells us what kind of file you're sending. There's no form, no multipart, no presign step, one request does it.

Query parameters

NameDescription
visibilityoptionalpublic (anyone with the link can view) or private (only you). Defaults to private. Ignored for team keys, whose uploads are always team-visible.

Accepted files

Supported types: image/png, image/jpeg, image/gif, image/webp, image/avif, and image/svg+xml. Up to 100 MB per request.

The Content-Type must match the real file. We check the actual bytes, so if you send a JPEG but label it image/png (or send something that isn't an image at all), the request is rejected with 400. Set the header to the file's true format.

Examples

With curl (--data-binary @file sends the file's raw bytes):

curl -X POST "https://snipbud.apoiolabs.com/api/v1/images?visibility=public" \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: image/png" \
  --data-binary @photo.png

With Node.js (built-in fetch, Node 18+):

import { readFile } from "node:fs/promises";

const bytes = await readFile("photo.png");
const res = await fetch("https://snipbud.apoiolabs.com/api/v1/images?visibility=public", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_...",
    "Content-Type": "image/png",
  },
  body: bytes,
});
const data = await res.json();
console.log(data.url); // https://snipbud.apoiolabs.com/i/aX9f2Qk1bZ.png

Response 201 Created

{
  "key": "aX9f2Qk1bZ.png",
  "url": "https://snipbud.apoiolabs.com/i/aX9f2Qk1bZ.png",
  "visibility": "public",
  "size": 48213,
  "contentType": "image/png"
}
FieldWhat it is
keyThe image's unique id, including its file extension. You'll use this to fetch or delete it later.
urlThe shareable link. Paste it anywhere.
visibilitypublic, private, or team.
sizeStored size in bytes.
contentTypeThe image type we stored.

List your images

GET /api/v1/images

Returns the images in the key's workspace, newest first.

NameDescription
limitoptionalHow many to return, 1 to 100. Default 50.
beforeoptionalFor paging. Pass the nextBefore value from the previous response to get the next page.
curl "https://snipbud.apoiolabs.com/api/v1/images?limit=50" \
  -H "Authorization: Bearer sk_live_..."

Response 200 OK

{
  "images": [
    {
      "key": "aX9f2Qk1bZ.png",
      "url": "https://snipbud.apoiolabs.com/i/aX9f2Qk1bZ.png",
      "visibility": "public",
      "contentType": "image/png",
      "size": 48213,
      "createdAt": 1723852800000,
      "modifiedAt": 1723852800000
    }
  ],
  "nextBefore": 1723852800000
}

Paging: when nextBefore is a number, there may be more images, call again with ?before=<that number>. When it's null, you've reached the end.

About timestamps: createdAt and modifiedAt are Unix time in milliseconds (milliseconds since 1 Jan 1970). In JavaScript, new Date(createdAt) turns one into a readable date.

Get one image

GET /api/v1/images/{key}

Returns the details of a single image (the same fields as a list entry). Responds 404 if the key doesn't exist in this workspace.

curl "https://snipbud.apoiolabs.com/api/v1/images/aX9f2Qk1bZ.png" \
  -H "Authorization: Bearer sk_live_..."

Delete an image

DELETE /api/v1/images/{key}

Removes the image and breaks its link. This can't be undone. In a team, you can delete your own uploads; team admins and owners can delete anyone's.

curl -X DELETE "https://snipbud.apoiolabs.com/api/v1/images/aX9f2Qk1bZ.png" \
  -H "Authorization: Bearer sk_live_..."

Returns { "ok": true, "key": "…" } on success.

Rate limits

To keep the service fair and safe, requests are capped. When you hit a cap you get a 429 response with a Retry-After header telling you how many seconds to wait. There are three limits:

LimitWhat it means
UploadsUp to 300 uploads per minute on Max. Plenty for normal use.
Per keyUp to 600 requests per minute for each key, across all endpoints. This stops one key from overwhelming the service.
Failed loginsIf many requests from one network use a bad or missing key, that network is briefly blocked. This never affects a working key.

Good practice: when you see a 429, wait the number of seconds in Retry-After before trying again, rather than retrying immediately.

Errors

Every error is JSON with a human-readable error message and a standard HTTP status code:

{ "error": "Invalid or revoked API key." }
StatusMeaning & what to do
400Bad request, an unsupported image type, an empty body, or bytes that don't match the Content-Type you set. Check the file and header.
401Your key is missing, malformed, revoked, or expired. Check the Authorization header, or create a new key.
402This workspace isn't on Max (the plan lapsed, or the key predates a downgrade). Upgrade to use the API.
404No image with that key exists in this workspace. (You'll also get 404 for an image that belongs to someone else, we never confirm it exists.)
413The file is too big, over 100 MB, or over your plan's per-file limit.
429You hit a rate limit. Wait for the seconds in the Retry-After header, then retry.
500Something went wrong on our side. Retrying after a short wait is safe.

Keeping your keys safe

Questions or need something the API doesn't cover yet (larger files, webhooks)? Talk to us.