List a service in the Marketplace
List an Ur Next Door Marketplace service with the API: category lookup, service mode, location rules, up to 3 packages priced in rupees, and FAQs.
A service is a Marketplace listing that buyers can find and book: what you offer, where you offer it, and up to three priced packages. It takes a few lookups first, so work through the steps in order.
Endpoint: POST /api/v1/services · Scope: content (and read for the lookups) · Quota: service
Step 1: Pick a category
Get the category tree from GET /api/v1/service-categories. Top level groups contain children; use a child's id as category_id.
curl "https://urnextdoor.com/api/v1/service-categories" \
-H "Authorization: Bearer $UND_API_KEY"const res = await fetch("https://urnextdoor.com/api/v1/service-categories", {
headers: {
Authorization: `Bearer ${process.env.UND_API_KEY}`,
},
});
console.log(res.status, await res.json());import os
import requests
api_key = os.environ["UND_API_KEY"]
response = requests.get(
"https://urnextdoor.com/api/v1/service-categories",
headers={
"Authorization": f"Bearer {api_key}",
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = getenv('UND_API_KEY');
$ch = curl_init('https://urnextdoor.com/api/v1/service-categories');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
echo $status, PHP_EOL, $body, PHP_EOL;Sample response: 200 OK
{
"ok": true,
"categories": [
{
"id": 3,
"name": "Development",
"slug": "development",
"icon": null,
"description": "Websites, apps and software.",
"count": 42,
"children": [
{
"id": 12,
"name": "Web development",
"slug": "web-development",
"count": 17
}
]
}
],
"request_id": "9f8e7d6c5b4a3921"
}Step 2: Choose how you deliver
service_mode is one of:
| Value | Meaning | Location needed? |
|---|---|---|
online | Delivered remotely, anywhere | No |
in_person | At a place, in your service area | Yes |
both | Either | Yes |
If you leave service_mode out, it is both, so a location is required.
Step 3: Set the location (unless online)
For in_person and both, send:
country_code: an ISO country code such asIN.postal_code: a postal code that is valid for that country.- At least a
state_id. Adddistrict_idandcity_idto narrow it. If you send adistrict_id, also send acity_id. - Optionally
area(free text, up to 120 characters), for example a neighbourhood.
Get the ids from GET /api/v1/geo: level=states&country=IN, then level=districts&state=19, then level=cities&state=19&district=329. See Look up ids.
Step 4: Add packages and FAQs
Up to 3 packages. Each package needs a positive price in Indian rupees (send 4999 for 4,999 rupees, not paise). A package without a positive price is dropped.
| Package field | Notes |
|---|---|
name | Up to 120 characters. Defaults to Basic, Standard and Premium by position. |
description | Up to 500 characters. |
price | Rupees, greater than 0. |
delivery_days | 1 or more. Defaults to 1. |
revisions | Revisions included. -1 means unlimited. |
features | Up to 20 short lines, up to 200 characters each. |
Up to 20 faqs, each { "q": "...", "a": "..." } (question up to 200 characters, answer up to 1000).
Step 5: Create the service
An online service with two packages:
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"
}An in person service, scheduled for later:
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": "Home tutoring for class 10 maths",
"body": "<p>One to one sessions at your home, weekday evenings.</p>",
"category_id": 31,
"service_mode": "in_person",
"country_code": "IN",
"postal_code": "411001",
"state_id": 19,
"area": "Kothrud",
"packages": [
{
"name": "Monthly",
"price": 6000,
"delivery_days": 30,
"features": [
"12 sessions"
]
}
],
"scheduled_at": "2026-10-20T09:30:00Z"
}'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: "Home tutoring for class 10 maths",
body: "<p>One to one sessions at your home, weekday evenings.</p>",
category_id: 31,
service_mode: "in_person",
country_code: "IN",
postal_code: "411001",
state_id: 19,
area: "Kothrud",
packages: [
{
name: "Monthly",
price: 6000,
delivery_days: 30,
features: ["12 sessions"],
},
],
scheduled_at: "2026-10-20T09:30:00Z",
}),
});
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": "Home tutoring for class 10 maths",
"body": "<p>One to one sessions at your home, weekday evenings.</p>",
"category_id": 31,
"service_mode": "in_person",
"country_code": "IN",
"postal_code": "411001",
"state_id": 19,
"area": "Kothrud",
"packages": [
{
"name": "Monthly",
"price": 6000,
"delivery_days": 30,
"features": ["12 sessions"],
},
],
"scheduled_at": "2026-10-20T09:30:00Z",
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = getenv('UND_API_KEY');
$payload = [
'title' => 'Home tutoring for class 10 maths',
'body' => '<p>One to one sessions at your home, weekday evenings.</p>',
'category_id' => 31,
'service_mode' => 'in_person',
'country_code' => 'IN',
'postal_code' => '411001',
'state_id' => 19,
'area' => 'Kothrud',
'packages' => [
[
'name' => 'Monthly',
'price' => 6000,
'delivery_days' => 30,
'features' => ['12 sessions'],
],
],
'scheduled_at' => '2026-10-20T09:30:00Z',
];
$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",
"status": "scheduled",
"scheduled_at": "2026-10-20T09:30:00Z",
"item": {
"id": 9,
"type": "service",
"slug": "business-website-in-7-days"
},
"request_id": "9f8e7d6c5b4a3921"
}All fields
| 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. |
Common errors
| Status and code | Cause | Fix |
|---|---|---|
422 validation_failed on category_id | Missing or unknown category | Use an id from the lookup |
422 validation_failed on the location | No country, an invalid postal code, no state, or a district without a city | Follow step 3, or use service_mode: "online" |
403 forbidden | You are not an editor of that community | List it without a community |
429 quota_exceeded | Today's service quota is used up | Wait until 00:00 UTC |
Next steps
- Upload images for
featured_imageandgallery. - Post a job.
- Look up ids.
Last updated