Media endpoint

Upload an image as base64 JSON or multipart, then use the returned URL as a featured image or in a gallery.

View as Markdown

All paths are relative to https://urnextdoor.com/api/v1. See API reference for the envelope and headers every endpoint shares.

EndpointWhat it doesScopeDaily quota
POST /mediaUpload an imagemediamedia

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)

FieldTypeRequiredDescription
image_base64stringYesThe image as raw base64 or a data: URI (data:image/png;base64,...). PNG, JPEG, GIF or WebP, up to 8 MB before conversion.
filenamestringNoA file name such as cover.png. Used to name the stored file.
contextstringNoWhat the image is for. One of: post, question, poll, mcq, community, service, job, misc. Default: post.
communitystringNoOptional community slug to file the image under.

Multipart (multipart/form-data)

FieldTypeRequiredDescription
filestringYesThe image file.
contextstringNoWhat the image is for. One of: post, question, poll, mcq, community, service, job, misc. Default: post.
communitystringNoOptional 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.json
JavaScript
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

StatusMeaningCodes
201 CreatedStored.
400 Bad RequestThe body is not a JSON object, or the Idempotency-Key header is malformed.invalid_json, invalid_idempotency_key
401 UnauthorizedNo key, or the key is unknown, malformed, revoked or expired, or the member is not active.missing_api_key, invalid_api_key
403 ForbiddenThe key lacks the scope, or the member may not do this.insufficient_scope, forbidden
404 Not FoundUnknown route or item, or the item is not yours.not_found
409 ConflictConflicts with the current state, or the same Idempotency-Key is still being processed.conflict, idempotency_in_progress
413 Payload Too LargeThe body is over the limit, or the image is over 8 MB.payload_too_large
415 Unsupported Media TypeWrong Content-Type, or a file type that is not allowed.unsupported_media_type
421 Misdirected RequestCalled on a community subdomain. Use https://urnextdoor.com.wrong_host
422 Unprocessable ContentField errors, or an Idempotency-Key reused with a different body.validation_failed, idempotency_key_reused
429 Too Many RequestsOver 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 ErrorOur fault. Safe to retry with the same Idempotency-Key.server_error
503 Service UnavailableThe API is switched off.api_disabled

Last updated