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.

View as Markdown

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
curl "https://urnextdoor.com/api/v1/service-categories" \
  -H "Authorization: Bearer $UND_API_KEY"
JavaScript
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());
Python
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
<?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

JSON
{
  "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:

ValueMeaningLocation needed?
onlineDelivered remotely, anywhereNo
in_personAt a place, in your service areaYes
bothEitherYes

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 as IN.
  • postal_code: a postal code that is valid for that country.
  • At least a state_id. Add district_id and city_id to narrow it. If you send a district_id, also send a city_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 fieldNotes
nameUp to 120 characters. Defaults to Basic, Standard and Premium by position.
descriptionUp to 500 characters.
priceRupees, greater than 0.
delivery_days1 or more. Defaults to 1.
revisionsRevisions included. -1 means unlimited.
featuresUp 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
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"
  ]
}'
JavaScript
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());
Python
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
<?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

JSON
{
  "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
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"
}'
JavaScript
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());
Python
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
<?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

JSON
{
  "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

FieldTypeRequiredDescription
titlestringYesThe service title (plain text).
bodystringYesThe full description as HTML.
category_idintegerYesA category id from GET /service-categories.
summarystringNoA short plain text summary shown on cards. Up to 300 characters.
service_modestringNoHow you deliver. Unless it is online, a location is required. One of: online, in_person, both. Default: both.
country_codestringNoISO 3166-1 alpha-2 country, for example IN. Required unless service_mode is online.
postal_codestringNoPostal code, valid for the country. Required unless service_mode is online.
state_idintegerNoFrom GET /geo?level=states. At least a state (or a district or city) is required unless service_mode is online.
district_idintegerNoFrom GET /geo?level=districts. If you send a district, also send a city.
city_idintegerNoFrom GET /geo?level=cities.
areastringNoNeighbourhood or area, free text. Up to 120 characters.
packagesarray of objectNoUp to 3 priced packages. Up to 3 items.
packages[].namestringNoPackage name. Defaults to Basic, Standard or Premium by position. Up to 120 characters.
packages[].descriptionstringNoWhat the package includes. Up to 500 characters.
packages[].pricenumberYesPrice in Indian rupees, for example 4999. A package without a positive price is skipped.
packages[].delivery_daysintegerNoDays to deliver. Defaults to 1.
packages[].revisionsintegerNoRevisions included. -1 means unlimited.
packages[].featuresarray of stringNoShort feature lines, up to 20. Up to 20 items.
faqsarray of objectNoUp to 20 questions and answers. Up to 20 items.
faqs[].qstringYesThe question. Up to 200 characters.
faqs[].astringYesThe answer. Up to 1000 characters.
tagsarray of stringNoTopic tags, without the leading #. Example: ["water", "home"].
community_idintegerNoPost inside this community. Use an id from GET /communities/postable. You must be an editor (or above) of it.
communitystringNoAlternative to community_id: the community slug. It is resolved to its id.
featured_imagestringNoA /storage/... path returned by POST /media (data.url), or any https:// image URL (linked, not copied).
galleryarray of stringNoExtra images. Each is a /storage/... path from POST /media or an https:// image URL. Up to 12 items.
scheduled_atstring (date-time)NoPublish 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 codeCauseFix
422 validation_failed on category_idMissing or unknown categoryUse an id from the lookup
422 validation_failed on the locationNo country, an invalid postal code, no state, or a district without a cityFollow step 3, or use service_mode: "online"
403 forbiddenYou are not an editor of that communityList it without a community
429 quota_exceededToday's service quota is used upWait until 00:00 UTC

Next steps

Last updated