Developers
API reference
JSON over HTTPS. Base URL https://govposts.co.za/api/v1. Machine-readable: openapi.json.
Authentication
Send your secret API key (gpk_…) in a header: Authorization: Bearer gpk_…. A key in the URL is refused, because URLs end up in logs. Use the key only from your server. The widget uses a different, public key (gpw_…) and needs none of this. Get a free key.
curl -H "Authorization: Bearer gpk_YOUR_KEY" \
"https://govposts.co.za/api/v1/jobs?province=limpopo&category=health-medical&limit=10" // On your server, never in a browser: the API key is secret.
const res = await fetch('https://govposts.co.za/api/v1/jobs?province=gauteng', {
headers: { authorization: `Bearer ${process.env.GOVPOSTS_API_KEY}` },
});
const { data, next_cursor, attribution } = await res.json();
// Show attribution.text, linked to attribution.url, with the jobs. Limits and caching
- 1 000 requests a day (resets at midnight SAST) and 60 a minute, per key. Over either:
429with aRetry-Afterheader in seconds. - A key that sends ten times its daily limit in a day is switched off automatically, and we email you.
- Responses are cached for 15 minutes and carry an
ETag; sendIf-None-Matchto get a304. - Only open posts are returned. A post leaves the API as soon as it closes, is withdrawn, or turns out to be internal-only.
Endpoints
GET /jobs
List open jobs, newest first.
| Parameter | In | Description |
|---|---|---|
province | query | Province slug. One of: eastern-cape, free-state, gauteng, kwazulu-natal, limpopo, mpumalanga, northern-cape, north-west, western-cape, national. |
category | query | Category slug (see /meta/categories). One of: health-medical, education-training, engineering-technical, it-data, finance-audit-procurement, admin-clerical, human-resources, legal-justice, policing-defence-correctional, social-development-community, agriculture-environment, transport-logistics, public-works-infrastructure, communications-marketing, management-executive, artisans-trades, general-support. |
department | query | Department slug (see /meta/departments). |
q | query | Words in the title, centre or reference. |
salary_level_min | query | Lowest salary level, 1–16. |
salary_level_max | query | Highest salary level, 1–16. |
closing_after | query | Closing on or after this date. |
closing_before | query | Closing on or before this date. |
circular | query | Circular as YEAR-NUMBER, for example 2026-41. |
limit | query | Jobs per page, 1–50 (default 20). |
cursor | query | The next_cursor from the previous page. |
GET /jobs/{id}
One open job.
| Parameter | In | Description |
|---|---|---|
id | path |
GET /circulars/latest
The newest published circular.
GET /circulars/{year}/{number}
One published circular.
| Parameter | In | Description |
|---|---|---|
year | path | |
number | path |
GET /meta/provinces
Provinces with open-post counts.
GET /meta/categories
Categories with open-post counts.
GET /meta/departments
Departments with open posts.
GET /meta/salary-levels
Per salary level: the lowest and highest annual figure published adverts printed (level, lowest_annual, highest_annual, posts).
Every response
Each response includes attribution: { "text": "Data: govposts.co.za, from DPSA Public Service Vacancy Circulars", "url": "https://govposts.co.za/" }. The terms ask you to show it with the data. Errors look like { "error": { "code": "…", "message": "…" } }.
Error codes
| Status | Code | Meaning |
|---|---|---|
| 400 | key_in_query | The key was sent in the URL. Send it only in the Authorization header. |
| 400 | bad_parameter | A filter or paging parameter is not valid. |
| 401 | missing_key | No Authorization: Bearer header. |
| 401 | invalid_key | The key is unknown, revoked, or its account is suspended. |
| 404 | not_found | No open job, or no published circular, with that id. |
| 429 | rate_limited | Over the daily or per-minute limit. Wait for the Retry-After seconds. |
RSS feeds
No key needed. The latest 50 open posts, newest first; each item links to the job's page.
https://govposts.co.za/feeds/jobs.xml: all provinceshttps://govposts.co.za/feeds/province/{province}.xml, for example /feeds/province/limpopo.xmlhttps://govposts.co.za/feeds/category/{category}.xml, for example /feeds/category/education-training.xmlhttps://govposts.co.za/feeds/circulars.xml: one item per circular