Media endpoint
Upload an image as base64 JSON or multipart, then use the returned URL as a featured image or in a gallery.
All paths are relative to https://urnextdoor.com/api/v1. See API reference for the envelope and headers every endpoint shares.
| Endpoint | What it does | Scope | Daily quota |
|---|---|---|---|
POST /media | Upload an image | media | media |
Upload an image
POST https://urnextdoor.com/api/v1/media
Uploads one image as JSON (base64) or multipart. It is converted to WebP and must end at 2 MB or less. Use data.url as featured_image or in gallery. Counts against the media quota.
- Auth: your API key (Bearer or X-API-Key)
- Scope:
media - Daily quota: counts as
media(see limits) - Idempotency-Key: supported (see Idempotency)
- operationId:
uploadMedia
Request body
JSON (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
image_base64 | string | Yes | The image as raw base64 or a data: URI (data:image/png;base64,...). PNG, JPEG, GIF or WebP, up to 8 MB before conversion. |
filename | string | No | A file name such as cover.png. Used to name the stored file. |
context | string | No | What the image is for. One of: post, question, poll, mcq, community, service, job, misc. Default: post. |
community | string | No | Optional community slug to file the image under. |
Multipart (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
file | string | Yes | The image file. |
context | string | No | What the image is for. One of: post, question, poll, mcq, community, service, job, misc. Default: post. |
community | string | No | Optional community slug to file the image under. |
Example
Base64 JSON (for AI generated images)
cURL
# Build the body in a file: a base64 image is too long for a command line argument.
printf '{"image_base64": "%s", "filename": "cover.png", "context": "post"}' \
"$(base64 < cover.png | tr -d '\n')" > upload.json
curl -X POST "https://urnextdoor.com/api/v1/media" \
-H "Authorization: Bearer $UND_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
--data-binary @upload.jsonJavaScript
import { readFile } from "node:fs/promises";
const image = await readFile("cover.png");
const res = await fetch("https://urnextdoor.com/api/v1/media", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.UND_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
image_base64: image.toString("base64"),
filename: "cover.png",
context: "post",
}),
});
console.log(res.status, await res.json());Python
import base64
import os
import uuid
import requests
api_key = os.environ["UND_API_KEY"]
with open("cover.png", "rb") as f:
image_base64 = base64.b64encode(f.read()).decode("ascii")
response = requests.post(
"https://urnextdoor.com/api/v1/media",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"image_base64": image_base64,
"filename": "cover.png",
"context": "post",
},
timeout=30,
)
print(response.status_code, response.json())PHP
<?php
$apiKey = getenv('UND_API_KEY');
$payload = [
'image_base64' => base64_encode(file_get_contents('cover.png')),
'filename' => 'cover.png',
'context' => 'post',
];
$ch = curl_init('https://urnextdoor.com/api/v1/media');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
echo $status, PHP_EOL, $body, PHP_EOL;Sample response: 201 Created
JSON
{
"ok": true,
"data": {
"id": 318,
"context": "post",
"community_id": null,
"url": "/storage/aarav/post/cover_3f9a1c2b7d4e_1792054800.webp",
"disk_path": "aarav/post/cover_3f9a1c2b7d4e_1792054800.webp",
"original_name": "cover.png",
"extension": "webp",
"mime_type": "image/webp",
"size_bytes": 184320,
"created_at": "2026-10-15 09:00:00",
"updated_at": "2026-10-15 09:00:00"
},
"web_url": "https://urnextdoor.com/storage/aarav/post/cover_3f9a1c2b7d4e_1792054800.webp",
"request_id": "9f8e7d6c5b4a3921"
}Multipart file upload
cURL
curl -X POST "https://urnextdoor.com/api/v1/media" \
-H "Authorization: Bearer $UND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F "file=@cover.png" \
-F 'context=post'JavaScript
import { openAsBlob } from "node:fs";
const form = new FormData();
form.append("file", await openAsBlob("cover.png", { type: "image/png" }), "cover.png");
form.append("context", "post");
const res = await fetch("https://urnextdoor.com/api/v1/media", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.UND_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
},
body: form,
});
console.log(res.status, await res.json());Python
import os
import uuid
import requests
api_key = os.environ["UND_API_KEY"]
response = requests.post(
"https://urnextdoor.com/api/v1/media",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
files={"file": ("cover.png", open("cover.png", "rb"), "image/png")},
data={
"context": "post",
},
timeout=30,
)
print(response.status_code, response.json())PHP
<?php
$apiKey = getenv('UND_API_KEY');
$ch = curl_init('https://urnextdoor.com/api/v1/media');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('cover.png', 'image/png'),
'context' => 'post',
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
echo $status, PHP_EOL, $body, PHP_EOL;Sample response: 201 Created
JSON
{
"ok": true,
"data": {
"id": 318,
"context": "post",
"community_id": null,
"url": "/storage/aarav/post/cover_3f9a1c2b7d4e_1792054800.webp",
"disk_path": "aarav/post/cover_3f9a1c2b7d4e_1792054800.webp",
"original_name": "cover.png",
"extension": "webp",
"mime_type": "image/webp",
"size_bytes": 184320,
"created_at": "2026-10-15 09:00:00",
"updated_at": "2026-10-15 09:00:00"
},
"web_url": "https://urnextdoor.com/storage/aarav/post/cover_3f9a1c2b7d4e_1792054800.webp",
"request_id": "9f8e7d6c5b4a3921"
}Responses
| Status | Meaning | Codes |
|---|---|---|
201 Created | Stored. | |
400 Bad Request | The body is not a JSON object, or the Idempotency-Key header is malformed. | invalid_json, invalid_idempotency_key |
401 Unauthorized | No key, or the key is unknown, malformed, revoked or expired, or the member is not active. | missing_api_key, invalid_api_key |
403 Forbidden | The key lacks the scope, or the member may not do this. | insufficient_scope, forbidden |
404 Not Found | Unknown route or item, or the item is not yours. | not_found |
409 Conflict | Conflicts with the current state, or the same Idempotency-Key is still being processed. | conflict, idempotency_in_progress |
413 Payload Too Large | The body is over the limit, or the image is over 8 MB. | payload_too_large |
415 Unsupported Media Type | Wrong Content-Type, or a file type that is not allowed. | unsupported_media_type |
421 Misdirected Request | Called on a community subdomain. Use https://urnextdoor.com. | wrong_host |
422 Unprocessable Content | Field errors, or an Idempotency-Key reused with a different body. | validation_failed, idempotency_key_reused |
429 Too Many Requests | Over 60 requests a minute from this member (or too many failed key checks from one address), or today's quota for this action is used up. | rate_limited, quota_exceeded |
500 Internal Server Error | Our fault. Safe to retry with the same Idempotency-Key. | server_error |
503 Service Unavailable | The API is switched off. | api_disabled |
Last updated