Pixeldrive is agent storage: a write API (HTTP, SDK, MCP), originals on a CDN, and a JSON listing. Share a public project to render on the web. Pro adds image transforms. No CMS schema, no bucket wiring.
Same allowlist on the dashboard, POST /v1/upload, the SDK, and MCP. kind is derived from MIME — callers never send it. HTML, SVG, and JavaScript are rejected, including on private projects.
| kind | What | Accepts |
|---|---|---|
| image | Photos | image/* except SVG |
| video | Video | video/* |
| application/pdf | ||
| doc | Word | .doc, .docx |
| text | Markdown | text/markdown |
A project can tighten this list and the max file size in Settings → Uploads. It cannot raise the account plan cap. Keys inherit the project’s rules.
In the app, open a project and turn on Public API (project settings), or share a single folder from the folder view. Copy the link. It looks like:
GEThttps://api.pixeldrive.dev/v1/projects/{publicId}Private projects never publish a listing. Use a Bearer API key instead — see Sharing.
No API key for a shared public project. CORS is open. A 404 means the id is wrong, sharing is still off, or you need a Bearer key (unpublished public listing / private project).
fetchconst res = await fetch(
"https://api.pixeldrive.dev/v1/projects/YOUR_PUBLIC_ID",
);
if (!res.ok) {
throw new Error("Not found or listing unpublished");
}
const project = await res.json();
for (const file of project.files) {
console.log(file.name, file.kind, file.url);
}API keys live in project settings. Create a Write key in project settings. POST /v1/upload returns a presigned PUT (small files) or multipart URLs — never POST the file body to this API. Put-mode curl:
curlcurl -sS https://api.pixeldrive.dev/v1/upload \
-H "Authorization: Bearer pd_live_…" \
-H "Content-Type: application/json" \
-d '{"name":"hero.jpg","contentType":"image/jpeg","size":4821933}'
# PUT the bytes to upload.url (mode: put), then:
curl -sS https://api.pixeldrive.dev/v1/upload/FILE_PUBLIC_ID/complete \
-H "Authorization: Bearer pd_live_…" \
-H "Content-Type: application/json" \
-d '{}'TypeScript: npm i @pixeldrive-dev/sdk. upload() hides put vs multipart. Full client: SDK.
sdkimport { readFile } from "node:fs/promises";
import { Pixeldrive } from "@pixeldrive-dev/sdk";
const pd = new Pixeldrive({ apiKey: process.env.PIXELDRIVE_API_KEY });
const file = await pd.upload({
file: await readFile("./hero.jpg"),
name: "selects/hero.jpg",
contentType: "image/jpeg",
});
console.log(file.publicId, file.url);Agents: npx @pixeldrive-dev/mcp with PIXELDRIVE_API_KEY. Setup and tools: MCP. Path-in-filename, tags, and rate limits live on the API page. The project’s Upload rules apply to keys the same as the dashboard.
mcp.json{
"mcpServers": {
"pixeldrive": {
"command": "npx",
"args": ["-y", "@pixeldrive-dev/mcp"],
"env": {
"PIXELDRIVE_API_KEY": "pd_live_…"
}
}
}
}Each file has url (this exact version, cached for a year) and stableUrl (https://cdn.pixeldrive.dev/f/{publicId}, follows replaces). Use url in <img> for photos unless you need the latest version after a replace. Other kinds are files — link them; don’t drop a PDF into an image tag.
Pro listings include src / srcset when optimizable is true. Or pass stableUrl to a width-based loader — see File URLs. ?w= only resizes photos.
JSX{files.map((file) =>
file.kind === "image" ? (
<img
key={file.publicId}
src={file.url}
alt={file.name}
width={file.width}
height={file.height}
/>
) : (
<a key={file.publicId} href={file.stableUrl}>
{file.name}
</a>
),
)}?recursive=truetags). What you see in the app is what you pass to ?tag=la-jollakind, MIME, size, and for photos: width, height, optional blurhashnextCursor — up to 500 files per request; see the API