Content endpoints
Create posts, questions, polls, MCQs, communities, Marketplace services and jobs, now or at a scheduled time.
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 /posts | Create a post | content | post |
POST /questions | Ask a question | content | question |
POST /polls | Create a poll | content | poll |
POST /mcqs | Create an MCQ | content | mcq |
POST /communities | Create a community | content | community |
POST /services | List a service | content | service |
POST /jobs | Post a job | content | job |
Create a post
POST https://urnextdoor.com/api/v1/posts
Publishes a post now, or schedules it when scheduled_at is set. title and body (HTML) are required. Send an Idempotency-Key so a retry never posts twice. Counts against the post quota.
- Auth: your API key (Bearer or X-API-Key)
- Scope:
content - Daily quota: counts as
post(see limits) - Idempotency-Key: supported (see Idempotency)
- operationId:
createPost
Request body
Send Content-Type: application/json.
| 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. |
Example
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"
]
}'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());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
$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
{
"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"
}Responses
| Status | Meaning | Codes |
|---|---|---|
201 Created | Created. Published now, or scheduled when scheduled_at was sent. | |
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 |
Ask a question
POST https://urnextdoor.com/api/v1/questions
Publishes a question now, or schedules it when scheduled_at is set. Only title is required; body (HTML) adds details. Counts against the question quota.
- Auth: your API key (Bearer or X-API-Key)
- Scope:
content - Daily quota: counts as
question(see limits) - Idempotency-Key: supported (see Idempotency)
- operationId:
createQuestion
Request body
Send Content-Type: application/json.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | The question itself (plain text). |
body | string | No | Optional details as HTML. |
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. |
Example
curl -X POST "https://urnextdoor.com/api/v1/questions" \
-H "Authorization: Bearer $UND_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"title": "Which plants survive a north facing balcony?",
"body": "<p>We get about two hours of direct sun. What has worked for you?</p>",
"tags": [
"gardening"
]
}'const res = await fetch("https://urnextdoor.com/api/v1/questions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.UND_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
title: "Which plants survive a north facing balcony?",
body: "<p>We get about two hours of direct sun. What has worked for you?</p>",
tags: ["gardening"],
}),
});
console.log(res.status, await res.json());import os
import uuid
import requests
api_key = os.environ["UND_API_KEY"]
response = requests.post(
"https://urnextdoor.com/api/v1/questions",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"title": "Which plants survive a north facing balcony?",
"body": "<p>We get about two hours of direct sun. What has worked for you?</p>",
"tags": ["gardening"],
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = getenv('UND_API_KEY');
$payload = [
'title' => 'Which plants survive a north facing balcony?',
'body' => '<p>We get about two hours of direct sun. What has worked for you?</p>',
'tags' => ['gardening'],
];
$ch = curl_init('https://urnextdoor.com/api/v1/questions');
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
{
"ok": true,
"id": 52,
"slug": "which-plants-survive-a-north-facing-balcony",
"url": "/questions/which-plants-survive-a-north-facing-balcony",
"web_url": "https://urnextdoor.com/questions/which-plants-survive-a-north-facing-balcony",
"item": {
"id": 52,
"type": "question",
"slug": "which-plants-survive-a-north-facing-balcony"
},
"request_id": "9f8e7d6c5b4a3921"
}Responses
| Status | Meaning | Codes |
|---|---|---|
201 Created | Created. Published now, or scheduled when scheduled_at was sent. | |
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 |
Create a poll
POST https://urnextdoor.com/api/v1/polls
Publishes a poll now, or schedules it. title and options (2 to 5 unique strings) are required. Counts against the poll quota.
- Auth: your API key (Bearer or X-API-Key)
- Scope:
content - Daily quota: counts as
poll(see limits) - Idempotency-Key: supported (see Idempotency)
- operationId:
createPoll
Request body
Send Content-Type: application/json.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | The poll question (plain text). |
options | array of string | Yes | The answers people vote on: 2 to 5 unique strings. 2 to 5 items. Items must be unique. |
body | string | No | Optional context as HTML. |
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. |
Example
curl -X POST "https://urnextdoor.com/api/v1/polls" \
-H "Authorization: Bearer $UND_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"title": "When should the weekend cleanup start?",
"options": [
"7 am",
"8 am",
"9 am"
],
"tags": [
"neighbourhood"
]
}'const res = await fetch("https://urnextdoor.com/api/v1/polls", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.UND_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
title: "When should the weekend cleanup start?",
options: ["7 am", "8 am", "9 am"],
tags: ["neighbourhood"],
}),
});
console.log(res.status, await res.json());import os
import uuid
import requests
api_key = os.environ["UND_API_KEY"]
response = requests.post(
"https://urnextdoor.com/api/v1/polls",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"title": "When should the weekend cleanup start?",
"options": ["7 am", "8 am", "9 am"],
"tags": ["neighbourhood"],
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = getenv('UND_API_KEY');
$payload = [
'title' => 'When should the weekend cleanup start?',
'options' => ['7 am', '8 am', '9 am'],
'tags' => ['neighbourhood'],
];
$ch = curl_init('https://urnextdoor.com/api/v1/polls');
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
{
"ok": true,
"id": 18,
"slug": "when-should-the-weekend-cleanup-start",
"url": "/polls/when-should-the-weekend-cleanup-start",
"web_url": "https://urnextdoor.com/polls/when-should-the-weekend-cleanup-start",
"item": {
"id": 18,
"type": "poll",
"slug": "when-should-the-weekend-cleanup-start"
},
"request_id": "9f8e7d6c5b4a3921"
}Responses
| Status | Meaning | Codes |
|---|---|---|
201 Created | Created. Published now, or scheduled when scheduled_at was sent. | |
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 |
Create an MCQ
POST https://urnextdoor.com/api/v1/mcqs
Publishes a multiple choice quiz question now, or schedules it. title, options (2 to 5) and correct_option_index (0-based) are required. Counts against the mcq quota.
- Auth: your API key (Bearer or X-API-Key)
- Scope:
content - Daily quota: counts as
mcq(see limits) - Idempotency-Key: supported (see Idempotency)
- operationId:
createMcq
Request body
Send Content-Type: application/json.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | The quiz question (plain text). |
options | array of string | Yes | The choices: 2 to 5 unique strings. 2 to 5 items. Items must be unique. |
correct_option_index | integer | Yes | The 0-based position of the correct choice in options. From 0 to 4. |
body | string | No | Optional context or explanation as HTML. |
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. |
Example
curl -X POST "https://urnextdoor.com/api/v1/mcqs" \
-H "Authorization: Bearer $UND_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"title": "Which planet has the shortest year?",
"options": [
"Venus",
"Mercury",
"Mars",
"Earth"
],
"correct_option_index": 1,
"tags": [
"science"
]
}'const res = await fetch("https://urnextdoor.com/api/v1/mcqs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.UND_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
title: "Which planet has the shortest year?",
options: ["Venus", "Mercury", "Mars", "Earth"],
correct_option_index: 1,
tags: ["science"],
}),
});
console.log(res.status, await res.json());import os
import uuid
import requests
api_key = os.environ["UND_API_KEY"]
response = requests.post(
"https://urnextdoor.com/api/v1/mcqs",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"title": "Which planet has the shortest year?",
"options": ["Venus", "Mercury", "Mars", "Earth"],
"correct_option_index": 1,
"tags": ["science"],
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = getenv('UND_API_KEY');
$payload = [
'title' => 'Which planet has the shortest year?',
'options' => ['Venus', 'Mercury', 'Mars', 'Earth'],
'correct_option_index' => 1,
'tags' => ['science'],
];
$ch = curl_init('https://urnextdoor.com/api/v1/mcqs');
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
{
"ok": true,
"id": 77,
"slug": "which-planet-has-the-shortest-year",
"url": "/mcqs/which-planet-has-the-shortest-year",
"web_url": "https://urnextdoor.com/mcqs/which-planet-has-the-shortest-year",
"item": {
"id": 77,
"type": "mcq",
"slug": "which-planet-has-the-shortest-year"
},
"request_id": "9f8e7d6c5b4a3921"
}Responses
| Status | Meaning | Codes |
|---|---|---|
201 Created | Created. Published now, or scheduled when scheduled_at was sent. | |
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 |
Create a community
POST https://urnextdoor.com/api/v1/communities
Creates a community now (201), or schedules its creation (202) when scheduled_at is set. name must be unique. A scheduled creation fails later if the name is taken by then. Counts against the community quota.
- Auth: your API key (Bearer or X-API-Key)
- Scope:
content - Daily quota: counts as
community(see limits) - Idempotency-Key: supported (see Idempotency)
- operationId:
createCommunity
Request body
Send Content-Type: application/json.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The community name. It must be unique. |
description | string | No | What the community is about, as HTML. |
visibility | string | No | Who can see and join it. One of: public, ask_to_join, private. |
icon_url | string | No | One of the paths returned by GET /community-icons. |
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. |
Example
curl -X POST "https://urnextdoor.com/api/v1/communities" \
-H "Authorization: Bearer $UND_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "Morning Runners Pune",
"description": "<p>Early runs, routes and race plans around Pune.</p>",
"visibility": "public",
"icon_url": "/avatars/community/running.png"
}'const res = await fetch("https://urnextdoor.com/api/v1/communities", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.UND_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
name: "Morning Runners Pune",
description: "<p>Early runs, routes and race plans around Pune.</p>",
visibility: "public",
icon_url: "/avatars/community/running.png",
}),
});
console.log(res.status, await res.json());import os
import uuid
import requests
api_key = os.environ["UND_API_KEY"]
response = requests.post(
"https://urnextdoor.com/api/v1/communities",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"name": "Morning Runners Pune",
"description": "<p>Early runs, routes and race plans around Pune.</p>",
"visibility": "public",
"icon_url": "/avatars/community/running.png",
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = getenv('UND_API_KEY');
$payload = [
'name' => 'Morning Runners Pune',
'description' => '<p>Early runs, routes and race plans around Pune.</p>',
'visibility' => 'public',
'icon_url' => '/avatars/community/running.png',
];
$ch = curl_init('https://urnextdoor.com/api/v1/communities');
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
{
"ok": true,
"id": 5,
"slug": "morning-runners-pune",
"url": "/communities/morning-runners-pune",
"web_url": "https://urnextdoor.com/communities/morning-runners-pune",
"item": {
"id": 5,
"type": "community",
"slug": "morning-runners-pune",
"name": "Morning Runners Pune"
},
"request_id": "9f8e7d6c5b4a3921"
}Responses
| Status | Meaning | Codes |
|---|---|---|
201 Created | Created now. | |
202 Accepted | Accepted: the community will be created at scheduled_at. id is the scheduled-creation id. | |
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 |
List a service
POST https://urnextdoor.com/api/v1/services
Publishes a Marketplace service now, or schedules it. title, body and category_id are required, plus a location (country_code, postal_code, state_id) unless service_mode is online. Prices are in rupees.
- Auth: your API key (Bearer or X-API-Key)
- Scope:
content - Daily quota: counts as
service(see limits) - Idempotency-Key: supported (see Idempotency)
- operationId:
createService
Request body
Send Content-Type: application/json.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | The service title (plain text). |
body | string | Yes | The full description as HTML. |
category_id | integer | Yes | A category id from GET /service-categories. |
summary | string | No | A short plain text summary shown on cards. Up to 300 characters. |
service_mode | string | No | How you deliver. Unless it is online, a location is required. One of: online, in_person, both. Default: both. |
country_code | string | No | ISO 3166-1 alpha-2 country, for example IN. Required unless service_mode is online. |
postal_code | string | No | Postal code, valid for the country. Required unless service_mode is online. |
state_id | integer | No | From GET /geo?level=states. At least a state (or a district or city) is required unless service_mode is online. |
district_id | integer | No | From GET /geo?level=districts. If you send a district, also send a city. |
city_id | integer | No | From GET /geo?level=cities. |
area | string | No | Neighbourhood or area, free text. Up to 120 characters. |
packages | array of object | No | Up to 3 priced packages. Up to 3 items. |
packages[].name | string | No | Package name. Defaults to Basic, Standard or Premium by position. Up to 120 characters. |
packages[].description | string | No | What the package includes. Up to 500 characters. |
packages[].price | number | Yes | Price in Indian rupees, for example 4999. A package without a positive price is skipped. |
packages[].delivery_days | integer | No | Days to deliver. Defaults to 1. |
packages[].revisions | integer | No | Revisions included. -1 means unlimited. |
packages[].features | array of string | No | Short feature lines, up to 20. Up to 20 items. |
faqs | array of object | No | Up to 20 questions and answers. Up to 20 items. |
faqs[].q | string | Yes | The question. Up to 200 characters. |
faqs[].a | string | Yes | The answer. Up to 1000 characters. |
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. |
Example
curl -X POST "https://urnextdoor.com/api/v1/services" \
-H "Authorization: Bearer $UND_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"title": "Business website in 7 days",
"body": "<p>I build fast, mobile friendly websites for small shops and clinics.</p><ul><li>Design and build</li><li>Hosting setup</li></ul>",
"category_id": 12,
"summary": "A fast, mobile friendly website for your shop or clinic.",
"service_mode": "online",
"packages": [
{
"name": "Starter",
"description": "One page site",
"price": 4999,
"delivery_days": 5,
"revisions": 2,
"features": [
"1 page",
"Contact form"
]
},
{
"name": "Business",
"description": "Up to 5 pages",
"price": 14999,
"delivery_days": 7,
"revisions": -1,
"features": [
"5 pages",
"Blog",
"Basic SEO"
]
}
],
"faqs": [
{
"q": "Do you provide hosting?",
"a": "I set it up on your account, so you own it."
}
],
"tags": [
"web-design"
]
}'const res = await fetch("https://urnextdoor.com/api/v1/services", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.UND_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
title: "Business website in 7 days",
body: "<p>I build fast, mobile friendly websites for small shops and clinics.</p><ul><li>Design and build</li><li>Hosting setup</li></ul>",
category_id: 12,
summary: "A fast, mobile friendly website for your shop or clinic.",
service_mode: "online",
packages: [
{
name: "Starter",
description: "One page site",
price: 4999,
delivery_days: 5,
revisions: 2,
features: ["1 page", "Contact form"],
},
{
name: "Business",
description: "Up to 5 pages",
price: 14999,
delivery_days: 7,
revisions: -1,
features: ["5 pages", "Blog", "Basic SEO"],
},
],
faqs: [
{
q: "Do you provide hosting?",
a: "I set it up on your account, so you own it.",
},
],
tags: ["web-design"],
}),
});
console.log(res.status, await res.json());import os
import uuid
import requests
api_key = os.environ["UND_API_KEY"]
response = requests.post(
"https://urnextdoor.com/api/v1/services",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"title": "Business website in 7 days",
"body": "<p>I build fast, mobile friendly websites for small shops and clinics.</p><ul><li>Design and build</li><li>Hosting setup</li></ul>",
"category_id": 12,
"summary": "A fast, mobile friendly website for your shop or clinic.",
"service_mode": "online",
"packages": [
{
"name": "Starter",
"description": "One page site",
"price": 4999,
"delivery_days": 5,
"revisions": 2,
"features": ["1 page", "Contact form"],
},
{
"name": "Business",
"description": "Up to 5 pages",
"price": 14999,
"delivery_days": 7,
"revisions": -1,
"features": ["5 pages", "Blog", "Basic SEO"],
},
],
"faqs": [
{
"q": "Do you provide hosting?",
"a": "I set it up on your account, so you own it.",
},
],
"tags": ["web-design"],
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = getenv('UND_API_KEY');
$payload = [
'title' => 'Business website in 7 days',
'body' => '<p>I build fast, mobile friendly websites for small shops and clinics.</p><ul><li>Design and build</li><li>Hosting setup</li></ul>',
'category_id' => 12,
'summary' => 'A fast, mobile friendly website for your shop or clinic.',
'service_mode' => 'online',
'packages' => [
[
'name' => 'Starter',
'description' => 'One page site',
'price' => 4999,
'delivery_days' => 5,
'revisions' => 2,
'features' => ['1 page', 'Contact form'],
],
[
'name' => 'Business',
'description' => 'Up to 5 pages',
'price' => 14999,
'delivery_days' => 7,
'revisions' => -1,
'features' => ['5 pages', 'Blog', 'Basic SEO'],
],
],
'faqs' => [
[
'q' => 'Do you provide hosting?',
'a' => 'I set it up on your account, so you own it.',
],
],
'tags' => ['web-design'],
];
$ch = curl_init('https://urnextdoor.com/api/v1/services');
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
{
"ok": true,
"id": 9,
"slug": "business-website-in-7-days",
"url": "/marketplace/business-website-in-7-days",
"web_url": "https://urnextdoor.com/marketplace/business-website-in-7-days",
"item": {
"id": 9,
"type": "service",
"slug": "business-website-in-7-days"
},
"request_id": "9f8e7d6c5b4a3921"
}Responses
| Status | Meaning | Codes |
|---|---|---|
201 Created | Created. Published now, or scheduled when scheduled_at was sent. | |
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 |
Post a job
POST https://urnextdoor.com/api/v1/jobs
Publishes a job now, or schedules it. title, body, employment_type, work_mode and experience_level are required (values from GET /job-options). Jobs take featured_image only, no gallery.
- Auth: your API key (Bearer or X-API-Key)
- Scope:
content - Daily quota: counts as
job(see limits) - Idempotency-Key: supported (see Idempotency)
- operationId:
createJob
Request body
Send Content-Type: application/json.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | The job title (plain text). |
body | string | Yes | The job description as HTML. |
employment_type | string | Yes | One of employment_types from GET /job-options, for example full_time. |
work_mode | string | Yes | One of work_modes from GET /job-options, for example remote. Location fields are ignored when it is remote. |
experience_level | string | Yes | One of experience_levels from GET /job-options, for example mid. |
openings | integer | No | Number of openings. From 1 to 999. |
salary_min | number | No | Lowest salary in currency, per salary_period. |
salary_max | number | No | Highest salary in currency, per salary_period. |
salary_period | string | No | One of salary_periods from GET /job-options, for example yearly. |
currency | string | No | Three letter currency code. Pattern: ^[A-Z]{3}$. Default: INR. |
application_deadline | string (date) | No | Last day to apply, YYYY-MM-DD. |
summary | string | No | A short plain text summary shown on cards. Up to 300 characters. |
country_code | string | No | ISO 3166-1 alpha-2 country. Ignored when work_mode is remote. |
state_id | integer | No | From GET /geo. Ignored when work_mode is remote. |
district_id | integer | No | From GET /geo. Ignored when work_mode is remote. |
city_id | integer | No | From GET /geo. Ignored when work_mode is remote. |
area | string | No | Neighbourhood or area, free text. Ignored when work_mode is remote. |
featured_image | string | No | A /storage/... path returned by POST /media (data.url), or any https:// image URL (linked, not copied). |
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. |
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. |
Example
curl -X POST "https://urnextdoor.com/api/v1/jobs" \
-H "Authorization: Bearer $UND_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"title": "Backend developer (PHP)",
"body": "<p>Join a small team building tools for local shops.</p><ul><li>3+ years with PHP or Node.js</li><li>Comfortable with MySQL</li></ul>",
"employment_type": "full_time",
"work_mode": "remote",
"experience_level": "mid",
"openings": 2,
"salary_min": 900000,
"salary_max": 1400000,
"salary_period": "yearly",
"currency": "INR",
"application_deadline": "2026-11-30",
"summary": "Remote, full time, PHP and MySQL.",
"tags": [
"php",
"hiring"
]
}'const res = await fetch("https://urnextdoor.com/api/v1/jobs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.UND_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
title: "Backend developer (PHP)",
body: "<p>Join a small team building tools for local shops.</p><ul><li>3+ years with PHP or Node.js</li><li>Comfortable with MySQL</li></ul>",
employment_type: "full_time",
work_mode: "remote",
experience_level: "mid",
openings: 2,
salary_min: 900000,
salary_max: 1400000,
salary_period: "yearly",
currency: "INR",
application_deadline: "2026-11-30",
summary: "Remote, full time, PHP and MySQL.",
tags: ["php", "hiring"],
}),
});
console.log(res.status, await res.json());import os
import uuid
import requests
api_key = os.environ["UND_API_KEY"]
response = requests.post(
"https://urnextdoor.com/api/v1/jobs",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"title": "Backend developer (PHP)",
"body": "<p>Join a small team building tools for local shops.</p><ul><li>3+ years with PHP or Node.js</li><li>Comfortable with MySQL</li></ul>",
"employment_type": "full_time",
"work_mode": "remote",
"experience_level": "mid",
"openings": 2,
"salary_min": 900000,
"salary_max": 1400000,
"salary_period": "yearly",
"currency": "INR",
"application_deadline": "2026-11-30",
"summary": "Remote, full time, PHP and MySQL.",
"tags": ["php", "hiring"],
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = getenv('UND_API_KEY');
$payload = [
'title' => 'Backend developer (PHP)',
'body' => '<p>Join a small team building tools for local shops.</p><ul><li>3+ years with PHP or Node.js</li><li>Comfortable with MySQL</li></ul>',
'employment_type' => 'full_time',
'work_mode' => 'remote',
'experience_level' => 'mid',
'openings' => 2,
'salary_min' => 900000,
'salary_max' => 1400000,
'salary_period' => 'yearly',
'currency' => 'INR',
'application_deadline' => '2026-11-30',
'summary' => 'Remote, full time, PHP and MySQL.',
'tags' => ['php', 'hiring'],
];
$ch = curl_init('https://urnextdoor.com/api/v1/jobs');
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
{
"ok": true,
"id": 23,
"slug": "backend-developer-php",
"url": "/jobs/backend-developer-php",
"web_url": "https://urnextdoor.com/jobs/backend-developer-php",
"item": {
"id": 23,
"type": "job",
"slug": "backend-developer-php"
},
"request_id": "9f8e7d6c5b4a3921"
}Responses
| Status | Meaning | Codes |
|---|---|---|
201 Created | Created. Published now, or scheduled when scheduled_at was sent. | |
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