Appearance
Images
An image you store here belongs to your workspace and is addressed by the name you give it, e.g., company-logo, rather than a URL you have to keep serving. Papermill keeps the bytes, so a document does not depend on a file staying where you first put it.
A name is 3–64 characters of lowercase letters, digits and single hyphens. Names are pointers: storing under a name you already hold replaces the image, and anything that draws that name draws the new one from then on. A name that breaks the rules is rejected with message Invalid image name '<name>': <the rule it broke>.
The bytes decide the format, not the Content-Type you send. Papermill accepts PNG, JPEG, WebP and SVG; anything else is rejected whatever it was labelled. One image is limited to 50 MB, and a workspace to 1 GB across at most 1000 names.
Upload an image
PUT
/v2/images/{name}
Stores image bytes under a name in your workspace. Send the file as the request body, with Content-Type set to one of image/png, image/jpeg, image/webp, image/svg+xml. The bytes decide the format: a declared type that disagrees with them is ignored, and bytes that are none of those types are refused. A name is a pointer: storing under a name you already hold replaces the image, and every document referencing that name renders the new one from then on. One image is limited to 50 MB, and a workspace to 1 GB across at most 1000 names. An upload the workspace has no room for is refused, and the refusal names what you hold now.
Authorizations
BearerAuth
Your Papermill API key, sent as Authorization: Bearer <key>.
Type
HTTP (bearer)
or
ApiKeyHeader
Your Papermill API key. Equivalent to the bearer form.
Type
API Key (header: x-api-key)
Parameters
Path Parameters
name*
Lowercase letters, digits and single hyphens — company-logo.
Type
Requiredstring
Min Length
3Max Length
64Request Body
Format
"binary"Responses
The stored image
application/json
JSON "name": "string", "content_type": "string", "bytes": 0, "width": 0, "height": 0, "sha256": "string", "created_at": "string", "created_by": { "id": "string", "name": "string", "email": "string" }, "updated_at": "string", "updated_by": { "id": "string", "name": "string", "email": "string" }
{
}
Store an image from a URL or inline data
POST
/v2/images
Stores an image Papermill fetches for you, or one carried in the request as base64 — the same result as uploading the bytes, for callers that have a URL or cannot set a binary body. Give exactly one of source_url and data. A name is a pointer: storing under a name you already hold replaces the image, and every document referencing that name renders the new one from then on. One image is limited to 50 MB, and a workspace to 1 GB across at most 1000 names. An upload the workspace has no room for is refused, and the refusal names what you hold now.
Authorizations
BearerAuth
Your Papermill API key, sent as Authorization: Bearer <key>.
Type
HTTP (bearer)
or
ApiKeyHeader
Your Papermill API key. Equivalent to the bearer form.
Type
API Key (header: x-api-key)
Request Body
application/json
JSON "name": "string", "source_url": "string", "data": "string"
{
}
Responses
The stored image
application/json
JSON "name": "string", "content_type": "string", "bytes": 0, "width": 0, "height": 0, "sha256": "string", "created_at": "string", "created_by": { "id": "string", "name": "string", "email": "string" }, "updated_at": "string", "updated_by": { "id": "string", "name": "string", "email": "string" }
{
}
List stored images
GET
/v2/images
The images your workspace holds, ordered by name, alongside what they occupy in total. next_cursor is present exactly when there is another page; send it back as start_after to continue. usage covers the whole workspace, not the page.
Authorizations
BearerAuth
Your Papermill API key, sent as Authorization: Bearer <key>.
Type
HTTP (bearer)
or
ApiKeyHeader
Your Papermill API key. Equivalent to the bearer form.
Type
API Key (header: x-api-key)
Parameters
Query Parameters
limit
Images per page. Defaults to 100.
Type
integer
Minimum
1Maximum
1000start_after
The previous page’s next_cursor.
Type
string
Min Length
3Max Length
64Responses
Images held
application/json
JSON "images": [ { "name": "string", "content_type": "string", "bytes": 0, "width": 0, "height": 0, "sha256": "string", "created_at": "string", "created_by": { "id": "string", "name": "string", "email": "string" }, "updated_at": "string", "updated_by": { "id": "string", "name": "string", "email": "string" } } ], "next_cursor": "string", "usage": { "total_bytes": 0, "count": 0 }
{
}
Read one image
GET
/v2/images/{name}
What a name holds: its format, size, dimensions and digest. The bytes themselves are not served back — reference the name from a document to draw the image.
Authorizations
BearerAuth
Your Papermill API key, sent as Authorization: Bearer <key>.
Type
HTTP (bearer)
or
ApiKeyHeader
Your Papermill API key. Equivalent to the bearer form.
Type
API Key (header: x-api-key)
Parameters
Path Parameters
name*
Lowercase letters, digits and single hyphens — company-logo.
Type
Requiredstring
Min Length
3Max Length
64Responses
The stored image
application/json
JSON "name": "string", "content_type": "string", "bytes": 0, "width": 0, "height": 0, "sha256": "string", "created_at": "string", "created_by": { "id": "string", "name": "string", "email": "string" }, "updated_at": "string", "updated_by": { "id": "string", "name": "string", "email": "string" }
{
}
Delete an image
DELETE
/v2/images/{name}
Removes an image and frees the space it occupied. Documents referencing the name stop finding an image, so delete a name only when nothing renders it.
Authorizations
BearerAuth
Your Papermill API key, sent as Authorization: Bearer <key>.
Type
HTTP (bearer)
or
ApiKeyHeader
Your Papermill API key. Equivalent to the bearer form.
Type
API Key (header: x-api-key)
Parameters
Path Parameters
name*
Lowercase letters, digits and single hyphens — company-logo.
Type
Requiredstring
Min Length
3Max Length
64Responses
Image deleted
Example
Store a logo, then check what your workspace holds.
shell
set -e
curl -fsS -X PUT "https://api.papermill.io/v2/images/company-logo" \
-H "Authorization: Bearer $PAPERMILL_API_KEY" \
-H "Content-Type: image/png" \
--data-binary @logo.png
curl -fsS "https://api.papermill.io/v2/images" \
-H "Authorization: Bearer $PAPERMILL_API_KEY"python
import os
import requests
key = os.environ["PAPERMILL_API_KEY"]
auth = {"Authorization": f"Bearer {key}"}
with open("logo.png", "rb") as f:
stored = requests.put(
"https://api.papermill.io/v2/images/company-logo",
headers={**auth, "Content-Type": "image/png"},
data=f.read(),
)
stored.raise_for_status()
print(stored.json()["bytes"], stored.json()["content_type"])
held = requests.get("https://api.papermill.io/v2/images", headers=auth)
held.raise_for_status()
print(held.json()["usage"])javascript
import fs from 'node:fs/promises'
const key = process.env.PAPERMILL_API_KEY
const auth = { Authorization: `Bearer ${key}` }
const stored = await fetch('https://api.papermill.io/v2/images/company-logo', {
method: 'PUT',
headers: { ...auth, 'Content-Type': 'image/png' },
body: await fs.readFile('logo.png'),
})
if (!stored.ok) throw new Error(`${stored.status} ${stored.statusText}`)
const held = await fetch('https://api.papermill.io/v2/images', { headers: auth })
if (!held.ok) throw new Error(`${held.status} ${held.statusText}`)
console.log((await held.json()).usage)Sending the same name again replaces the image and succeeds, so this is safe to repeat from a setup script.
When you have a URL instead of a file
POST /v2/images takes a source_url and downloads the image for you, which saves a round trip when the file already lives somewhere public. Only http and https URLs are fetched, and only addresses reachable from the public internet — a URL that resolves to a private network is refused.
The same request accepts data instead: the image as unwrapped base64, under its own cap — lower than the upload route's, because encoding grows the value by a third and the whole thing has to travel inside one JSON request. The exact figure is on the data field above. It exists for callers that cannot set a binary request body, and for assistants working from a file they already hold. Give exactly one of the two; a request carrying both is rejected with message Give either 'source_url' or 'data', not both.
Keeping track of what you hold
GET /v2/images returns a page of names alongside a usage object covering the whole workspace — total_bytes and count. When there is another page, next_cursor names where it starts; send it back as start_after.
An upload the workspace has no room for is rejected with a message naming what you hold now and the limit you are held to, so a script can report the position rather than only the failure. Deleting frees the space immediately.
Deleting
DELETE /v2/images/{name} removes the image and frees its space. Anything that draws the name stops finding an image, so delete a name only when nothing renders it. When the workspace holds no image of that name, Papermill rejects the request with message This workspace holds no image named '<name>'.