Post a job
Post a job on Ur Next Door with the API: employment type, work mode, experience level, salary range and currency, deadline and location rules.
A job post advertises an opening. Members can find it on the Jobs page and apply on the site.
Endpoint: POST /api/v1/jobs · Scope: content (and read for the lookup) · Quota: job
Step 1: Get the allowed values
employment_type, work_mode, experience_level and salary_period accept fixed values. Read them from GET /api/v1/job-options instead of guessing:
curl "https://urnextdoor.com/api/v1/job-options" \
-H "Authorization: Bearer $UND_API_KEY"const res = await fetch("https://urnextdoor.com/api/v1/job-options", {
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/job-options",
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/job-options');
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,
"employment_types": [
"full_time",
"part_time",
"contract",
"internship",
"temporary"
],
"work_modes": [
"remote",
"hybrid",
"onsite"
],
"experience_levels": [
"entry",
"mid",
"senior",
"lead",
"executive"
],
"salary_periods": [
"yearly",
"monthly",
"hourly",
"project"
],
"request_id": "9f8e7d6c5b4a3921"
}Step 2: Write the job
Required: title, an HTML body, employment_type, work_mode and experience_level.
Optional but useful:
openings: 1 to 999.salary_min,salary_maxandsalary_period(for exampleyearly), incurrency(3 letters,INRif you leave it out).application_deadline: the last day to apply, asYYYY-MM-DD.summary: up to 300 characters, shown on cards.tags,featured_image(jobs take one image, no gallery), andcommunity_idorcommunity.
Step 3: Set the location (unless remote)
For onsite and hybrid jobs, send country_code and the state_id, district_id and city_id from GET /api/v1/geo (see Look up ids), plus an optional area. When work_mode is remote, location fields are ignored.
Post a remote job
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"
}Schedule an on site job
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": "Store manager",
"body": "<p>Run our Kothrud store: staff, stock and customers.</p>",
"employment_type": "full_time",
"work_mode": "onsite",
"experience_level": "senior",
"country_code": "IN",
"state_id": 19,
"city_id": 1204,
"salary_min": 35000,
"salary_max": 45000,
"salary_period": "monthly",
"scheduled_at": "2026-10-20T09:30:00Z"
}'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: "Store manager",
body: "<p>Run our Kothrud store: staff, stock and customers.</p>",
employment_type: "full_time",
work_mode: "onsite",
experience_level: "senior",
country_code: "IN",
state_id: 19,
city_id: 1204,
salary_min: 35000,
salary_max: 45000,
salary_period: "monthly",
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/jobs",
headers={
"Authorization": f"Bearer {api_key}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"title": "Store manager",
"body": "<p>Run our Kothrud store: staff, stock and customers.</p>",
"employment_type": "full_time",
"work_mode": "onsite",
"experience_level": "senior",
"country_code": "IN",
"state_id": 19,
"city_id": 1204,
"salary_min": 35000,
"salary_max": 45000,
"salary_period": "monthly",
"scheduled_at": "2026-10-20T09:30:00Z",
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = getenv('UND_API_KEY');
$payload = [
'title' => 'Store manager',
'body' => '<p>Run our Kothrud store: staff, stock and customers.</p>',
'employment_type' => 'full_time',
'work_mode' => 'onsite',
'experience_level' => 'senior',
'country_code' => 'IN',
'state_id' => 19,
'city_id' => 1204,
'salary_min' => 35000,
'salary_max' => 45000,
'salary_period' => 'monthly',
'scheduled_at' => '2026-10-20T09:30:00Z',
];
$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",
"status": "scheduled",
"scheduled_at": "2026-10-20T09:30:00Z",
"item": {
"id": 23,
"type": "job",
"slug": "backend-developer-php"
},
"request_id": "9f8e7d6c5b4a3921"
}All fields
| 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. |
Common errors
| Status and code | Cause | Fix |
|---|---|---|
422 validation_failed | A required field is missing, or a value is not in job-options | Fix the field named in errors |
422 validation_failed on scheduled_at | Outside the 5 minute to 180 day window | Pick a time in the window, in UTC |
429 quota_exceeded | Today's job quota is used up | Wait until 00:00 UTC |
When you list scheduled items, jobs appear with the type job_post. You can use job or job_post in scheduling paths.
Next steps
- List a service.
- Manage scheduled content.
- Upload images for
featured_image.
Last updated