# Create a post

> Publish an Ur Next Door post with the API: title, HTML body, tags, a featured image, a community, and an optional schedule, with examples in four languages.

Source: https://docs.urnextdoor.com/guides/create-post/

A post is an article or an update: a title, an HTML body, and optional tags and images. It appears in the feed, on your profile, and in a community if you choose one.

**Endpoint:** `POST /api/v1/posts` · **Scope:** `content` · **Quota:** `post`

## Fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | string | Yes | The post title (plain text). |
| `body` | string | Yes | The post body as HTML. See the Formatting guide for allowed tags. |
| `tags` | array of string | No | Topic tags, without the leading #. Example: ["water", "home"]. |
| `community_id` | integer | No | Post inside this community. Use an id from GET /communities/postable. You must be an editor (or above) of it. |
| `community` | string | No | Alternative to community_id: the community slug. It is resolved to its id. |
| `featured_image` | string | No | A /storage/... path returned by POST /media (data.url), or any https:// image URL (linked, not copied). |
| `gallery` | array of string | No | Extra images. Each is a /storage/... path from POST /media or an https:// image URL. Up to 12 items. |
| `scheduled_at` | string (date-time) | No | Publish later instead of now. ISO 8601 with an offset or Z, at least 5 minutes and at most 180 days ahead. Stored in UTC. |

## Publish a post now

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/posts" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "title": "Five easy ways to save water at home",
  "body": "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
  "tags": [
    "water",
    "home"
  ]
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    title: "Five easy ways to save water at home",
    body: "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
    tags: ["water", "home"],
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/posts",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "title": "Five easy ways to save water at home",
        "body": "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
        "tags": ["water", "home"],
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'title' => 'Five easy ways to save water at home',
    'body' => '<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>',
    'tags' => ['water', 'home'],
];

$ch = curl_init('https://urnextdoor.com/api/v1/posts');
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,
  "id": 41,
  "slug": "five-easy-ways-to-save-water-at-home",
  "url": "/posts/five-easy-ways-to-save-water-at-home",
  "web_url": "https://urnextdoor.com/posts/five-easy-ways-to-save-water-at-home",
  "item": {
    "id": 41,
    "type": "post",
    "slug": "five-easy-ways-to-save-water-at-home"
  },
  "request_id": "9f8e7d6c5b4a3921"
}
```

Show the member the `web_url`: that is where the post lives.

## Post inside a community

Add `community_id` (a number) or `community` (the community's slug). You must be an editor, admin or creator of that community, otherwise the request answers `403 forbidden`.

Get the list of communities you can post in from `GET /api/v1/communities/postable` (scope `read`). Each item has an `id` for `community_id` and a `slug` for `community`. See [Look up ids](https://docs.urnextdoor.com/guides/lookups.md).

```json
{
  "title": "Saturday route: Vetal Hill loop",
  "body": "<p>Meet at 6:15 at the gate. About 8 km, easy pace.</p>",
  "community": "morning-runners-pune"
}
```

An unknown slug answers `422` with an error on the `community` field.

## Add images

1. Upload the image with `POST /api/v1/media` (scope `media`) and read `data.url` from the response, a path like `/storage/aarav/post/cover_...webp`.
2. Send that path as `featured_image`. Add more images (up to 12) in `gallery`.

You can also pass any `https://` image URL. It is linked, not copied, so it disappears if its host removes it.

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/posts" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "title": "Five easy ways to save water at home",
  "body": "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
  "featured_image": "/storage/aarav/post/cover_3f9a1c2b7d4e_1792054800.webp",
  "tags": [
    "water",
    "home"
  ]
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    title: "Five easy ways to save water at home",
    body: "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
    featured_image: "/storage/aarav/post/cover_3f9a1c2b7d4e_1792054800.webp",
    tags: ["water", "home"],
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/posts",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "title": "Five easy ways to save water at home",
        "body": "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
        "featured_image": "/storage/aarav/post/cover_3f9a1c2b7d4e_1792054800.webp",
        "tags": ["water", "home"],
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'title' => 'Five easy ways to save water at home',
    'body' => '<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>',
    'featured_image' => '/storage/aarav/post/cover_3f9a1c2b7d4e_1792054800.webp',
    'tags' => ['water', 'home'],
];

$ch = curl_init('https://urnextdoor.com/api/v1/posts');
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,
  "id": 41,
  "slug": "five-easy-ways-to-save-water-at-home",
  "url": "/posts/five-easy-ways-to-save-water-at-home",
  "web_url": "https://urnextdoor.com/posts/five-easy-ways-to-save-water-at-home",
  "item": {
    "id": 41,
    "type": "post",
    "slug": "five-easy-ways-to-save-water-at-home"
  },
  "request_id": "9f8e7d6c5b4a3921"
}
```

Full details, including AI generated images, are in [Upload images](https://docs.urnextdoor.com/guides/upload-images.md).

## Schedule it

Add `scheduled_at` in UTC, at least 5 minutes and at most 180 days ahead. The response adds `"status": "scheduled"` and the time it goes live.

**cURL**

```bash
curl -X POST "https://urnextdoor.com/api/v1/posts" \
  -H "Authorization: Bearer $UND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "title": "Five easy ways to save water at home",
  "body": "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
  "tags": [
    "water",
    "home"
  ],
  "scheduled_at": "2026-10-20T09:30:00Z"
}'
```

**JavaScript**

```js
const res = await fetch("https://urnextdoor.com/api/v1/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.UND_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    title: "Five easy ways to save water at home",
    body: "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
    tags: ["water", "home"],
    scheduled_at: "2026-10-20T09:30:00Z",
  }),
});
console.log(res.status, await res.json());
```

**Python**

```python
import os
import uuid

import requests

api_key = os.environ["UND_API_KEY"]

response = requests.post(
    "https://urnextdoor.com/api/v1/posts",
    headers={
        "Authorization": f"Bearer {api_key}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "title": "Five easy ways to save water at home",
        "body": "<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>",
        "tags": ["water", "home"],
        "scheduled_at": "2026-10-20T09:30:00Z",
    },
    timeout=30,
)
print(response.status_code, response.json())
```

**PHP**

```php
<?php
$apiKey = getenv('UND_API_KEY');

$payload = [
    'title' => 'Five easy ways to save water at home',
    'body' => '<p>Small habits add up. These five saved our building a lot of water this summer.</p><ul><li>Fix dripping taps the same week.</li><li>Run the washing machine only when it is full.</li></ul>',
    'tags' => ['water', 'home'],
    'scheduled_at' => '2026-10-20T09:30:00Z',
];

$ch = curl_init('https://urnextdoor.com/api/v1/posts');
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,
  "id": 41,
  "slug": "five-easy-ways-to-save-water-at-home",
  "url": "/posts/five-easy-ways-to-save-water-at-home",
  "web_url": "https://urnextdoor.com/posts/five-easy-ways-to-save-water-at-home",
  "status": "scheduled",
  "scheduled_at": "2026-10-20T09:30:00Z",
  "item": {
    "id": 41,
    "type": "post",
    "slug": "five-easy-ways-to-save-water-at-home"
  },
  "request_id": "9f8e7d6c5b4a3921"
}
```

See [Schedule content](https://docs.urnextdoor.com/guides/schedule-content.md).

## Write the body

The body is HTML. Use `<p>` for paragraphs, `<h2>` to `<h4>` for headings, `<ul>`/`<ol>` for lists, `<a href>` for links, `<blockquote>`, `<code>` and `<pre>`, `<img>` and YouTube embeds. Anything else is cleaned out. Typing `#topic` turns into a tag link and `@username` into a mention. See [Format content with HTML](https://docs.urnextdoor.com/guides/formatting.md).

If your text is Markdown, convert it to HTML before you send it. Markdown bodies are on the roadmap but not supported yet.

## Common errors

| Status and code | Cause | Fix |
| --- | --- | --- |
| `422 validation_failed` | `title` or `body` is missing or empty | Send both; the `errors` object names the field |
| `422 validation_failed` with `scheduled_at` | The time is in the past, under 5 minutes or over 180 days ahead | Pick a time in the window, in UTC |
| `403 forbidden` | You are not an editor of that community | Post without a community, or ask its admins |
| `429 quota_exceeded` | Today's post quota is used up | Wait until 00:00 UTC, see `Retry-After` |

## Next steps

- [Ask a question](https://docs.urnextdoor.com/guides/create-question.md) or [create a poll](https://docs.urnextdoor.com/guides/create-poll.md).
- [Upload images](https://docs.urnextdoor.com/guides/upload-images.md).
- Automate it with [an AI agent](https://docs.urnextdoor.com/ai.md).
