DILSE · for developers
Organisations post opportunities, events, courses and more on DILSE once. With DILSE’s open API, your website or tool can show them too, kept up to date by itself, so their work reaches more people. It is free for everyone, and you can start without signing up. Back to DILSE
Six collections, each filterable, each also a feed. Only real work that the DILSE team has reviewed and that is live comes back, and only when its owner shares it beyond DILSE (they can switch that off). Real work is only beginning to arrive, so each collection also has samples from DILSE’s demonstration to build against, always marked as samples and never mixed in.
Opportunities 0 live opportunities
DILSE Youth
Jobs, fellowships, internships and volunteering, posted by organisations in India’s development sector. Job boards, campus portals and NGO career pages show them; anyone can follow a feed of them.
Events 0 live events
The Collective
Upcoming workshops, webinars and gatherings, with their dates and where they happen. Event calendars and newsletters list them; anyone can subscribe to them in Google Calendar or Outlook.
Courses 0 live courses
DILSE Learn
Courses made for people working in the sector: the catalogue, with who made each one. Skilling portals, universities and learning platforms list them.
Projects 0 live projects
India Impact Map
Projects on the ground, where they are and who runs them. Dashboards, funders and researchers see who is working where.
Resources 0 live resources
Shared Library
Reports, toolkits and case studies the sector has shared. Knowledge portals and course platforms link to them.
Also one at a time, by id: organisations, funding asks, digital tools.
https://dilse.duckdns.org/api/v1/opportunities.rss, or only fellowships in Rajasthan: add ?state=Rajasthan&type=Fellowship.https://dilse.duckdns.org/api/v1/events.ics. New events appear by themselves.https://dilse.duckdns.org/api/v1 in your browser: it lists every collection and its links. Without a key you have 200 calls a day, enough to try things and to run a feed.curl "https://dilse.duckdns.org/api/v1/opportunities?state=Rajasthan&type=Fellowship" \ -H "Authorization: Bearer YOUR_KEY"
url, its page on DILSE: link to it. The whole thing (details) carries an attribution line to show beside it.const res = await fetch('https://dilse.duckdns.org/api/v1/events?limit=5')
const { data } = await res.json()
for (const event of data) {
// Show the title, and always link back to DILSE.
console.log(event.title, event.organisation, event.url)
}What comes back, in short:
{
"data": [
{
"id": "yl-…",
"type": "opportunity",
"title": "Field fellowship, water security",
"organisation": "An organisation on DILSE",
"state": "Rajasthan",
"category": "Fellowship",
"themes": [],
"starts_at": null,
"url": "https://dilse.duckdns.org/share/opening/yl-…",
"details": "https://dilse.duckdns.org/api/v1/opportunities/yl-…",
"sample": false
}
],
"total": 1,
"next": null,
"licence": {
"name": "CC BY 4.0",
"url": "https://creativecommons.org/licenses/by/4.0/"
}
}Pages hold 20 entries (up to 100 with limit). When there are more, next_url is the next page.
This asks the real API from your browser, exactly as a website would.
GET /api/v1/opportunities?limit=3&sample=trueKeys are free, for non-profits and companies alike. A key lets you make more calls, and lets us tell you if something changes.
Looking for your keys…
Instead of asking DILSE every hour, give it an address on your server. Within a minute of a new fellowship in Bihar, say, going live, DILSE sends it there; when something is closed, withdrawn, taken down or kept inside by its owner, DILSE sends that too, so you can take it down.
opportunity.published, opportunity.removed, and the same for events, courses, projects, resources and stories. A test sends ping.{
"type": "opportunity.published",
"timestamp": "2026-10-10T09:30:00.000Z",
"data": {
"id": "yl-…",
"type": "opportunity",
"title": "Field fellowship, water security",
"organisation": "An organisation on DILSE",
"state": "Bihar",
"category": "Fellowship",
"themes": [],
"starts_at": null,
"url": "https://dilse.duckdns.org/share/opening/yl-…",
"details": "https://dilse.duckdns.org/api/v1/opportunities/yl-…",
"sample": false
}
}Checking that a message came from DILSE, in Node.js (the same in any language: an HMAC-SHA256):
import { createHmac, timingSafeEqual } from 'node:crypto'
// headers: webhook-id, webhook-timestamp, webhook-signature; body: the raw text
function fromDilse(headers, body, secret /* whsec_… */) {
const key = Buffer.from(secret.replace('whsec_', ''), 'base64')
const signed = `${headers['webhook-id']}.${headers['webhook-timestamp']}.${body}`
const expected = createHmac('sha256', key).update(signed).digest('base64')
const old = Math.abs(Date.now() / 1000 - Number(headers['webhook-timestamp'])) > 300
return !old && headers['webhook-signature'].split(' ').some((s) => {
const sig = s.replace(/^v1,/, '')
return sig.length === expected.length && timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
})
}Looking for your webhooks…
If you run a jobs board, a volunteering platform or a network’s own system, you can send opportunities and events into DILSE for the organisations that ask you to, so they write them once. Three yeses come first, and one after:
curl -X POST https://dilse.duckdns.org/api/v1/opportunities \
-H "Authorization: Bearer $DILSE_KEY" -H "content-type: application/json" \
-d '{ "organisation": "<id from /connected>", "external_id": "job-4417",
"title": "Field fellow, water and sanitation", "type": "Fellowship", "mode": "Onsite",
"state": "Bihar", "city": "Gaya", "duration": "12 months", "hours_per_week": 40,
"description": "A year with village water committees…",
"support": "₹15,000 a month and travel", "closes_at": "2026-12-31" }'| Send | What it does |
|---|---|
GET /api/v1/connected | The organisations that allowed you, with the id to send as organisation. |
POST /api/v1/opportunities | title, type (Volunteering, Internship, Fellowship), mode (Onsite, Remote, Hybrid), state, city, duration, hours_per_week, skills, experience_level (Beginner-friendly, Some experience, Experienced), description, expectations, support (what it pays or covers: always needed), sdgs (1–17), closes_at. DILSE Youth’s own checks apply, and the answer says what is missing. |
POST /api/v1/events | name, format (INPERSON, ONLINE, HYBRID), starts_at, ends_at, venue, platform, description, tags, registration_url, contact_email. |
| Both also need | organisation, and external_id: your own id for it. Sending the same external_id again changes it (and it is read again) instead of adding another. |
GET /api/v1/submissions | Everything you sent in, where each stands (review, live, changes, declined…) and what the team said. |
DELETE /api/v1/opportunities/{id} | Takes one back, by DILSE’s id or your external_id. The same for events. |
Errors add not_partner (not approved yet) and not_allowed (that organisation has not allowed you, or the person who did is no longer in charge of it). Posting is only from servers, never from a browser.
Two tools that never met can match each other’s data if they use the same codes. DILSE publishes one set, free to use, and every entry from the API carries them in codes beside the words.
| Vocabulary | What it holds |
|---|---|
GET /api/v1/vocabularies/states | LGD state codes, as India’s own systems use them. 36 in all. |
GET /api/v1/vocabularies/districts | LGD district codes, each with its state’s code, and its Census 2011 code where there is one. 765 in all. |
GET /api/v1/vocabularies/themes | The causes work is about. Each has the words DILSE’s apps use for it, and the SDGs it serves. 18 in all. |
GET /api/v1/vocabularies/sdgs | The UN’s seventeen goals, by number. 17 in all. |
GET /api/v1/vocabularies/languages | BCP 47 tags: India’s scheduled languages, English, and the others DILSE holds. 25 in all. |
GET /api/v1/vocabularies/skills | What people in the sector learn: DILSE Learn’s areas. 14 in all. |
Add .csv for a spreadsheet, q= to find one in any spelling DILSE knows (“Orissa” finds Odisha), and state= for one state’s districts. States and districts are India’s own Local Government Directory codes, as of 2023-10-20; goals are the UN’s numbers; languages are the tags every phone already uses.
A record on someone’s My Records, or a DILSE Learn certificate, can leave DILSE as a signed file its holder keeps, in the open formats made for it: W3C Verifiable Credentials 2.0, as an Open Badges 3.0 credential, sealed with Ed25519. Only the person it names can make one, from their own record. Your tool can accept them without calling anyone.
GET /api/v1/credentials/issuer (a did:key, and the same key as a JWKS).POST /api/v1/credentials/verify with { "credential": "…" } says whether DILSE signed it, whether it was changed, and whether it still stands today.Builders listing a tool in the Digital Tools Registry can say how it connects: what it shares, what it takes in, the open standards it uses, and where its API is. DILSE then shows which tools can pass what to which, itself among them.
GET /api/v1/connections lists them, with the pairs. It is what each builder says of their own tool, read by the DILSE team when it was listed; DILSE has not tested the connections.
The full reference is in the OpenAPI format, which Postman, Swagger and code generators read: https://dilse.duckdns.org/api/v1/openapi.json.
| Ask for | What comes back |
|---|---|
GET /api/v1 | Every collection, its live count and its links. |
GET /api/v1/opportunities | A page of opportunities. Filters: state, type, org, and q for words. |
GET /api/v1/events | A page of events. Filters: org, type, and q for words. |
GET /api/v1/courses | A page of courses. Filters: org, theme, and q for words. |
GET /api/v1/projects | A page of projects. Filters: state, org, theme, and q for words. |
GET /api/v1/resources | A page of resources. Filters: org, theme, type, and q for words. |
GET /api/v1/stories | A page of stories. Filters: state, theme, org, and q for words. |
GET /api/v1/opportunities/{id} | One opportunity, whole: summary, facts, dates, where to apply, its embed and its attribution. |
GET /api/v1/events/{id} | One event, whole: summary, facts, dates, where to apply, its embed and its attribution. |
GET /api/v1/courses/{id} | One course, whole: summary, facts, dates, where to apply, its embed and its attribution. |
GET /api/v1/projects/{id} | One project, whole: summary, facts, dates, where to apply, its embed and its attribution. |
GET /api/v1/resources/{id} | One resource, whole: summary, facts, dates, where to apply, its embed and its attribution. |
GET /api/v1/stories/{id} | One story, whole: summary, facts, dates, where to apply, its embed and its attribution. |
GET /api/v1/organisations/{id} | An organisation’s public profile, and whether DILSE has verified it. |
GET /api/v1/funding-asks/{id} | A project or organisation seeking funds on DILSE Capital. |
GET /api/v1/tools/{id} | A tool in the Digital Tools Registry. |
….rss · ….ics | The same collection as an RSS feed, or events as a calendar. |
Add sample=true to anything for the samples instead. Errors come back as { "error": { "code", "message" } }: not_found, sample_only, bad_key (an unknown or revoked key: make a new one), rate_limited (see x-ratelimit-remaining and x-ratelimit-reset on every answer).
url on DILSE. Each thing’s attribution line does both.Building something for the sector and want it early? Tell the DILSE team what you are making.